Knockout Validation not working with a DatePicker bindingHandler - javascript

So I am using Knockout Validation to validate my viewmodel and a custom knockout datepicker bindingHandler to attach a jQuery-UI datepicker to dynamically added items in my observableArray.
It seems my bindingHandler is destroying or breaking the validation rules on that field. Neither of the validation rules for the Start or End date seem to be getting enforced.
JSFiddle Link of my code
HTML code:
<form>
<a class="btn btn-default" data-bind="click: function () { $root.addMed(); }">Add New Medication</a>
<h6 data-bind="visible: patientMeds().length < 1">No medications entered.</h6>
<table class="table table-condensed" data-bind="visible: patientMeds().length > 0">
<thead>
<tr>
<th>Med</th>
<th>From</th>
<th>To</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: patientMeds">
<tr>
<td>
<input class="form-control" data-bind="value: MedicationID" />
</td>
<td>
<input class="form-control" data-bind="datepicker: StartDate, datepickerOptions: { changeMonth: true, changeYear: true, showButtonPanel: true }" />
</td>
<td>
<input class="form-control" data-bind="datepicker: DiscontinuedDate, datepickerOptions: { changeMonth: true, changeYear: true, showButtonPanel: true }" />
</td>
<td>
<button class="btn btn-default" data-bind="click: $parent.removeMed">Delete</button>
</td>
</tr>
</tbody>
</table>
</form>
Javascript / ViewModel code:
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
}
};
function PatientMedication(p) {
var self = this;
p = p || {};
self.MedicationID = ko.observable(p.MedicationID || 1)
.extend({
required: {
params: true,
message: 'Please enter a medication'
},
number: true
});
self.StartDate = ko.observable(p.StartDate).extend({
required: {
params: true,
message: 'Please enter a date'
},
date: true
});
self.DiscontinuedDate = ko.observable(p.DiscontinuedDate || '').extend({
required: {
params: true,
message: 'Please enter a date'
},
date: true
});
}
function MedicationViewModel() {
var self = this;
self.patientMeds = ko.observableArray([]);
self.addMed = function () {
self.patientMeds.unshift(new PatientMedication());
};
self.removeMed = function (med) {
self.patientMeds.remove(med)
};
};
var medvm = new MedicationViewModel();
ko.applyBindings(medvm);

The validation plugin only hooks into the value, checked, textinput and selectedOptions bindings.
If you want to make your custom binding trigger the validations you need to call the validation.makeBindingHandlerValidatable method of the plugin and pass in the name of your custom binding:
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//...
}
};
ko.validation.makeBindingHandlerValidatable('datepicker');
Demo JSFiddle.

Related

ASP.NET MVC Knockout button click call POST API

I have a table (list) on my front end in html, and foreach row, I have a button:
...
<td>
<button data-bind="click: sendDataToApi">Test button</button>
</td>
...
and in the .js file I have something like this:
define(['viewmodels/shell', 'durandal/services/logger', 'plugins/dialog', 'viewmodels/shell', 'toastr', 'knockout', 'kovalidationconfig', 'plugins/router', 'typeahead.bundle'],
function (shell, logger, dialog, shell, toastr, ko, kvc, router, typeahead) {
var vm = {
activate: activate,
shell: shell,
data: ko.observableArray([]),
close: function () {
$(window).off('popstate', vm.goBack);
$(window).off('resize', adjustModalPosition);
dialog.close(vm, 'cancel');
},
goBack: function () {
$(window).off('popstate', vm.goBack);
$(window).off('resize', adjustModalPosition);
dialog.close(vm, 'back');
},
editPreregisteredChildren: function () {
router.navigate("#/function/" + this.id);
},
currentPage: ko.observable(1),
itemsPerPage: ko.observable(10),
hasNextPage: ko.observable(false),
previousPage: previousPage,
nextPage: nextPage,
sendDataToApi: function () {console.log("sdsdsds")},
searchCriteria: ko.observable(''),
applySearch: applySearch,
locations: ko.observableArray([]),
locationId: ko.observable(),
LocationName: ko.observable(),
exportHref: ko.observable("/spa/ExportSchedulings"),
bindingComplete: function (view) {
bindFindLocationEvent(view);
}
};
function sendDataToApi() {
console.log("hello.")
};
});
so, firstly, I want to get console.log("something") to work.
for now Im getting error in my console in chrome:
Uncaught ReferenceError: Unable to process binding "click: function(){return sendDataToApi }"
Message: sendDataToApi is not defined
I dont get it why?
after that I need to do an ajax call to my controller, and the end to call some api in that controller, and return the information if the api call was successfull or not.
I am going to assume that you are trying to display information in a table given the
<td>
<button data-bind="click: sendDataToApi">Test button</button>
</td>
I am also going to assume that that there is a ko:foreach at the table or table-body level. if that is the case then sendDataToApi is associated with the parent vm object and not the object that is currently being used to create the table rows.
If that is the case then you would need to use the $parent.sendDataToApi or $root.sendDataToApi
<td>
<button data-bind="click: $parent.sendDataToApi">Test button</button>
</td>
or
<td>
<button data-bind="click: $root.sendDataToApi">Test button</button>
</td>
EDIT
you just need to add a parameter to the receiving function because the knockout passes the current object.
var serverData = [{
id: 1,
name: 'Test 1'
},
{
id: 2,
name: 'Test 2'
},
{
id: 3,
name: 'Test 3'
},
];
function ViewModel() {
var self = this;
self.data = ko.observableArray([]);
self.checkServer = function checkServer(row) {
console.log(ko.toJS(row));
}
self.fillTable = function fillTable() {
var mappedData = serverData.map(r => new RowViewModel(r));
self.data(mappedData);
}
}
function RowViewModel(data) {
var self = this;
self.id = ko.observable(data.id || 0);
self.name = ko.observable(data.name || '');
}
ko.applyBindings(new ViewModel());
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<button class="button" data-bind="click: fillTable">Fill Table</button>
<table class="table">
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
<td>
<button data-bind="click: $parent.checkServer">Check Server</button>
</td>
</tr>
</tbody>
</table>

Validation message not appearing

On adding ko validation extender to dynamic objects it is not displaying the error message when showAllMessages() is called. There is also no span tag added below the respective controls which will show the error message.
I also like to show the error message just below the control as soon as the object is added to observable array.
Please find the fiddle
http://plnkr.co/edit/PUgxqrarDeaabDxUwgLO?p=preview
JavaScript
var schedule = function() {
var self = this;
self.name = ko.observable();
self.startDate = ko.observable();
self.endDate = ko.observable();
// Add validation
self.name.extend({required: {message: 'Name Required'}});
self.startDate.extend({required: {message: 'Start Date Required'}});
self.endDate.extend({required: {message: 'End Date Required'}});
}
var viewmodel = {
model: {
lookups: {
grandSlams: ["Australian Open", "French Open", "Wimbledon", "US Open"]
},
schedules: ko.observableArray(),
status: ko.observable()
},
actions: {
addSchedule: function() {
console.log('Add Called');
viewmodel.model.schedules.push(new schedule());
viewmodel.model.status('Edited');
console.log(viewmodel.model.schedules().length);
},
saveSchedule: function(){
console.log('Save Called');
var errors = ko.validation.group(viewmodel.model.schedules, { deep: true });
if (errors().length > 0) {
console.log(errors());
errors.showAllMessages();
hasError = true;
}
viewmodel.model.status('Saved!!!');
}
}
};
$(function() {
ko.validation.init({
insertMessages: true,
messagesOnModified: true,
grouping: {
deep: true, //by default grouping is shallow
observable: true //and using observables
}
});
ko.applyBindings(viewmodel);
});
HTML
<!DOCTYPE html>
<html>
<head>
<script data-require="jquery#*" data-semver="2.1.4" src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script data-require="knockout#2.2.1" data-semver="2.2.1" src="//cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<script data-require="knockout-validation#*" data-semver="1.0.2" src="//cdnjs.cloudflare.com/ajax/libs/knockout-validation/1.0.2/knockout.validation.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<h1>Knockout Validation!</h1>
<button data-bind="click: actions.addSchedule">Add Schedule</button>
<button data-bind="click: actions.saveSchedule">Save Schedule</button>
<h3>Schedules</h3>
<table>
<thead>
<tr>
<th>Grand Slams</th>
<th>Start</th>
<th>End</th>
</tr>
</thead>
<tbody data-bind="foreach:model.schedules">
<tr>
<td style="width:250px">
<select data-bind="options: $root.model.lookups.grandSlams, optionsCaption:'Select...'"></select>
</td>
<td style="width:250px">
<input type="text" data-bind="value: startDate" />
</td>
<td style="width:250px">
<input type="text" data-bind="value: endDate" />
</td>
</tr>
</tbody>
</table>
<h3 data-bind="text: model.status"></h3>
</body>
</html>
Updated example
You're missing the value binding on the select element. It should be
<select data-bind="value: name, options: $root.model.lookups.grandSlams, optionsCaption:'Select...'"></select>
Updated viewmodel if you want to validate on addSchedule (you can probably remove this.actions.showErrors() from save now, I left it just in case)
var viewmodel = {
model: {
lookups: {
grandSlams: ["Australian Open", "French Open", "Wimbledon", "US Open"]
},
schedules: ko.observableArray(),
status: ko.observable()
},
actions: {
addSchedule: function() {
console.log('Add Called');
viewmodel.model.schedules.push(new schedule());
viewmodel.model.status('Edited');
this.actions.showErrors();
console.log(viewmodel.model.schedules().length);
},
showErrors: function() {
var errors = ko.validation.group(viewmodel.model.schedules, { deep: true });
if (errors().length > 0) {
console.log(errors());
errors.showAllMessages();
hasError = true;
}
},
saveSchedule: function(){
console.log('Save Called');
var errors = ko.validation.group(viewmodel.model.schedules, { deep: true });
this.actions.showErrors();
viewmodel.model.status('Saved!!!');
}
}
};

Backbone.js validation error appears in more than one view

I'm having trouble with a backbone.js app I'm working on as a learning exercise. I set up a jsfiddle for it.
Here's the JavaScript:
var app = {};
$(document).ready(function() {
app.Contact = Backbone.Model.extend({
defaults: {
firstName: '',
lastName: '',
email: ''
},
validate: function(attrs) {
var errors = [];
if (attrs.firstName.trim() == "") {
errors.push({
'message': 'Please enter a first name.',
'field': 'firstName'
});
}
if (attrs.lastName.trim() == "") {
errors.push({
'message': 'Please enter a last name.',
'field': 'lastName'
});
}
if (attrs.email.trim() == "") {
errors.push({
'message': 'Please enter an email address.',
'field': 'email'
});
}
if (errors.length) {
return errors;
} else {
return false;
}
}
});
app.ContactList = Backbone.Collection.extend({
model: app.Contact,
localStorage: new Store('backbone-addressbook')
});
app.contactList = new app.ContactList();
app.ContactView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#contact-template').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
var self = this;
this.model.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
console.log(self.el);
$(self.el).find('[data-field="' + error.field + '"]').parent().addClass('has-error');
$(self.el).find('[data-field="' + error.field + '"]').parent().find('.help-block').remove();
$(self.el).find('[data-field="' + error.field + '"]').parent().append('<span class="help-block">' + error.message + '</span>');
});
});
this.model.on('change', function(model, response) {
//console.log(self.el);
$(self.el).removeClass('editing');
this.render;
})
},
events: {
'dblclick label': 'edit',
'keypress .edit': 'updateOnEnter',
'click .destroy': 'destroy',
'click .save': 'close'
},
edit: function(e) {
this.$el.addClass('editing');
$(e.currentTarget).next('input').focus();
},
updateOnEnter: function(e) {
if (e.which == 13) {
this.close(e);
}
},
close: function(e) {
e.preventDefault();
var updateObject = {};
$(this.el).find('input[type="text"]').each(function() {
node = $(this);
updateObject[node.data('field')] = node.val();
});
this.model.save(updateObject, {validate: true});
},
destroy: function() {
this.model.destroy();
}
});
app.AppView = Backbone.View.extend({
el: '#newContact',
initialize: function() {
$(this).find('.has-error').removeClass('has-error');
$(this).remove('.help-block');
app.contactList.on('add', this.addOne, this);
app.contactList.fetch();
var self = this;
app.contactList.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
console.log(self.el);
$(self.el).find('[data-field="' + error.field + '"]').parent().addClass('has-error');
$(self.el).find('[data-field="' + error.field + '"]').parent().find('.help-block').remove();
$(self.el).find('[data-field="' + error.field + '"]').parent().append('<span class="help-block">' + error.message + '</span>');
});
});
},
events: {
'click .add': 'createContact'
},
createContact: function(e) {
e.preventDefault();
app.contactList.create(this.newAttributes(), {validate: true});
},
addOne: function(contact) {
var view = new app.ContactView({model: contact});
$('#contactList').append(view.render().el);
$('form input[type="text"]').val('');
$('form input[type="text"]').parent().removeClass('has-error');
$('.help-block').remove();
},
newAttributes: function() {
var updateObject = {};
$(this.el).find('input[type="text"]').each(function() {
node = $(this);
updateObject[node.data('field')] = node.val();
});
return updateObject;
},
});
app.appView = new app.AppView();
});
And here's the HTML:
<div class="container">
<section id="addressbookapp">
<header id="header">
<h1>Address Book</h1>
<div class="well">
<form id="newContact" action="#" role="form">
<div class="form-group">
<label class="control-label" for="firstName">First Name</label>
<input data-field="firstName" class="newFirstName form-control input-sm" type="text" />
</div>
<div class="form-group">
<label class="control-label" for="lastName">Last Name</label>
<input data-field="lastName" class="newLastName form-control input-sm" type="text" />
</div>
<div class="form-group">
<label class="control-label" for="email">Email Address</label>
<input data-field="email" class="newEmail form-control input-sm" type="text" />
</div>
<button class="add btn-xs">Add</button>
</form>
</div>
</header>
<section id="main">
<table class="table table-striped">
<caption>Double-click to edit an entry.</caption>
<thead>
<tr>
<th>First</th>
<th>Last</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contactList"></tbody>
</table>
</section>
</section>
</div>
<script id="contact-template" type="text/template">
<form action="#" role="form">
<td>
<label class="control-label" for="firstName"><%- firstName %></label>
<input data-field="firstName" class="firstName input-sm edit" value="<%- firstName %>" type="text" />
</td>
<td>
<label class="control-label" for="lastName"><%- lastName %></label>
<input data-field="lastName" class="lastName input-sm edit" value="<%- lastName %>" type="text" />
</td>
<td>
<label class="control-label" for="email"><%- email %></label>
<input data-field="email" class="email input-sm edit" value="<%- email %>" type="email" />
</td>
<td>
<button class="btn-xs save">Save</button>
<button class="btn-xs destroy">Delete</button>
</td>
</form>
</script>
Specifically, when the user edits an entry in the list (by double-clicking), clears an input (a last name, for example) and then tries to save, there's (correctly) a validation error. The problem is that the form at the top (for creating a new entry) is also responding to the invalid event.
My question is not just how to keep this from happening but what would be the ideal way to organize things. This is a learning exercise for me, so I'd thankful for any tips -- anything you see that could be improved.
It's due to the way you've built the app: in both the "new" and "edit" forms, you tell the app to "display error messages if there's a validation problem in the collection". So when you try editing an existing model and there's a validation problem, the "new" form updates to display the errors.
What you need to do instead, is use a new (blank) model in the "new" form, display errors if it doesn't validate, and add it to the collection if it's valid. That way, both forms have their errors handled by different mechanisms and won't overlap.
See http://jsfiddle.net/n9yq2/3/
app.AppView = Backbone.View.extend({
el: '#newContact',
initialize: function() {
this.model = new app.Contact();
// edited for brevity
this.model.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
createContact: function(e) {
e.preventDefault();
var attrs = this.newAttributes();
if(this.model.set(attrs, {validate: true})){
app.contactList.create(attrs);
}
},

KNockoutJS with jQuery datatables, bound rows are not updated properly

Here's JS code:
function ProductViewModel() {
// Init.
var self = this;
self.products = ko.observableArray();
self.singleProduct = ko.observable();
var mappedProducts;
// Initialize table here.
$.getJSON("/admin/test", function(allData) {
mappedProducts = $.map(allData, function(item) { return new Product(item);});
self.products(mappedProducts);
self.oTable = $('#products-table').dataTable( {
"aoColumns": [
{ "bSortable": false, "mDataProp": null, sDefaultContent: '' },
{"mData": "name"},
{"mData": "dealer"},
{"mData": "cost"},
{"mData": "price"},
{ "bSortable": false, sDefaultContent: '' }
],
});
});
// Here i'm using the basic switch pattern, as from KO tutorials.
// This is intended for showing a single product form.
self.edit = function(product) {
self.singleProduct(product);
}
// This is intended to hide form and show list back.
self.list = function() {
self.singleProduct(null);
}
// This is the form save handler, actually does nothing
// but switch the view back on list.
self.doEdit = function(product) {
self.list();
}
}
// My model.
function Product(item) {
this.name = ko.observable(item.name);
this.dealer = ko.observable(item.dealer);
this.cost = ko.observable(item.cost);
this.price = ko.observable(item.price);
this.picture = ko.observable();
}
Here's my markup:
<table id="products-table" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Pic</th>
<th>Name</th>
<th>Dealer</th>
<th>Cost</th>
<th>Price</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: $parent.products">
<tr>
<td><span data-bind='ifnot: picture'>-</span></td>
<td><a data-bind="text: name"></a></td>
<td><span data-bind='text: dealer'></span></td>
<td><span data-bind='text: cost'></span></td>
<td><span data-bind='text: price'></span></td>
<td>
<button data-bind='click: $root.edit'><i class='icon-pencil'></i>
</button>
</td>
</tr>
</tbody>
</table>
When i do click on the edit button, triggering the $root.edit handler, a form is shown because of a
<div data-bind='with: singleProduct'>
binding i have made. Inside this binding i have a form, with a
<input data-bind="value: name" type="text" id="" placeholder="Name"
> class="col-xs-10 col-sm-5">
field.
Problem: When i edit the value in the input field, the relative row in the datatable is not updated. I have tried a basic table without the datatables plugin and it does work, meaning that if i change value, the row in the table is properly updated.
What's wrong here?
== EDIT ==
I found out that moving the bind point to the table TD fixed the problem, though still i can't figure out why.
<tr>
<td data-bind="text: name"></td>
<!-- More columns... -->
</tr>
The above code is working properly now. Why ?
== EDIT2 ==
Now that i fixed first issue, comes the second. I implemented my "save new" method like so
self.doAdd = function(product) {
$.ajax("/product/", {
data: ko.toJSON({ product: product }),
type: "post", contentType: "application/json",
success: function(result) { alert('ehh'); }
}).then(function(){
self.products.push(product); // <--- Look at this!
self.list();
});
}
The self.products.push(product); in the success handler is properly updating my products observable. Then, a new row is automatically added to my table, and this is the good news.
Bad news is that datatables controls, such search field or clickable sorting arrows, disappear as soon as i push the new product in the array. Why so!?
Did you ever resolve this?
I've been having similar issues for ages.
In the end my fix was to map all my entities using ko.mapping.fromJS(entity) - this then hooked up all the required dependencies and ensured that any changes flowed through my model.
http://jsfiddle.net/zachpainter77/4tLabu56/
ko.bindingHandlers.DataTablesForEach = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var nodes = Array.prototype.slice.call(element.childNodes, 0);
ko.utils.arrayForEach(nodes, function (node) {
if (node && node.nodeType !== 1) {
node.parentNode.removeChild(node);
}
});
return ko.bindingHandlers.foreach.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var value = ko.unwrap(valueAccessor()),
key = "DataTablesForEach_Initialized";
var newValue = function () {
return {
data: value.data || value,
beforeRenderAll: function (el, index, data) {
if (ko.utils.domData.get(element, key)) {
$(element).closest('table').DataTable().destroy();
}
},
afterRenderAll: function (el, index, data) {
$(element).closest('table').DataTable(value.options);
}
};
};
ko.bindingHandlers.foreach.update(element, newValue, allBindingsAccessor, viewModel, bindingContext);
//if we have not previously marked this as initialized and there is currently items in the array, then cache on the element that it has been initialized
if (!ko.utils.domData.get(element, key) && (value.data || value.length)) {
ko.utils.domData.set(element, key, true);
}
return { controlsDescendantBindings: true };
}
};
https://rawgit.com/zachpainter77/zach-knockout.js/master/zach-knockout.debug.js

Binding Knockout.JS viewmodel to jQuery dialog

I have a jQuery modal dialog inside which I would like to pass data from a Knockout viewmodel. The dialog works fine as is - however, below code is broken.
Ideally, I would like to be able to click on the URI that triggers the modal dialog, and have the dialog load the data from the Knockout viewmodel. Any help would be greatly appreciated.
Markup:
List Names
<div id="listNames" data-bind="dataModel: { autoOpen: false, modal: true }">
<div>
<form action=''>
<p>You have added <span data-bind='text: name().length'> </span>
person(s)</p>
<table data-bind='visible: name().length > 0'>
<thead>
<tr><th>Select</th>
<th>Name</th>
<th>Age</th>
<th />
</tr>
</thead>
<tbody data-bind='foreach: metrics'>
<tr>
<td><input type="checkbox" /></td>
<td><span data-bind='text: name' > </span></td>
<td><span data-bind='text: age'> </span></td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
ViewModel:
var dataModel = function (edata) {
var self = this;
self.edata = ko.observableArray(edata);
self.addname = function () {
self.edata.push({
name: "",
age: ""
});
};
self.removename = function (name) {
self.edata.remove(name);
};
self.save = function (form) {
alert("Could now transmit to server: "
+ ko.utils.stringifyJson(self.edata));
// To actually transmit to server as a regular form post, write this:
// ko.utils.postJson($("form")[0], self.edata);
};
};
var viewModel = new dataModel([
{ name: "Jack", age: "41" },
{ name: "Jill", age: "33" }
]);
ko.applyBindings(new viewModel);
jQuery Dialog:
$("#listNames, ").dialog({
autoOpen: false,
width: 300,
modal: true,
buttons: {
"OK": function () {
$(this).dialog("destroy");
},
Cancel: function () {
$(this).dialog("close");
}
}
});
$("#openList")
.click(function () {
$("#listNames").dialog("open");
});
There are a few errors in the code you posted.
I have a working version here : http://jsfiddle.net/uFgz8/1/
Here is the HTML :
Open dialog //you had 2 elements with the same ID, I removed the ID on the link and bound it to a method in the view model
<div id="listNames"> <div>
<form action=''>
<p>You have added <span data-bind='text: name.length'> </span> person(s)</p> // name item is not observable, so you cannot use name().length
<table data-bind='visible: name.length > 0'> // same remark for name
<thead>
<tr>
<th>Select</th>
<th>Name</th>
<th>Age</th>
<th />
</tr>
</thead>
<tbody data-bind='foreach: edata'>
<tr>
<td>
<input type="checkbox" />
</td>
<td><span data-bind='text: name'> </span>
</td>
<td><span data-bind='text: age'> </span>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
The JS:
$("#listNames").dialog({
autoOpen: false,
width: 300,
modal: true,
buttons: {
"OK": function () {
// do something
$(this).dialog("close"); // I replaced destroy by close, so it can be opened after ok has been clicked
},
Cancel: function () {
$(this).dialog("close");
}
}
});
var dataModel = function (edata) {
var self = this;
self.edata = ko.observableArray(edata);
self.addname = function () {
self.edata.push({
name: "",
age: ""
});
};
self.openDialog = function () {
$("#listNames").dialog("open");
};
self.removename = function (name) {
self.edata.remove(name);
};
self.save = function (form) {
alert("Could now transmit to server: " + ko.utils.stringifyJson(self.edata));
// To actually transmit to server as a regular form post, write this: ko.utils.postJson($("form")[0], self.edata);
};
};
var viewModel = new dataModel([{
name: "Jack",
age: "41"
}, {
name: "Jill",
age: "33"
}]);
ko.applyBindings(viewModel); // you have created a variable viewModel with data, but you bound ko with a new object of type viewModel, you must either call ko with viewModel you created, or inline the creation of a new "dataModel"
edit : I added some comments to my changes
edit 2 : I updated the link to the jsfiddle to get to the correct version ;)

Categories

Resources