Tip: How to make nested objects observable in Knockout

This tip focuses on how to use Knockout mapping plugin to make nested objects observable as well. (Beginners Level for Knockout)

Lets say if we have a model like below:
public class StudentInfo
{
public virtual int Id { get; set; }
public virtual int RollNo { get; set; }
public virtual string Name { get; set; }
public DateTime BirthDate { get; set; }
[ForeignKey("MarksId")]
public virtual StudentMarks Marks { get; set; }
}
This class uses another entity called StudentMarks  which is as below:
public class StudentMarks
{
[Key]
public int MarksId { get; set; }
public float EnglishMarks { get; set; }
public float MathsMarks { get; set; }
public float ScienceMarks { get; set; }
private float totalMarks;
/// <summary>
/// TotalMarks Property will set the sum of all marks as totalMarks.
/// </summary>
[Display(Name = "Total Marks")]
public float TotalMarks
{
get { return (totalMarks); }
set { totalMarks = EnglishMarks + MathsMarks + ScienceMarks; }
}
private float percentage;
/// <summary>
/// Percentage Property will calculate percentage based on Total Marks & set             it as percentage
/// </summary>
public float Percentage
{
get { return percentage; }
set { percentage = (TotalMarks / 300) * 100; }
}
private bool result;
/// <summary>
/// Result Will be calculated based on Percentage
/// If >40 then Pass else fail
/// </summary>
public bool Result
{
get { return result; }
set
{
if (percentage >= 40)
result = true;
else
result = false;
}
}
}
If we use knockout to map a view model to studentInfo Model, then we will be having two objects basically one student object & other nested object for Marks. All the properties inside that student object can be made observable by using ko.observable but that Object will not serve as observable through that. Basically If we change any property of StudentMarks object through UI, that will not be reflected in our ViewModel & controller methods.
Here Knockout-Mapping js comes in picture. You can download Knockout-mapping js file through nuget & make use of that.
Make a viewmodel separately for Marks Object like:
var MarksViewModel = function (data) {

this.EnglishMarks = ko.observable(data ? data.EnglishMarks : 0);
this.ScienceMarks = ko.observable(data ? data.ScienceMarks : 0);
this.MathsMarks = ko.observable(data ? data.MathsMarks : 0);
this.TotalMarks = ko.computed(data ? data.TotalMarks: 0);
this.Result = ko.observable(data ? 'Result:' : 0);
this.Percentage = ko.observable(data ? data.Percentage : 0);
this.$type = 'MarksViewModel';
ko.mapping.fromJS(data, yourMapping, this);
};

Create a mapping to map Marks nested object with the created view model
var yourMapping = {

'Marks': {
create: function (options) {
return new MarksViewModel(options.data);
}
}
}

After we are fetching Data from DB & assign it to the object with which we bind controls in view, we can make use of ko.mapping.fromJS to make everything work together, like:
$.ajax({

url: '/Student/GetStudentById/' + studentId,
async: false,
dataType: 'json',
data: ko.mapping.toJSON(this),
success: function (json) {
self.student = ko.mapping.fromJS(json, yourMapping, this);
}
});

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s