Using Knockout-Kendo - having some binding issues

1,202 views
Skip to first unread message

Stacey

unread,
Oct 30, 2013, 2:13:53 PM10/30/13
to knock...@googlegroups.com
Hey guys, I am a long time knockout user, and have recently begun to use Kendo UI - and was elated to get ahold of the knockout-kendo library { Knockout-Kendo }

But I am having a great deal of trouble with some general implementation. In general, I am using two widgets - the kendoMultiSelect, and the kendoNumericTextBox.

This is for a game that I am developing - thank you in advance for any help that you can provide. I will try to explain my problems very verbosely. To try and make it clearer, I am highlighting my problems in red, and things that work right in blue.

There is a grid, and it displays a list of items. This works as expected.
Upon clicking an item in the grid, its data is converted using the knockout mapping plugin, and populates the view model. This is also working as expected.

There is a kendoDropDownList for something known as "mutations". This is where statistics get added to game items. This pulls data from a remote place, which is working as expected. Upon clicking a statistic, it adds it to the Mutations observableArray. This is not working as expected. While it does show up in the view, it does not seem to "bind" right, as the "Preview" area does not update when the numericTextBox changes. However this only applies to new mutations - ones that existed in the initial loading phase (in the grid's change event) do update correctly.

Every item has a list of tags. They are loaded in from the grid as well. This is working as expected.

There is a kendoMultiSelect for selecting tags. This is not working as expected. It does not update with the already existing tags, and adding new tags through it does not update the view model.

(function ($, window, kendo) {
ehrpg.module('admin.controllers.prototypes', function (exports, require) {
exports.edit = function () {
var elements = {
wizard: $('#ui-wizard-prototype'),
validatable: {},
form: $('form'),
mutations: $('#mutations'),
tags: $('#tags'),
grid: $('#grid')
};

var widgets = {
grid: {},
tags: {},
mutations: {}
};

var urls = {
tags: "/administrator/items/list/tags",
prototypes: "/administrator/items/list/prototypes",
mutations: "/list/statistics"
};

var viewModel = {
Id: ko.observable(null),
Name: ko.observable(null),
Consumable: ko.observable(false),
Equipable: ko.observable(false),
Mutations: ko.observableArray([]),
Tags: ko.observableArray([])
};

var Setup = {
Grid: function($widget, $target, $url){
$widget = $target.kendoGrid({
dataSource: {
transport: {
read: {
url: $url,
dataType: "json",
type: 'GET'
}
},
schema: {
total: "total",
data: "data"
},
page: 0,
pageSize: 15,
take: 15,
serverPaging: true,
serverFiltering: true,
type: "aspnetmvc-ajax"
},
pageable: {
refresh: true,
pageSizes: true
},
selectable: "row",
change: function (e) {
// get the selected row from the grid
var selected = this.select();
// get the data from the selected row
var data = this.dataItem(selected);
// update the knockout view model
ko.mapping.fromJS(data.toJSON(), {}, viewModel);
console.log(viewModel);
},
columns: [
{
field: "Id",
width: 25,
title: "Identity"
},
{
field: "Name",
width: 90,
title: "Name"
}
]
});
},
//// used to instantiate a wizard control on the view
Wizard: function ($target, $form) {
// create the new wizard on the given element
$target.wizard({
onFinish: function () {
if (elements.form.valid()) {
$.ajax({
url: '/administrator/items/edit/prototype',
type: 'POST',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: $.toJSON({ model: viewModel })
}).done(function (data) {
// reset the view model
}).fail(function () {

});
}
}
});
},

//// used to instantiate and attach a validator to the view
Validator: function ($target, $form) {
$target = $form.validate({
rules: {
name: {
required: true,
pattern: "^(?!.*[ ]{2})(?!.*[']{2})(?!.*[-]{2})(?:[a-zA-Z0-9 \p{L}'-]{3,64}$)$"
}
},
messages: {
name: {
required: "You must enter a name",
pattern: "You have entered an invalid name."
}
}
});
},

//// used to wire up the combo box for selecting mutations in the view
Mutations: function ($widget, $element, $url, $model) {
$widget = $element.kendoComboBox({
dataTextField: "Name",
delay: 800,
dataSource: {
serverFiltering: true,
type: "json",
transport: {
read: {
url: $url
}
}
},
select: function (e) {
// retrieve the slot from the database and construct a new
// observable view model for it
var statistic = this.dataItem(e.item.index()).toJSON();

// create a new mutation for this statistic 
var mutatable = {
Statistic: {
Id: statistic.Id,
Name: statistic.Name
},
Measurement: 0.000
};

$model.Mutations.push(mutatable);
}
}).data("kendoAutoComplete");
},

Tags: function ($widget, $element, $url, $model) {
$widget = $element.kendoMultiSelect({
dataTextField: "Name",
dataValueField: "Id",
itemTemplate: $('#editing-tags-template').html(),
dataSource: {
transport: {
read: {
dataType: "json",
url: $url
}
}
},
open: function (e) {
this.list.addClass("k-tag-cloud");
},
close: function (e) {
//e.preventDefault();
},
select: function(e){
console.log(ko.toJS(viewModel));
}
}).data("kendoMultiSelect");
}
}

// create an over encompassing view model that will
// synchronize with the entire view
Setup.Grid(widgets.grid, elements.grid, urls.prototypes);
Setup.Validator(elements.validatable, elements.form);
Setup.Wizard(elements.wizard, elements.form);
Setup.Mutations(widgets.mutations, elements.mutations, urls.mutations, viewModel);
Setup.Tags(widgets.tags, elements.tags, urls.tags, viewModel);

// perform the view model binding
//kendo.bind($("#binding-content"), viewModel);
ko.applyBindings(viewModel);

// return the view model to the global scope
return viewModel;
};
});
})(jQuery, window, kendo);



@{ Html.BeginForm(); }
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<div id="ui-wizard-prototype">
<div style="width: 600px; float: left;">
<div class="ui-wizard-step">
<div style="width: 300px;">
<h2 class="fg-color-blueDeep">Name</h2>
<div class="input-control" data-for="name">
<input data-bind="value: Name"
  data-val="true"
  required
  pattern="^(?!.*[ ]{2})(?!.*[']{2})(?!.*[-]{2})(?:[a-zA-Z0-9 \p{L}'-]{3,64}$)$"
  data-msg-pattern="This isn't a good name."
  data-msg-required="The item prototype name is required."
  id="name"
  name="name"
  placeholder="Name"
  type="text"
  title="name"
  style="width: 280px; font-size: 2em;" />
</div>
</div>
<hr />
<div style="width: 300px;">
<div class="input-control checkbox" data-for="equipable">
<input type="checkbox"
  name="equipable"
  id="equipable"
  data-bind="checked: Equipable" />
<label for="equipable" class="fg-color-blueDeep helper">
Equipable
</label>
</div>
</div>
<div style="width: 300px;">
<div class="input-control checkbox" data-for="consumable">
<input type="checkbox"
  name="consumable"
  id="consumable"
  data-bind="checked: Consumable" />
<label for="consumable" class="fg-color-blueDeep helper">
Consumable
</label>
</div>
</div>
<div style="width: 500px;">
<h2 class="fg-color-blueDeep">Mutations</h2>
<div class="input-control" data-for="mutations">
<input data-val="true"
  required
  id="mutations"
  name="mutations"
  placeholder="Statistic"
  type="text"
  title="mutations"
  class="dark"
  style="width: 280px; margin: 0px; padding: 0px;" />
</div>
<div data-bind="foreach: Mutations">
<input id="${Statistic.Name}-size"
  type="number"
  class="dark"
  style="width: 100px; float: left;"
  data-bind="value: Measurement"
  data-role="numerictextbox"
  data-format="\#.\#\#\#\#"
  data-decimals="5"
  data-step="0.1" />
<h1 data-bind="text: Statistic.Name"></h1>
</div>
</div>
<div style="width: 500px;">
<h2 class="fg-color-blueDeep">Tags</h2>
<div class="input-control" data-for="tags">
<select id="tags" multiple="multiple" 
data-placeholder="Select Tags..." 
class="dark k-tag-cloud" 
data-bind="kendoMultiSelect: { value: Tags }"
style="width: 500px;"></select>
</div>
</div>
</div>
</div>
<div style="width: 400px; float: left; padding-left: 15px;">
<h2>Preview</h2>
<div style="border: dashed 2px black;">
<div style="padding: 8px;">
<h3 data-bind="text: Name" style="margin: auto;"></h3>
<h5 data-bind="visible: Equipable" style="margin: auto;">Equipable</h5>
<h5 data-bind="visible: Consumable" style="margin: auto;">Consumable</h5>
<div data-bind="foreach: Mutations">
+<span data-bind="text: Measurement"></span> <span data-bind="text: Statistic.Name"></span>
</div>
</div>
</div>
</div>
</div>
@{ Html.EndForm(); }
</article>


michael....@gmail.com

unread,
Jan 21, 2014, 11:50:13 AM1/21/14
to knock...@googlegroups.com
Hello!
I've faced the same issue. I've tried to use custom serialization like it was described here, but the serialization is called two times.
However, if I use just 'ajax' for 'type' the nested objects deserialized ok.

среда, 30 октября 2013 г., 20:13:53 UTC+2 пользователь Stacey написал:
Reply all
Reply to author
Forward
0 new messages