Build knockout model and view dynamically, radio buttons not being set - javascript

I am in the process of making one of my previous questions fully dynamic in that the model is built from server data, and the view loops through the viewmodel via the knockout ko foreach functionality.
The problems I am facing are:
The radio options don't stay with the value set, i.e. I click on the Operating System, and then select a Database option, and then the Operating System setting disappears.
The dependent options (in this case database and clustering) do not have their initial selection selected when the dependent option changes (i.e. when OS changes, DB should go back to the first option, none).
My fiddle is here and i think the problem is either related to the code below:
computedOptions.subscribe(function () {
var section = this;
console.log("my object: %o", section);
section.selection(section.options()[0].sku);
},section);
Or my view bindings:
<!-- ko foreach: selectedOptions -->
<h3><span data-bind="text: description"></span></h3>
<table class="table table-striped table-condensed">
<thead>
<tr>
<th colspan="2" style="text-align: left;">Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: options -->
<tr>
<td><input type="radio" name="$parent.name" data-bind="checkedValue: $data, checked: $parent.selection" /></td>
<td style="text-align: left;"><span data-bind="text: name"></span></td>
<td style="text-align: left;"><span data-bind="text: price"></span></td>
</tr>
<!-- /ko -->
</tbody>
</table>
<!-- /ko -->
I am not sure which and would appreciate a fresh eyes as my brain hurts from the jsfiddle session.

You have two problems:
You are not correctly binding your radio button's names: name="$parent.name" is not a knockout binding expression and it just assigns the string "$parent.name" to all of your radio buttons. What you need is to use the attr binding:
<input type="radio" data-bind="checkedValue: $data,
checked: $parent.selection,
attr: { name: $parent.name }" />
The initial selection is not working because you are using the checkedValue: $dataoption this means that your checked should contain the whole object and not just one property (sku) so you need to change your computedOptions.subscribe to:
computedOptions.subscribe(function () {
var section = this;
section.selection(section.options()[0]);
},section);
Demo JSFiddle.

Related

Get row in which checkbox column is selected - knockout JS

I am totally new to Knockout JS.I am having a table in which one column is input type checkbox.
at the end of html table I have one button as "Add".
Now what I want to do is on click of "Add" button I should be able to get all the rows in which checkboxes are checked.
HTML
<table>
<thead>
<tr>
<th>Add Data</th>
</tr>
</thead>
<tbody data-bind="foreach: SearchResult">
<tr>
<td data-bind="text: Name"></td>
<td><input type="checkbox" class="" data-bind="checked: selectedArray"/></td>
</tr>
</tbody>
<tfoot>
<tr>
<td><button type="button" id="addButton" data-bind="click: AddSelection">Add</button></td>
</tr>
</tfoot>
</table>
Now can anybody tell me how can I get all the rows in which checkbox column is checked.
I have gone through this but this didn't work for me.
send information from multiple checkbox to array Knockout js
fiddle for reference
http://jsfiddle.net/smsharma/u9ts4uvf/
Here's the updated fiddle: http://jsfiddle.net/u9ts4uvf/1/
You can create an addItem function, that creates a new item, and adds it to the array availableItems:
self.addItem = function() {
var item = new BookItem(1, 'Lorem Ipsum', '$0.99');
item.Selected(true);
self.availableItems.push(item);
};
But you also need to add a checked binding to the checkboxes to ensure that they are checked when Selected is set to true for BookItem:
<input type="checkbox" data-bind="value: id(), click: $root.toggleAssociation, checked: Selected" />

select obsservableArray record from table in knockout

trying to figure out how to set up the radio button bindings in a table in knockout. I would like when the radio button on the table is selected. the entire selected record is available in the model. not quite sure how to set up the binding on the radio input. I assume I need to use $parent and a function for the value binding?
here is the fiddle. (the selected record does not do anything right now I would like it to be populated when the radio button is selected)
https://jsfiddle.net/othkss9s/5/
HTML
<table>
<thead>
<tr>
<th>Select</th>
<th>First</th>
<th>Last</th>
<th>Dept</th>
</tr>
</thead>
<tbody data-bind='foreach: employees'>
<tr>
<td><input type="radio" name="employees"></td>
<td data-bind='text: first'></td>
<td data-bind='text: last'></td>
<td data-bind='text: dept'></td>
</tr>
</tbody>
</table>
<div data-bind="with: selectedRow">
<h2>
Selected Record
</h2>
<p>
First: <span data-bind="first" ></span>
</p>
<p>
Last: <span data-bind="last" ></span>
</p>
<p>
Dept: <span data-bind="dept" ></span>
</p>
</div>
JS
function employee(first, last, dept) {
this.first = ko.observable(first);
this.last = ko.observable(last);
this.dept = ko.observable(dept);
}
function model() {
var self = this;
this.employees = ko.observableArray("");
this.selectedRow = ko.observable({});
};
var myViewModel = new model();
$(document).ready(function() {
ko.applyBindings(myViewModel);
myViewModel.employees.push(
new employee("Bob","Jones", "Hr")
);
myViewModel.employees.push(
new employee("Sarah","Smith", "It")
);
myViewModel.employees.push(
new employee("John","Miller", "It")
);
});
You have to perform two steps to get your code working.
First, apply bindings to radio buttons:
<input type="radio" data-bind="checked: $root.selectedRow, checkedValue: $data" />
checkedValue should contain actual value that corresponds the current radio button. In this case we refer $data variable that is whole employee object rather than simple (scalar) value.
checked binding should refer to an observable containing currently selected employee.
Second, correct the line where the selectedRow property is defined:
this.selectedRow = ko.observable();
Do not pass empty object as default value. Otherwise with binding won't work correctly.
By fixing the syntax of bindings in your second block that displays selected employee you will get something like this.

JQuery timepicker in knockout list

Why TimePicker working well outside knockout list, but does not work in him. How to launch it in knockout list?
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<link href="~/Scripts/timepicker/bootstrap-datepicker.css" rel="stylesheet" />
<link href="~/Scripts/timepicker/jquery.timepicker.css" rel="stylesheet" />
<link href="~/Scripts/timepicker/site.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/timepicker/bootstrap-datepicker.js"></script>
<script src="~/Scripts/timepicker/jquery.timepicker.min.js"></script>
<script src="~/Scripts/timepicker/site.js"></script>
<script src="~/Scripts/knockout-2.2.0.js"></script>
<div class="demo">
<h2>Basic Example</h2>
<p><input id="basicExample" type="text" class="time" /></p>
</div>
<table>
<thead>
<tr>
<th>Passenger name</th>
<th>Time</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: name"></td>
<td><input id="basicExample" type="text" class="time" data-bind="value: time"/></td>
</tr>
</tbody>
</table>
<script>
$('#basicExample').timepicker();
function MyViewModel() {
var self = this;
self.items = ko.observableArray();
self.items = [{ name: 'Jhon', time: '12:00' }, { name: 'Nick', time: '11:00' }];
}
ko.applyBindings(new MyViewModel());
</script>
When you use a foreach binding, Knockout is creating DOM elements for each of the items in your list. They are not there when you do you timepicker initialization.
(Also, you can't use the same ID twice in an HTML document. Your call will only find the first one.)
For any widget that needs to be initialized, you should have a custom binding handler. It might be as simple as this:
ko.bindingHandlers.timePicker = {
init: function (el) {
$(el).timepicker();
}
}
Then you would use that to bind it.
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: name"></td>
<td><input type="text" class="time" data-bind="timepicker: true, value: time"/></td>
</tr>
</tbody>
Probably there needs to be more done in the binding handler than that. Someone else wrote an example fiddle with their own timepicker binding handler.
The main problem you are facing here is that you are trying to define the JqueryUI TimePicker before you have defined your viewmodel and apply the bindings.
That means basically that your DOM nodes do not exist at this point.
To avoid that I would recommend you using the "afterRender(nodes, elements)" option in knockout foreach:
http://knockoutjs.com/documentation/foreach-binding.html
This way your DOM nodes will be there and so your inputs can be "transformed" into TimePickers
BTW, remove the "id" inside the KO foreach part, it´s useless (KO will generate another unique UI in each node)
Hope this helps

Remove button UI issue inside nested templates - Knockout.js

I'm in a bit of trouble from good 20 hours now. I am using knockout.js and dynamically add/remove rows from html table. I am having trouble in displaying an extra column for the remove button dynamically, my template is:
<table class="tg">
<tbody data-bind="template: {name: 'LineItemsBodyScript', foreach: LineItemFields, afterRender: addRowRemoveButton}"></tbody>
</table>
//template that gets called from HTML table.
<script id="LineItemsBodyScript" type="text/html">
<!-- ko ifnot: isFirsElement($index) -->
<tr data-bind="template: {name: 'LineItemDataTemplate', foreach: $data }"></tr>
<!-- /ko -->
</script>
//template called inside the template
<script id="LineItemDataTemplate" type="text/html">
<td><input type="text" data-bind="value: FieldValue, visible: IsVisible, enable: IsUpdatable" class="table-column" /></td>
</script>
If i add remove button in 'LineItemDataTemplate' template, it renders the remove button after every column (makes sense). And if i add remove button in 'LineItemsBodyScript', it gets overwritten by the child template. My model is, List>.
How and where could i add the remove button?
<td><input type='button' value="Remove" /></td>
I looked around and found afterRender afterAdd methods but they are not going to solve the issue.
Note: No. of columns are unknown (therefore i made a generic class for Column-Name & Column-Value)
You can add an extra <td> in the LineItemDataTemplate template when it's being rendered for the last field (grootboek) for each row that is not the header row:
Last field when: $index() == $parentContext.$data.length - 1
Not header row (first row): $parentContext.$index() > 0
Which results in:
<script id="LineItemDataTemplate" type="text/html">
<td><input type="text"
data-bind="value: FieldValue, visible: IsVisible,
enable: IsUpdatable"
class="table-column" /></td>
<!-- ko if: $parentContext.$index() > 0
&& $index() == $parentContext.$data.length - 1 -->
<td>
<button data-bind="click: removeLineItem">Remove</button>
</td>
<!-- /ko -->
</script>

Update Knockoutjs table

I have a table bound to knockoutjs using foreach.
<table>
<thead>
<tr>
<th>ID</th>
<th>BALANCE</th>
<th>GENDER</th>
<th>AGE</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: data -->
<tr>
<!-- ko foreach: Object.keys($data) -->
<td>
<label data-bind="text: $parent[$data]" />
</td>
<!-- /ko -->
</tr>
<!-- /ko -->
</tbody>
</table>
Table rows iterate an observableArray (about 2000 items).
After table is rendered, I need to edit a row but table do not render the row changed.
How can I do this whithout clear observableArray and reload it again?
Here JSFIDDLE
Thank you
You need to make your data array properties observable so knockout would observe the changes. I'd suggest you to use knockout mapping to do the job of creating observables for you, like this:
var foo = new MyVM();
var mapping = {
create: function(options) {
return ko.mapping.fromJS(options.data);
}
};
ko.mapping.fromJS(myJS, mapping, foo.data);
But you need to change your markup so it won't just iterate through object properties, but explicitly specify which property should be used:
<tbody>
<!-- ko foreach: data -->
<tr>
<td>
<label data-bind="text: _id" />
</td>
<td>
<label data-bind="text: balance" />
</td>
<td>
<label data-bind="text: gender" />
</td>
<td>
<label data-bind="text: age" />
</td>
</tr>
<!-- /ko -->
</tbody>
Here is working demo. Of course you can make observables yourself, then just rewrite your code, so every property of each item in data array would be an observable.
Edit:
Well, since we don't know the actual properties, I'd suggest the following code to make observables:
var foo = new MyVM();
for (var i=0, n = myJSON.length; i < n; i++) {
for (var prop in myJSON[i])
if (myJSON[i].hasOwnProperty(prop))
myJSON[i][prop] = ko.observable(myJSON[i][prop]);
}
foo.data(myJSON);
And in your view model function:
self.changeRow = function (){
if(typeof self.data() != "undefined"){
self.data()[0]["gender"]("male");
}
};
See updated demo.

Categories

Resources