checkbox data-bind using knockout.js and jquery mobile - jquery-mobile

I am facing problem to data-bind checkbox using knockout.js.
The jsfiddle http://jsfiddle.net/sajesh1985/ypbLN/
(HTML)Code:
<h2>View1</h2>
<h4>Select Columns:</h4>
<ul data-bind="foreach: gridOptions.columns" data-role="listview">
<div data-role="fieldcontain" >
<li id="li">
<label>
<input type="checkbox" id="chk" data-bind="event: {change: function(){ checked(!checked()); } }, checkbox: checked" /> <span data-bind="text: header"></span>
</label>
</li>
</div>
</ul>
<hr>
<table>
<thead>
<tr data-bind="foreach: gridOptions.columns">
<th data-bind="visible:checked, text: header"></th>
</tr>
</thead>
<tbody data-bind="foreach: people">
<tr data-bind="foreach: $parent.gridOptions.columns">
<td data-bind="text: $parent[dataMember], visible:checked"></td>
</tr>
</tbody>
</table>
JS Code:
var ProductSearchViewModel = {
gridOptions: {
columns: [{
header: 'First Name',
dataMember: 'firstName',
checked: ko.observable(true)
}, {
header: 'Last Name',
dataMember: 'lastName',
checked: ko.observable(true)
}]
},
people: [{
firstName: 'Bert',
lastName: 'Bertington'
}, {
firstName: 'Charles',
lastName: 'Charlesforth'
}, {
firstName: 'Denise',
lastName: 'Dentiste'
}]
};
ko.bindingHandlers.checkbox = {
update: function (element, valueAccessor) {
var value = valueAccessor();
var valueUnwrapped = ko.utils.unwrapObservable(value);
//$(element).checkboxradio().trigger('create');
$(element).attr("checked", valueUnwrapped).checkboxradio("refresh");
}
};
$(document).ready(function () {
ko.applyBindings(ProductSearchViewModel);
});
Can you please help me in rectifying the issue?
I am getting Uncaught cannot call methods on checkboxradio prior to initialization; attempted to call method 'refresh' in the browser.

The best way to bind checkboxes is to use the checked binding like here:
<input type="checkbox" id="chk" data-bind="checked: checked" />
See also documentation: http://knockoutjs.com/documentation/checked-binding.html

Related

ASP.NET MVC code modal popup not sending parameter to controller

I am trying to present Organization Details in a partial view in a Bootstrap Modal. After a day of reading/watching tutorials I am stumped.
Organizations are loaded into a Table and there is a View option on each row. The table has an id="OrganizationGrid"
<table class="table table-striped table-bordered dt-responsive nowrap" width="100%" cellspacing="0" id="OrganizationGrid">
<thead>
<tr style="color:#126E75; background-color:lightcyan">
<th style=" width:5%">
#Html.ActionLink("ID", "Index", new { sortOrder = ViewBag.IDSortParm, currentFilter=ViewBag.CurrentFilter })
</th>
<th style=" width:25%">
#Html.ActionLink("Name", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter })
</th>
<th style=" width:25%">
Parent Org
</th>
<th style=" width:10%; text-align:center">
Website
</th>
<th style="width:25%">
Comment
</th>
<th scope="col" colspan="3" style=" width:10%; text-align:center">Action</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr class=" table-light">
<td style="text-align:left">
#Html.DisplayFor(modelItem => item.Id)
</td>
<td style="text-align:left">
#Html.DisplayFor(modelItem => item.Name)
</td>
<td style="text-align:left">
#Html.DisplayFor(modelItem => item.ParentOrg.Name)
</td>
<td style="text-align: center">
<a href=#Html.DisplayFor(modelItem=> item.Website) target="_blank">
<i class="fa-solid fa-globe" target="_blank" rel="noopener noreferrer"></i>
</a>
</td>
<td style="text-align:left">
#Html.DisplayFor(modelItem => item.Comment)
</td>
<td style="text-align: center">
<a asp-action="Edit" asp-route-id="#item.Id">
<i class="fas fa-edit"></i>
</a>
</td>
<td>
<a class="details" href="javascript:;">View</a>
</td>
<td style="text-align: center">
<a asp-action="Delete" asp-route-id="#item.Id">
<i class="fas fa-trash" style="color:red"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
My understanding is that it triggers the JavaScript function which is currently residing at the bottom of the same page:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
$("#OrganizationGrid .details").click(function() {
var organizationId = $(this).closest("tr").find("td").eq(0).html();
$.ajax({
type: "POST",
url: "/Organizations/Details",
data: '{organizationId: "' + organizationId + '" }',
contentType: "application/json; charset=utf-8",
dataType: "html",
success: function(response) {
$("#partialModal").find(".modal-body").html(response);
$("#partialModal").modal('show');
},
failure: function(response) {
alert(response.responseText);
},
error: function(response) {
alert(response.responseText);
}
});
});
});
</script>
I inserted a break in the controller to see if ajax was passing the organizationId but it is not. This is the Controller Details Action method:
[HttpPost]
public ActionResult Details(int organizationId)
{
var organization = _context.Organizations
.Include(o => o.ParentOrg).Where(p=>p.Id == organizationId);
if (organization == null)
{
return NotFound();
}
return PartialView("_Details", organization);
}
If I hard code organizationId in the Controller the popup loads with the correct information BUT if I hard code organizationId in the JavaScript function it still passes a null value to the controller. Could someone please point me in the right direction.
The issue was with the JavaScript function and specifically the syntax of the data attribute in ajax. Corrected solution is as follows:
$(function () {
$("#OrganizationGrid .details").click(function () {
var organizationId = $(this).closest("tr").find("td").eq(0).html();
//alert(organizationId)
$.ajax({
type: "POST",
url: "/Organizations/Details",
data: { 'id': organizationId },
//contentType: "application/json; charset=utf-8",
//dataType: "html",
success: function (response) {
$("#viewEditOrgModal").find(".modal-body").html(response);
$("#viewEditOrgModal").modal('show');
},
failure: function (response) {
alert(response.responseText);
},
error: function (response) {
alert(response.responseText);
}
});
});
});

knockout.js ul li databinding not proper

HTML
<h4>People</h4>
<ul data-bind="foreach: people">
<li>
<span data-bind="text: name"> </span>
Remove
</li>
</ul>
<button data-bind="click: addPerson">Add</button>
<input type="text" data-bind="value: cardtext" /><br /><br /><br />
JS
function AppViewModel() {
var self = this;
self.cardtext=ko.observable();
self.people = ko.observableArray([
{ name: 'Bert' },
{ name: 'Charles' },
{ name: 'Denise' }
]);
self.addPerson = function() {
self.people.push({ name: self.cardtext });
};
self.removePerson = function() {
self.people.remove(this);
}
}
ko.applyBindings(new AppViewModel());
This is the result
The problem is that the textbox keep adding new elements but the previous newly added elements keep getting updated by the new elements.
3rd element was 3rd element
4th element was 4th element
when I added 5th element the 3rd and the 4th element get updated by 5th element. why it is that? what I am doing wrong?. I have no idea.
You just need to add () at the end of self.cardtext(). If you don't put the parenthesis, what it will do is it will push the observable object of cardtext to the array instead of its value. So when you modify cardtext from the textbox, it will also modify the previous object that was pushed.
function AppViewModel() {
var self = this;
self.cardtext=ko.observable();
self.people = ko.observableArray([
{ name: 'Bert' },
{ name: 'Charles' },
{ name: 'Denise' }
]);
self.addPerson = function() {
self.people.push({ name: self.cardtext() });
};
self.removePerson = function() {
self.people.remove(this);
}
}
ko.applyBindings(new AppViewModel());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h4>People</h4>
<ul data-bind="foreach: people">
<li>
<span data-bind="text: name"> </span>
Remove
</li>
</ul>
<button data-bind="click: addPerson">Add</button>
<input type="text" data-bind="value: cardtext" /><br /><br /><br />

Fill table with data from knockout js observableArray then take one item from the list and display it in the form

Good day
I am new to knockout js and what is described in the title is what I am trying to do. The first part I can do but I just cant figure out how to put values into form here is some code.
With this I get the data:
$.ajax('#Url.Action("GetEducations", "Candidate")', {
data: { id: #ViewBag.CandidateId },
type: "post", dataType: 'json'
})
.done(function (result) {
var mappedEducations = $.map(result, function (item) { return new Education(item) });
self.educations(mappedEducations);
})
.fail(function (xhr, status) {
alert('#Resources.WebAppLocalization.general_Error');
});
Here I put them into table:
<tbody data-bind="foreach: educations, visible: educations().length > 0">
<tr>
<td data-bind="text: InstitutionName"></td>
<td data-bind="text: Qualification"></td>
<td data-bind="text: EducationFrom"></td>
<td data-bind="text: EducationTill"></td>
<td>
<a class="link" data-bind="attr: {href: ''}, click: $parent.editEducationFill, clickBubble: false"></a>
</td>
</tr>
</tbody>
Now when someone click's on edit link it goes here:
self.editEducationFill = function (education) {
//TODO
}
From here I want the passed object to go to edit form here:
<form id="FormID">
<div class="detValue"><input type="text" data-bind="value: InstitutionName"/> </div>
<div class="detValue"><input type="text" data-bind="value: Qualification" /></div>
<div class="detValue"><input type="text" data-bind="value: EducationFrom" /></div>
<div class="detValue"><input type="text" data-bind="value: EducationTill" /></div>
</form>
However I just cant get it to work.
For any help thank you in advance
Add an observable to your view model that will hold the education object you want to edit.
self.educationToEdit = ko.observable();
In your method: self.editEducationToFill, set the educationToEdit to the one that's passed into the method.
self.editEducationFill = function(education){
self.educationToEdit(education);
}
In your view, add a data-binding that tells the form to use the educationToFill observable to display on your page.
<form id="FormID" data-bind="with: educationToEdit">
<div class="detValue"><input type="text" data-bind="value: InstitutionName"/></div>
<div class="detValue"><input type="text" data-bind="value: Qualification" /></div>
<div class="detValue"><input type="text" data-bind="value: EducationFrom" /></div>
<div class="detValue"><input type="text" data-bind="value: EducationTill" /></div>
</form>

How to raise an event after knockout rendering is complete?

This jquery mobile table is being rendered with knockout.
<table data-role="table" id="report-table" class="ui-body-a ui-shadow ui-responsive table-stripe"
data-column-btn-theme="a" data-column-btn-text="Spalten..." data-column-popup-theme="a" data-mode="columntoggle"">
<thead>
<tr data-bind="foreach: columns">
<th data-bind="text: $data.Caption, attr: { 'data-priority': 1 + Math.floor($index() / 4) }"></th>
</tr>
</thead>
<tbody data-bind="foreach: { data: rows, afterRender: tableRowAfterRender }">
<tr data-bind="foreach: $parent.columns">
<!-- ko template: { name: $data.template } -->
<!-- /ko -->
</tr>
</tbody>
</table>
To get the "columntoggle" to actually work, I currently use the "aferRender" event:
self.tableRowAfterRender = function (element, data) {
// Skip unless data.Items()[i] is not the last element in the rows collections
for (var i = 0; i < data.Items().length - 1; i++) {
if (data.Items()[i] !== self.rows()[self.rows().length - 1].Items()[i])
return;
}
// refresh table after 100ms delay
setTimeout(function () { $("#report-table").table("refresh"); }, 100);
}
This is shaky, I hate the setTimeout() way of doing things, and this situation became quiet common for me with jquery mobile and knockout. I need a robust way to raise an event once all the knockout rendering or ideally once all the rendering concerned with elements inside the table-element is done. I was able to use custom bindings in some such situations, but I would not know how to do this here.
Try this. Wrap the table with: <div data-bind='template: { afterRender: myPostProcessingLogic }'>. Then do whatever you need to do in myPostProsssingLogic. This will only be called when the table is first rendered however. Here's a fiddle:
<div data-bind='template: { afterRender: myPostProcessingLogic }'>
<table data-role="table" id="report-table" class="ui-body-a ui-shadow ui-responsive table-stripe"
data-column-btn-theme="a" data-column-btn-text="Spalten..." data-column-popup-theme="a" data-mode="columntoggle"">
<thead>
<tr data-bind="foreach: columns">
<th data-bind="text: $data.Caption, attr: { 'data-priority': 1 + Math.floor($index() / 4) }"></th>
</tr>
</thead>
<tbody data-bind="foreach: { data: rows, afterRender: tableRowAfterRender }">
<tr data-bind="foreach: $parent.columns">
<!-- ko template: { name: $data.template } -->
<!-- /ko -->
</tr>
</tbody>
</table>
You can try to use binding init
ko.bindingHandlers.init = {
init: function(element, valueAccessor, allBindings, viewModel) {
var action = valueAccessor();
if (typeof action === 'function') {
setTimeout(action.bind(viewModel, element), 0);
}
}
};
Like this:
ko.components.register('simple-component', {
viewModel: function(params) {
this.title = params.title;
this.initHandler = function() {
console.log('DOM of component has been built.');
};
},
template:
'<div data-bind="init: initHandler">\
Title is: <span data-bind="text: title"></span>\
</div>'
});
Information for more complex cases is here - http://blog.atott.ru/2015/08/dom-built-event-for-knockout-components.html
There is a simple way of doing it. Here is the process
Assign a class to your row
<tr class="row" data-bind="foreach: $parent.columns">
Check in your function with if condition
if(data.Items().length == $('row').length){
$("#report-table").table("refresh")
}
Or call any event manually
$('#report-table').trigger('click')
Hope that's helpful to you.
EDITS:
Create a new observable and set it to false.
self.EnableTimeOut = ko.observable(false)
Now set it true according to your requirement. This is only example
if(data.Items().length == $('row').length && data.Items().length > 0){
self.EnableTimeOut(true)
$("#report-table").table("refresh")
}
And finally
Wrap it in condition
setTimeout(function () {
if(self.EnableTimeOut()){
$("#report-table").table("refresh");
}
}, 100);

one radio is not SELECTED?

I changed the approch, now, I get selected radio value correctly.
Issue: One of my radio is selected when initialize (first load then user can change the selection), but radio is not selected first time, how to initialize "Selected" appropriately?
#model Demo.Web.ViewModels.HomeViewModel
#{
ViewBag.Title = "Two Page";
}
#using (Html.BeginForm("Index", "Two", FormMethod.Post, new Dictionary<string, object> { { "data-bind", "submit:onSubmit" } }))
{
<table>
<tr>
<td>
<div data-bind="foreach: items">
<input type="radio" name="items" data-bind="attr: { value: id }, checked: $root.selected" />
<span data-bind="text: name"></span>
</div>
<div data-bind="text: selected">
</div>
</td>
</tr>
</table>
<input type="submit" name="send" value="Send" />
}
<script type="text/javascript">
var viewModel = {
items: [
{ "id": 1, "name": "one", "selected": false },
{ "id": 2, "name": "two", "selected": true },
{ "id": 3, "name": "three", "selected": false }
],
selected: ko.observable(),
onSubmit: function(){
var x = this.selected();
}
};
$(function() {
ko.applyBindings(viewModel);
});
When bound against inputs of type radio, the checked binding in Knockout tries to write the input's value to whatever value is bound against. The idea is that you all of the radios are bound against the same observable.
Something like:
var viewModel = {
items: [
{ id: 1, name: "one" },
{ id: 2, name: "two" },
{ id: 3, name: "three" }
],
selected: ko.observable()
};
Bound against a UI like:
<div data-bind="foreach: items">
<input type="radio" name="items" data-bind="attr: { value: id }, checked: $root.selected" />
<span data-bind="text: name"></span>
</div>
<div data-bind="text: selected"></div>
Sample: http://jsfiddle.net/rniemeyer/BbX4S/
So, in your case, you should be able to bind your radios against $root.IsSelected and use it rather than looping through the items.
When you initialize it, you might want to look to see if any button is checked, and then populate the root level IsSelected appropriately.

Resources