What is the best practice to get MVC Model parameters within knockout Model?
1) If you need to get just one parameter, you can use the following to set up knockout view model property:
this.firstName = @Model.FirstName;
2) If you have a bunch of properties wihtin a model, you can do the following:
var modelData = function () { return @Html.Raw(Json.Encode(Model)); }();
and then later use
this.firstName = modelData.FirstName;
this.lastName = modelData.LastName;
this.phoneNumber = modelData.PhoneNumber;
etc.
3) What if you have some Collection within Person model. What is the best way to get the collection out of the person model?
For example, Person has FirstName, LastName, PhoneNumber etc. and has a List of Books (each book has tile, author and other properties).
I believe you can use seomething like this:
var booksJSON = @Html.Raw(Json.Encode(Model.Books));
and then in the knockout Person model use:
self.books = ko.observableArray(booksJSON);
This gives only Books.
But what if I want to get all information about Person (Books, including FirstName, LastName, Phone and a bunch of properties), what is the best way to do this? Can I somehow use Json.Encode
for the whole Person model and then get out of there everything, including Person.Books or should I split it into multiple Json.Encode-s
??
All data what you need should be in your C# Model. Then in knockout viewModel you should parse this data. For example:
C#
public class Person
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
public List<Book> Books {get;set}
}
public class Book
{
public int Id {get;set;}
public string Name {get;set;}
}
Html, you should convert your C# model to json:
function (ko, ViewModel) {
ko.applyBindings(new ViewModel(@Html.Raw(Model.ToJson())), document.getElementById('Container'));
});
Knockout viewModel:
function ViewModel(model) {
var self = this;
self.FirstName = ko.observable(model.FirstName);
self.LastName = ko.observable(model.LastName);
self.Books = ko.observableArray(model.Books);
}
If you want to make a simple (one-way) binding, this will do
var PersonViewModel = {};
$(function () {
var personJSON = @Html.Raw(Json.Encode(Model));
PersonViewModel = ko.observable(personJSON);
ko.applyBindings(PersonViewModel, $("#person-container").get(0));
});
But if you want a two-way binding, ie, you wanna post back changes to the existing array back to server, you wanna make all properties in the object array ko.observable
. That's because, as per knockoutjs documentation,
Simply putting an object into an observableArray doesn’t make all of
that object’s properties themselves observable. Of course, you can
make those properties observable if you wish, but that’s an
independent choice. An observableArray just tracks which objects it
holds, and notifies listeners when objects are added or removed.
To make each object properties in the array ko.observable
, we can use this generic function.
function MakeArrayKoObservableObjectArray(arr) {
var observableArr = [];
for (var i = 0; i < arr.length; i++) {
var observableObj = {}, obj = arr[i];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
observableObj[prop] = ko.observable(obj[prop]);
}
}
observableArr.push(observableObj);
}
return observableArr;
}
Now we can use the function like this.
var personJSON = @Html.Raw(Json.Encode(Model));
personJSON.Books = MakeArrayKoObservableObjectArray(personJSON.Books);
PersonViewModel = ko.observable(personJSON);