I have a MVC site where I use Kendo UI and knockout.js to display the pages. One scenario is to get the database information from server through $.getJSON and then display this information on a KendoUI grid.
<div data-bind="kendoGrid:{sortable:true, data:users, rowTemplate:'userRowTemplate'}>
<table>
<thead>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead> </table>
</div>
<script type="text/html">
<tr>
<td data-bind="text: Username"></td>
<td data-bind="text: FirstName"></td>
<td data-bind="text: LastName"></td>
<tr>
</script>
and the javascript :
<script type="text/javascript">
var ViewModel = function () {
var self=this;
self.users=ko.mapping.fromJS([]);
$getJSON("/UserManagementController/GetUsers",function(data){
ko.mapping.fromJS(data,{},self.users);
});
};
$(document).ready(function(){
var newViewModel=new ViewModel();
ko.applyBindings(newViewModel);
});
</script>
I want this data to be sortable on specific columns (the ones specified here are for example's sake), but I haven't been able to achieve this successfully. I have tried the solution from this knockout-kendo plugin issue post, which works well on simple objects, but does not work on observables. So my question is : how to map data from database through MVC controller to observables in knockout and displaying them in a Kendo grid, but still be able to sort them?
Thanks,
Alex Barac
You can do this by creating a JS view model to represent the data coming back from the server, and mapping the data into the view model. Then you can have simple objects that get set by subscribing to the matching observable properties to implement the sort.
Here is an example:
http://jsfiddle.net/R4Jys/1/
HTML:
<div data-bind="kendoGrid: gridOptions(myList)"></div>
<script id="rowTmpl" type="text/html">
<tr>
<td>
<span data-bind="text: firstName" />
</td>
<td>
<span data-bind="text: lastName" />
</td>
<td>
<span data-bind="text: userName" />
</td>
</tr>
</script>
JavaScript:
var gridItem = function () {
var self = this;
self.firstName = ko.observable();
self.firstNameSort;
self.firstName.subscribe(function (value) {
self.firstNameSort = value;
});
self.lastName = ko.observable();
self.lastNameSort;
self.lastName.subscribe(function (value) {
self.lastNameSort = value;
});
self.userName = ko.observable();
self.userNameSort;
self.userName.subscribe(function (value) {
self.userNameSort = value;
});
self.other = ko.observable('test');
return self;
};
var vm = function() {
var self = this;
self.myList = ko.observableArray();
self.test = ko.observable();
self.gridOptions = function (data) {
return {
data: data,
rowTemplate: 'rowTmpl',
useKOTemplates: true,
scrollable: true,
sortable: true,
columns: [
{
field: "firstNameSort",
title: "First Name",
width: 130
},
{
field: "lastNameSort",
title: "Last Name",
filterable: true
},
{
field: "userNameSort",
title: "Username"
}
]
}
};
var data = [{'firstName':'Steve', 'lastName':'Jones', 'userName': 'steve.jones'},
{'firstName':'Janet', 'lastName':'Smith', 'userName': 'janet.smith'},
{'firstName':'April', 'lastName':'Baker', 'userName': 'april.baker'},
{'firstName':'Dave', 'lastName':'Lee', 'userName': 'dave.lee'},
{'firstName':'Jack', 'lastName':'Bennet', 'userName': 'jack.bennet'},
{'firstName':'Chris', 'lastName':'Carter', 'userName': 'chris.carter'}];
self.myList(ko.utils.arrayMap(data, function(item) {
var g = new gridItem();
ko.mapping.fromJS(item, {}, g);
return g;
}));
};
var pageVm = new vm();
ko.applyBindings(pageVm);
As an alternative you can modify the kendo.web.js (version 2013.3.1119) on line 6844 & 6844.
Replace
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = selector(a);
b = selector(b);
With
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = ko.utils.unwrapObservable(selector(a));
b = ko.utils.unwrapObservable(selector(b));
By using the knockout utility "ko.utils.unwrapObservable" you can get the value of the observable for use when kendo compares the values of the column
DEFINE CUSTOM COMPARE FUNCTION will serves solution, where you can override the comparing function in field definition.
So you can do something like this:
$("#grid").kendoGrid({
dataSource: dataSource,
sortable: true,
columns: [{
field: "item",
sortable: {
compare: function(a, b) {
var valueA = a["item"];
var valueB = b["item"];
if (typeof valueA === "function") valueA = valueA();
if (typeof valueB === "function") valueB = valueB();
if (this.isNumeric(valueA) && this.isNumeric(valueB))
{
valueA = parseFloat(valueA);
valueB = parseFloat(valueB);
}
if (valueA && valueA.getTime && valueB && valueB.getTime)
{
valueA = valueA.getTime();
valueB = valueB.getTime();
}
if (valueA === valueB)
{
return a.__position - b.__position;
}
if (valueA == null)
{
return -1;
}
if (valueB == null)
{
return 1;
}
if (valueA.localeCompare)
{
return valueA.localeCompare(valueB);
}
return valueA > valueB ? 1 : -1;
}
}
}]
});
private isNumeric(input)
{
return (input - 0) == input && ('' + input).trim().length > 0;
}
The compare method is stolen from kendo script, but change is where the property is typeof function (what ko.observable is), it unwraps the value. Plus, I added support for numbers.
Related
I have c# classes that need to be passed into a knockout view model for look up purposes. Show a user status of 1000 or a role of 14038 is useless.
I need to be able to resolve those values to their text representation.
I've got some "reference data" populated in a database. On top of that are some T4 transformations which translate the reference data into C# code, for example:
public static class UserStatus
{
#region Members
public const string ClassName = "UserStatus";
public const int Pending = 1000;
public const int Active = 1001;
public const int Inactive = 1002;
public const int Deleted = 1003;
#endregion
}
This class is then used throughout the code to assign values to a User class, instead of saying user.UserStatus = 1000 it's user.UserStatus = UserStatus.Pending.
Now onto the issue...
CURRENT ISSUE
I've got a page that lists users in the system and one of the columns in the list is the user's status. Well that status passed from in the object is 1000 not "Pending". What I'd like to do is be able to resole 1000 to "Pending" in knockout. Problem is because knockout is executed on the client side it has no knowledge of my C# classes. Ideally I'd like to be able to pre-poplate a list in my controller with the possible values of a UserStatus, pass that into my knockout view model and have it loop through the possible statuses and resolve it based on that specific users status.
HTML Code
<tbody style="display: none;" class="table-body" data-bind="visible: true, foreach: { data: viewData.ClientGroups, as: 'ClientGroup' }">
<tr>
<td><span data-bind="html: ClientGroup.Name()"></span></td>
<td>TODO: Bind Client</td>
<td><span data-bind="html: ClientGroup.StatusText()"></span></td>
<td><span data-bind="html: ClientGroup.CreatedOnText()"></span></td>
</tr>
</tbody>
Creating and binding the knockout view model.
var viewData = {};
require(['main'], function () {
require(['message', 'viewModel/clientGroupViewModel', 'viewModel/clientGroupDetailsViewModel'],
function (message, clientGroupViewModel, clientGroupDetailsViewModel) {
$(document).ready(function () {
// enable ko punches
ko.punches.enableAll();
var json = #Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model, Newtonsoft.Json.Formatting.None, new IHtmlStringConverter()));
// I'd like to be able to create something here and pass it to my model.
var lookupValues = { blah }
viewData = new clientGroupViewModel(json, lookupValues);
var zdm = new clientGroupDetailsViewModel(json, lookupValues );
ko.applyBindings(viewData, document.getElementById('clientGroupAdmin'));
ko.applyBindings(zdm, document.getElementById('detailsModal'));
});
});
});
In the knockout view model
_self.StatusText = ko.computed(function () {
console.log('user status');
if (ko.utils.arrayFirst(UserStatus, function (value) {
console.log(value);
return value.Id == _self.UserStatus();
}));
return 'false';
});
Please read my extra info questions but this is one way how I'd go about it..
console.clear();
var $fakeAsync = function (api, result) {
var isEmptyCall = _.isNull(api) || api.length === 0;
dfd = $.Deferred(function () {
setTimeout(function () {
dfd.resolve(isEmptyCall ? null : result);
}, isEmptyCall ? 0 : 50);
});
return dfd.promise();
};
var $fakeData = [
{Id: 1, Name: 'foo', UserStatus: 1000},
{Id: 2, Name: 'bar', UserStatus: 1001},
{Id: 3, Name: 'stack', UserStatus: 1000},
{Id: 4, Name: 'overflow', UserStatus: 1002}
]
var $fakeUserStates = {
1000: 'pending',
1001: 'ready',
1002: 'cancelled',
1003: 'banned'
};
function MyViewModel() {
var self = this;
self.userStates = [];
self.loadMapping = function () {
return $fakeAsync('api/fakemappingcall', $fakeUserStates).done(function (result) {
console.log('mapping gathered');
self.userStates = result;
});
};
self.loadData = function () {
return $fakeAsync('api/fakedatacall', $fakeData);
};
self.method = function () {
self.loadData().done(function (result){
_.each(result, function (r) { _.extend(r, { 'UserStatusText': self.userStates[r['UserStatus']] }) });
console.log(result);
});
};
self.loadMapping().done(function () {
self.method();
});
};
ko.applyBindings(new MyViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
I'm using a viewbag to load the options for a multi-select list. However, when I do that I'm getting a 1-based index instead of the actual text value.
<td> #Html.DropDownList("MAMUserGroupName", (MultiSelectList)ViewBag.MAMUserGroupsList, new { multiple = "multiple", data_bind = "selectedOptions:MAMUserGroups" })</td>
Here's the viewModel:
var viewModel = new UserModel([
{
id: ko.observable(""), firstName: ko.observable(""), lastName: ko.observable(""),MAMUserGroups: ko.observable()
}
]);
When the value is selected and I call the knockout save function, the value is 1, 2, and/or 3, instead of the actual text value; in this case "Group1", "Group2", "Group3"
When I do it the standard way of hard-coded options like here it works as expected: Knockout selectedOptions example. But in my case I need to load it from the viewbag.
The #Html.DropdownList is a server side rendering of the list and knockoutjs is a client side rendering of the list and the two do not know about each other. What you need to do is either render the list to a javascript array on they page, or retrieve it via Ajax/fetch from the server then render it client side. So using your code examples, i would be filling you knockoutjs view model with the data you need. something like the following.
var PageModel = function(data) {
var users = ko.observableArray();
var userGroups = ko.observableArray();
var mappedUsers = data.Users.map(function(user) {
return {
id: ko.observable(user.id),
firstName: ko.observable(user.firstName),
lastName: ko.observable(user.lastName),
mameUserGroups: ko.observableArray()
};
});
users(mappedUsers);
var mappedGroups = data.MAMUserGroups.map(function(item) {
return {
id: item.id,
name: item.name
};
});
userGroups(mappedGroups);
return {
users: users,
userGroups: userGroups
};
};
ko.applyBindings(new PageModel({
//****** Data loaded here ******
Users: [{
id: 1,
firstName: "test",
lastName: "user 1"
}, {
id: 2,
firstName: "test",
lastName: "user 2"
}, {
id: 3,
firstName: "test",
lastName: "user 3"
}, {
id: 4,
firstName: "test",
lastName: "user 4"
}],
MAMUserGroups: [{
id: 1,
name: "User Group 1"
}, {
id: 2,
name: "User Group 2"
}, {
id: 3,
name: "User Group 3"
}, {
id: 4,
name: "User Group 4"
}]
}));
<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.4.2/knockout-min.js"></script>
<table>
<tbody data-bind="foreach: users">
<tr>
<td data-bind="text: firstName"></td>
<td data-bind="text: lastName"></td>
<td>
<select data-bind="options: $parent.userGroups, selectedOptions: mameUserGroups, optionsText: 'name' " size='5' multiple='true'> </select>
</td>
</tr>
</tbody>
</table>
<ul data-bind="foreach: users">
<li>
<span data-bind="text: firstName"></span><span data-bind="text: lastName"></span>
<ul data-bind="foreach: mameUserGroups">
<li data-bind="text: name"></li>
</ul>
</li>
</ul>
<!--
<script type="text/javascript">
// Render the data on the page server side to then be used client side
$().ready(function(){
var users = #(Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Users)));
var mamUserGroups = #(Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.mamUserGroups)));
});
</script>
-->
EDIT Ajax implementation
This is one way to do it. probably more code than is needed but i like to break things up into separate area of responsibility. If you try running this snippet from here what you will get is an error alert and a finished alert for the users and user groups.
var app = window.app || {};
app.modelMap = (function() {
function userMap(user) {
return {
id: ko.observable(user.id),
firstName: ko.observable(user.firstName),
lastName: ko.observable(user.lastName),
mameUserGroups: ko.observableArray()
};
}
function userGroupMap(userGroup) {
return {
id: userGroup.id,
name: userGroup.name
};
}
return {
userMap: userMap,
userGroupMap: userGroupMap
}
})();
app.data = (function() {
function getUserGroups() {
return $.get("<MAMUSerGroup_URL_HERE>").then(function(data) {
alert("successfully retrieved user group data");
return data.map(app.modelMap.userGroupMap);
})
.fail(function() {
alert("error");
})
.always(function() {
alert("finished");
});
};
function getUsers() {
return $.get("<Users_URL_HERE>").then(function(data) {
alert("successfully retrieved user data");
return data.map(app.modelMap.userMap);
})
.fail(function() {
alert("error");
})
.always(function() {
alert("finished");
});
}
return {
getUserGroups: getUserGroups,
getUsers: getUsers
}
})();
app.pageModel = (function() {
var users = ko.observableArray();
var userGroups = ko.observableArray();
function getUsers() {
return app.data.getUsers().done(users);
}
function getUserGroups() {
return app.data.getUserGroups().done(userGroups);
}
function activate() {
getUsers();
getUserGroups();
}
var vm = {
users: users,
userGroups: userGroups,
activate: activate
};
return vm;
})();
var vm = app.pageModel;
vm.activate();
ko.applyBindings(vm);
<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.4.2/knockout-min.js"></script>
probably going about this the wrong way but I have an html table that is populated using a knockout observable array using foreach. in each row I have a drop down. I like the jquery ui select menu so I am using that. unfortunately when you run the fiddle you will notice that when updating the drop down the corresponding knockout selected value is not being updated.
here is the fiddle https://jsfiddle.net/8rw4ruze/3/
here is the html.
<table class="table table-condensed table-responsive">
<thead>
<th>id</th>
<th>animal</th>
<th>Selected Value</th>
</thead>
<tbody data-bind="foreach: tableRows">
<tr>
<td data-bind="text: id"></td>
<td>
<select class="form-control" data-bind="options: recordList,
optionsText: 'desc',
optionsValue: 'val',
value: selectedVal,
attr: {id: selectId}">
</select>
</td>
<td data-bind="text: selectedVal"></td>
</tr>
</tbody>
</table>
and here is the javascript
function record(val, desc) {
var self = this;
this.val = ko.observable(val);
this.desc = ko.observable(desc);
}
function tableRow(id, recordList) {
var self = this;
this.id = ko.observable(id);
this.recordList = ko.observable(recordList)
this.selectedVal = ko.observable('A');
this.selectId = ko.computed(function() {
return 'mySelect' + self.id()
}, this);
}
function Model() {
var self = this;
this.records = ko.observableArray("");
this.tableRows = ko.observableArray("");
}
var mymodel = new Model();
$(function() {
mymodel.records.push(new record('A', 'ant'));
mymodel.records.push(new record('B', 'bird'));
mymodel.records.push(new record('C', 'cat'));
mymodel.records.push(new record('D', 'dog'));
mymodel.tableRows.push(new tableRow(1, mymodel.records()));
mymodel.tableRows.push(new tableRow(2, mymodel.records()));
mymodel.tableRows.push(new tableRow(3, mymodel.records()));
mymodel.tableRows.push(new tableRow(4, mymodel.records()));
ko.applyBindings(mymodel);
for (var i = 0; i < 4; i++) {
var id = '#mySelect' + (i + 1)
$(id).selectmenu({
width: 125,
change: function(event, ui) {
var newVal = $(this).val();
mymodel.tableRows()[i].selectedVal(newVal);
}
});
}
});
thanks all I went with a data attribute. I'd prefer to use the custom binding but I'm not smart enough to figure that out so I went with this.
for (var i = 0; i < 4; i++) {
var id = '#mySelect' + (i + 1)
$(id).selectmenu({
width: 125,
change: function(event, ui) {
var newVal = $(this).val();
var index = $(this).data( "index" );
mymodel.tableRows()[index].selectedVal(newVal);
}
}).data( "index", i );
}
here is the fiddle https://jsfiddle.net/8rw4ruze/7/
I think I got it working with the custom binding here it is
https://jsfiddle.net/8rw4ruze/8/
Matt.k is correct that your value of i is not what you want it to be when it is used. One way around that is to use this loop instead of a simple for:
$('select').each(function (i, e) {
e.selectmenu({
width: 125,
change: function(event, ui) {
var newVal = $(this).val();
mymodel.tableRows()[i].selectedVal(newVal);
}
});
});
This is a little fragile, as it relies on the i and the e to correspond correctly. A more robust (and typical in Knockout) approach is to use a binding handler, which would allow you to specify the binding and have Knockout go through the elements and make the necessary calls. Surprisingly, a little web-searching didn't turn one up for me.
I see there is a knockout-jqueryui project on github that might also give you a clean way of using the widgets you want.
I'm trying to make a custom binding based on this code, http://jsfiddle.net/rniemeyer/WpnTU/ , Mine after a field checkbox is selected then open a jQueryUI dialog. Here is the code: http://jsfiddle.net/superjohn_2006/UFEg6/ , another question is it posible to acomplish this without a template.
<table>
<tbody data-bind="foreach: records">
<tr data-bind="foreach: fields">
<th align="left">
<input type="checkbox" data-bind="checked: chkedValue" /><span data-bind=" text: field"></span>
</th>
</tr>
<tr data-bind="foreach: fields">
<th align="left"><a data-bind="click: $root.addFormatting" href="#">Add Formatting</a></th>
</tr>
<tr data-bind="foreach: row">
<td data-bind="text: value"></td>
</tr>
</tbody>
</table>
<div id="details" data-bind="jqDialog: { autoOpen: false, resizable: false, modal: true }, template: { name: 'editTmpl', data: selectedField }, openDialog: selectedField">
</div>
<script id="editTmpl" type="text/html">
<p>
<label>Selected Field: </label>
<span data-bind="value: field" />
</p>
<button data-bind="jqButton: {}, click: $root.accept">Accept</button>
<button data-bind="jqButton: {}, click: $root.cancel">Cancel</button>
</script>
**The model
// custom binding
ko.bindingHandlers.jqDialog = {
init: function(element, valueAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor()) || {}; // initialize a jQuery UI dialog
$(element).dialog(options);
// handle disposal
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).dialog("destroy");
});
}
};
//custom binding handler that opens/closes the dialog
ko.bindingHandlers.openDialog = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).dialog("open");
} else {
$(element).dialog("close");
}
}
};
//custom binding to initialize a jQuery UI button
ko.bindingHandlers.jqButton = {
init: function(element, valueAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor()) || {};
//handle disposal
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).button("destroy");
});
$(element).button(options);
}
};
var resultsData = [
{ fields: [{ field: "Field1", chkedValue: false }, { field: "Field2", chkedValue: false }] },
{ row: [{ value: "1" }, { value: "True" }] },
{ row: [{ value: "2" }, { value: "False" }] }
];
var TableModel = function (records) {
var self = this;
self.records = ko.observableArray(ko.utils.arrayMap(records, function (record) {
return { fields: ko.observableArray(record.fields), row: ko.observableArray(record.row) };
}));
self.selectedField = ko.observable();
self.addFormatting = function (formatToAdd) {
self.selectedField();
};
};
this.accept = function() {
},
this.cancel = function() {
}
ko.applyBindings(new TableModel(resultsData));
the following couple of lines need to be changed.
span data-bind="value: field"
for:
span data-bind="text: $data.field"
and,
self.selectedField();
for:
self.selectedField(formatToAdd);
modified code is in the same jsFiddle, jus add: /1/
to the end of the url address.
attached the file EXAMPLE: http://jsfiddle.net/brux88/9fzG4/1/
hi,
I'm starting to use knockoutjs in an asp.net mvc project.
i have a view :
<button data-bind='click: load'>Load</button>
<table>
<thead>
<tr>
<th>Cliente</th>
<th>Colli</th>
<th>Tara</th>
<th>Peso Tara</th>
<th> </th>
</tr>
</thead>
<tbody data-bind='foreach: righe'>
<tr>
<td>
<select data-bind="
value: selectedCli,
options: clienteList,
optionsText: function(item) { return item.Rscli + '-' + item.Codcli },
optionsCaption: '--Seleziona un Cliente--'"
style=" width: 150px">
</select>
</td>
<td >
<input data-bind='value: Ncolli' />
</td>
<td>
<select data-bind="value: selectedTara,
options: taraList,
optionsText: function(item) { return item.Destara +
'-' + item.Codtara},
optionsCaption: '--Seleziona un Cliente--'"
style=" width: 150px">
</select>
</td>
<td >
<input data-bind="value: Ptara" />
</td>
<td>
<a href='#' data-bind='click: $parent.rimuoviRiga'>Elimina</a>
</td>
</tr>
</tbody>
</table>
<button data-bind='click: aggiungiRiga'>Aggiungi</button>
<button data-bind='click: salva'>Salva</button>
<button data-bind='click: annulla'>Annulla</button>
my result from data db:
[{"Codcli":4,"Rscli":"antonio","Codtart":"1002","Despar":"ciliegino","Ncolli":10,"Pcolli":100,"Codtara":"03","Destara":"","Ptara":82,"Pnetto":18,"Prezzo":1},{"Codcli":1,"Rscli":"bruno","Codtart":"1001","Despar":"pomodoro","Ncolli":10,"Pcolli":100,"Codtara":"03","Destara":"","Ptara":10,"Pnetto":90,"Prezzo":1}]
my viewmodel knockoutjs:
<script type="text/javascript">
var listCli= [{Codcli: 1,Rscli: "Bruno"},{Codcli: 2,Rscli: "Pippo"},{Codcli: 3,Rscli: "Giacomo"}];
var listTa= [{Codtara: 01,Destara: "Plastica",Pertara:4},{Codtara: 02,Destara: "Legno",Pertara:6},{Codtara: 03,Destara: "Ploto",Pertara:8}];
var mydataserver = [{"Codcli":3,"Rscli":"Giacomo","Ncolli":10,"Codtara":"03","Destara":"Legno","Ptara":82},{"Codcli":1,"Rscli":"Bruno","Ncolli":10,"Codtara":"02","Destara":"Plastica","Ptara":10}];
var RigaOrdine = function () {
var self = this;
self.selectedCli = ko.observable();
self.clienteList = ko.observableArray(listCli);
self.Ncolli = ko.observable();
self.selectedTara = ko.observable();
self.taraList = ko.observableArray(listTa);
self.Ptara = ko.observable();
self.Ncolli.subscribe(function () {
self.Ptara(self.Ncolli() ? self.selectedTara().Pertara * self.Ncolli() : 0);
});
self.selectedTara.subscribe(function () {
self.Ptara(self.Ncolli() ? self.selectedTara().Pertara * self.Ncolli() : self.selectedTara().Pertara);
});
};
var Ordine = function () {
var self = this;
self.righe = ko.observableArray([new RigaOrdine()]); // Put one line in by default
// Operations
self.aggiungiRiga = function () {
self.righe.push(new RigaOrdine());
};
self.rimuoviRiga = function (riga) {
self.righe.remove(riga);
};
self.salva = function() {
var righe = $.map(self.righe(), function (riga) {
return riga.selectedCli() ? {
Codcli: riga.selectedCli().Codcli,
Rscli: riga.selectedCli().Rscli,
Ncolli: riga.Ncolli(),
Codtara: riga.selectedTara().Codtara,
Ptara: riga.Ptara(),
} : undefined;
});
alert( ko.toJSON(righe));
//save to server
/* $.ajax({
url: "/echo/json/",
type: "POST",
data: ko.toJSON(righe),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function(data) {
}
});*/
self.righe([new RigaOrdine()]);
};
//load from server
self.load = function() {
$.ajax({ url: '/echo/json/',
accepts: "application/json",
cache: false,
statusCode: {
200: function (data) {
alert(ko.toJSON(mydataserver));
//i do not know apply to viewmodel
},
401: function (jqXHR, textStatus, errorThrown) {
alert('401: Unauthenticated');
// self.location = "../../Account/Login.html?returnURL=/Index.html";
}
}
});
};
self.annulla = function() {
self.righe([new RigaOrdine()]);
};
};
var viewmodel = new Ordine();
ko.applyBindings(viewmodel);
</script>
if I want to load data from a db, how do I? Whereas there are dropdownlist
Your question is a bit weak so I will give you a more general answer.
To answer your question regarding how to load data from a db, it looks like that you have started on the right track. Usually you use an AJAX request to do the async request of data. To do this, knockoutJS provides the following function:
$.getJSON("/some/url", function(data) {
// Now use this data to update your view models,
// and Knockout will update your UI automatically
})
Source: http://knockoutjs.com/documentation/json-data.html
In the callback provided you will have access to the data returned from the server. It depends on the logic of your application what you want to do here - for some applications it might make sence to update the state of the viewmodel to make corresponding updates in the view.
If your question is more specific, please elaborate. Otherwise, I hope I got you on the right track.
As i can see.. you may want some good pratice to load.
I'll share with you mine.
Well.. return a Json as an JsonResult.
// POST: /Client/LoadClient
[HttpPost]
public JsonResult LoadClient(int? id)
{
if (id == null) return null;
var client = _business.FindById((int) id);
return Json(
new
{
id = cliente.id,
name = cliente.name,
list = cliente.listOfSomething.Select(s => new {idItemFromList = s.idWhatever, nameItemFromList = s.nameWhatever})
});
}
JS
viewmodel.Client.prototype.LoadClient= function (id) {
var self = this;
if (id == 0) {
return null;
}
$.ajax({
url: actionURL("LoadClient", "Client"),
data: { id: parseInt(id) },
dataType: "json",
success: function (result) {
if (result != null)
self.Load(result);
}
});
Load method.
viewmodel.Client.prototype.Load = function (result) {
var self = this;
self.idClient(result.id);
self.nameCliente(result.name);
self.ListOfSomething(result.list);
};
and..
ko.applyBinding(yourModel);
as u can see I'm using prototype it's a good practice too.