Problems using the Razor model when creating my knockout ViewModel - asp.net-mvc

I'm having problems with using the Model in a javascript call when setting up my knockout VM..
#model List<AdminGui.Models.Domain>
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js" type="text/javascript"></script>
<script type="text/javascript">
function ViewModel() {
var self = this;
self.domains = ko.observableArray(
ko.utils.arrayMap(#Model, function(item) {
return new Domain(item.guid, item.description, item.namespaces);
}));
}
I get a syntax error on #Model in the ko.utils.arrayMap call. I suspect it might be my Razor-fu that is lacking... :)

The Model is your C# model which cannot be used directly from JavaScript, because writing #Model just calls ToString on it which is not what you need.
What you need is to convert your Model to JSON, you can do this with the Json.Encode method
self.domains = ko.observableArray(
ko.utils.arrayMap(#Html.Raw(Json.Encode(Model)), function(item) {
return new Domain(item.guid, item.description, item.namespaces);
}));
Note: You need to wrap it with Html.Raw to turn off the automatic HTML Encoding in Razor.

Related

How KnockoutJS Mapping plugin works

here i got a sample code which said KnockoutJS Mapping plugin automatically bind UI.
here is code
<script src="~/Scripts/knockout.mapping-latest.js"></script>
<script type="text/javascript">
$(function() {
var viewModel = ko.mapping.fromJS(#Html.Raw(Model.ToJson()));
ko.applyBindings(viewModel);
});
</script>
suppose my html binding as follows
<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>
and my server side model return json like
{fname:Josh;lname:Steve};
so here my question is how KnockoutJS Mapping plugin could understand that fname value need to map to databinding firstName & lname need to map to databinding lastName ?
am i clear what i am trying to say. so guide me in this case how one could bind json to html UI through KnockoutJS Mapping plugin.
in this situation KnockoutJS Mapping plugin would be right choice or not ?
do i need to bind manually by viewmode like
First name:
Last name:
json as follows var person = {fname:Josh;lname:Steve};
var viewModel = function(person) {
this.firstname= ko.observable(person.fname);
this.lastname= ko.observable(person.lname);
};
ko.applyBindings(new viewModel(person));
As requested in the comment I put this as a possible answer.
If you have model with many properties and want only some of them to map to different names, this is the way to do it:
var viewModel = ko.mapping.fromJS(#Html.Raw(Model.ToJson()));
var extendViewModel = function () {
this.firstName = ko.computed({
read: function(){
return viewModel.fname();
},
write: function(value){
viewModel.fname(value);
},
owner: viewModel
});
};
viewModel = ko.mapping.fromJS(new extendViewModel(), viewModel);
ko.applyBindings(viewModel);
Then you can use firstName in your markup for bindings and viewModel.fname will be updated, too.

Knockout Mapping attempt not mapping with JSON responses

I am new to knockout and cannot seem to get a simple mapping to work at all. I am in an MVC4 C#.
My View looks like such:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "Index";
}
<h2>Simple Mapping Example</h2>
The time on the server is: <span data-bind="text: ServerTime"></span>
and <span data-bind="text: NumberOfUsersConnected"></span> user(s) are connected.
#section Scripts
{
<script type="text/javascript">
function ViewModel() {
var self = this;
self.ServerTime = ko.observable();
self.NumberOfUsersConnected = ko.observable();
}
var vm = new ViewModel();
ko.applyBindings(vm);
$.getJSON('/Mapping/GetServerInfo', function(data) {
ko.mapping.fromJSON(data, {}, vm);
});
</script>
}
Example JSON that is returned from the controller call 'Mapping/GetServerInfo' is:
{"ServerTime":"2013-03-13T14:24:10.5675104-07:00","NumberOfUsersConnected":5}
For the data-binds I have tried text and value, neither one causes any data to be bound and displayed.
Am I missing something really obvious?
As per the question in the comments yes this is what is in my Layout.
<script type="text/javascript" src="~/Scripts/knockout-2.2.1.js"></script>
<script type="text/javascript" src="~/Scripts/knockout.mapping-latest.js"></script>
Thanks
From the $.getJSON documentation
The success callback is passed the returned data, which is typically a
JavaScript object or array as defined by the JSON structure and parsed
using the $.parseJSON() method.
So your the data variable is holding an already parsed object so you need to use the ko.mapping.fromJS method:
$.getJSON('/Mapping/GetServerInfo', function(data) {
ko.mapping.fromJS(data, {}, vm);
});

How to use Knockout.js in asp.net mvc Razor correctly and sufficiently?

i try to learn knockout.js on asp.net mvc razor. i have been coding below code to learn and test myself
But View side throws me a js error.
Error occurs on "var model = #Html.Raw(Json.Encode(Model));" Error : Microsoft JScript run-time error: 'fromJSON' Unable to get value of the property: the object is empty or undefined
Controllers:
[HttpGet]
public ActionResult GetGift()
{
GiftModel gift = new GiftModel();
gift.Price = 120;
gift.Title = "Test";
return View(gift);
}
View:
#using System.Web.Script.Serialization;
#model knockout1.Models.GiftModel
#{
ViewBag.Title = "GetGift";
}
<h2>GetGift</h2>
<script src="/Scripts/knockout-2.1.0.js" type="text/javascript"></script>
<script type="text/javascript">
var initialData = #Html.Raw( new JavaScriptSerializer().Serialize(Model));
var viewModel = ko.mapping.fromJSON(initialData);
$(document).ready(function () { ko.applyBindings(viewModel); });
</script>
<p>Title: <strong data-bind="text: Title"></strong></p>
<p>Price: <strong data-bind="text: Price"></strong></p>
But i changed my js codes. Error disappears. i can not understand first usage why doesn't correct? i readed Darin Dimitrov's response:
Darin Dimitrov :
<script type="text/javascript">
var jsonResultData = #Html.Raw(Json.Encode(Model));
</script>
Me: (it is working good.)
<script type="text/javascript">
$(function()
{
var model = #Html.Raw(Json.Encode(Model));
// Activates knockout.js
ko.applyBindings(model);
});
</script>
Based on the error message
'fromJSON' Unable to get value of the property: the object is empty or undefined
and page setup your problem is that you are trying to use the KO mapping plugin without including the plugin.
All the methods which are string with ko.mapping are part of the mapping plugin and for using them you need to refeence the knockout.mapping.js file in your HTML page.
Can you can download mapping plugin from github

How to access the entire model client side

I have the following code.
Index.cshtml:
#using System.Web.Script.Serialization
#model MvcApplication3.Models.Person
<script src="../../Scripts/knockout-2.1.0.js" type="text/javascript"></script>
<!-- This is a *view* - HTML markup that defines the appearance of your UI -->
<p>First name: <input data-bind="value: firstName" /></p>
#*<p>Last name: <input data-bind="value: lastName" /></p>*#
<script type="text/javascript">
var initialData = #Html.Raw(
new JavaScriptSerializer().Serialize(Model.FirstName));
alert(initialData);
// The *viewmodel* - JavaScript that defines data and behavior of the UI
function AppViewModel() {
this.firstName = ko.observable(initialData);
// this.lastName = ko.observable("Bertington");
}
// Activates knockout.js
ko.applyBindings(new AppViewModel());
</script>
HomeController:
public class HomeController : Controller
{
public ActionResult Index()
{
var people = new PeopleEntities();
var person = people.People.First();
return View(person);
}
}
Basically what this does is loads a person from the database and using knockout makes an editable field for the first name and loads the FirstName into it.
This works fine, however, I also want to load the LastName. I'm unsure how to do this because I'm serializing just the first name. I'm thinking I want to serialize the whole model but am not sure how I would then get access to each name.
The JavaScriptSerializer converts an object to a JSON string. If you serialize the entire model you can then access its fields the same way you do in C# using the dot notation more specifically initialData.FirstName and initialData.LastName.
This works because initialData contains a javascript object initialized from the JSON string obtained from the serializer. If the model contained only the first and last name properties this would be the string generated by the serializer:
{"FirstName":"John","LastName":"Doe"}
Here is how I do it:
var initialData = #(new MvcHtmlString(Model.ToJson()));
var viewModel = new AppViewModel(ko.toJS(initialData));
function AppViewModel(m) {
this.firstName = ko.observable(m.FirstName);
this.lastName = ko.observable(m.LastName);
}
$(function () {
ko.applyBindings(viewModel, document.body);
}
Where the .ToJson() method on the Model is an extension method:
public static class HtmlHelperExtensions
{
public static string ToJson(this object item)
{
return Newtonsoft.Json.JsonConvert.SerializeObject(item);
}
}
You can see I used the Newtonsoft.Json converter, but you could probably use the JavaScriptSerializer as well.

MVC Model Binding before KnockoutJS Model Binding

So if your Controller Action returns a Model with pre-populated values, how do you make KnockoutJS aware of them?
E.g.:
#Html.TextBoxFor(m => m.Title, new { data_bind="value: title"} )
however, on $(document).ready() where I bind knockout.js ViewModel, this value isn't yet populated:
$(document).ready({
var viewModel = {
title: ko.observable($("#Title").val()) // too early for this?!
}
ko.applyBindings(viewModel);​
});
How do you make KnockoutJS work with MVC's model binding?
One workaround I found was to set the JavaScript variable in my Razor View, like so:
<script>
var strTitle = '#Model.Title';
</script>
and than use it in the Knockout model binding. That works, but I hate it. What if your form has like hundreds of fields? You don't want as many JavaScript variables in your page.
Am I missing the obvious here?
This seems similar to this question. Normally you would set your view model by converting #Model to JSON in a script:
<script type="text/javascript">
var model = #(new HtmlString(new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(Model)));
</script>
You could also create your own binding handler that will initially load the view model based on control values. This new myvalue handler basically calls the existing value handler, except it updates the view model from the initial control value on init.
ko.bindingHandlers['myvalue'] = {
'init': function (element, valueAccessor, allBindingsAccessor) {
// call existing value init code
ko.bindingHandlers['value'].init(element, valueAccessor, allBindingsAccessor);
// valueUpdateHandler() code
var modelValue = valueAccessor();
var elementValue = ko.selectExtensions.readValue(element);
modelValue(elementValue); // simplified next line, writeValueToProperty isn't exported
//ko.jsonExpressionRewriting.writeValueToProperty(modelValue, allBindingsAccessor, 'value', elementValue, /* checkIfDifferent: */ true);
},
'update': function (element, valueAccessor) {
// call existing value update code
ko.bindingHandlers['value'].update(element, valueAccessor);
}
};
Then when you call ko.applyBindings, your observable will be set based on the control's value initially:
<input type="text" data-bind="myvalue: Title" value="This Title will be used" />
<input type="text" data-bind="value: Title" value="This will not be used" />
<!-- MVC -->
#Html.TextBoxFor(m => m.Title, new { data_bind="myvalue: Title"} )
SAMPLE FIDDLE
What about simply serializing your entire page model to json using JSON.NET or similar. Then your page will be populated via normal razor view bindings for non-js users. Then your page scripts can be something like:
<script>
ko.applyBindings(#Html.ToJSON(Model));
</script>
Or if you have a typed viewModel
<script>
var viewModel = new MyViewModel(#Html.ToJSON(Model));
ko.applyBindings(viewModel);
</script>
It makes sense to structure your client side and actual view models the same so no manipulation of the json shape is required.
EDIT
Example of the toJSON helper.
public static MvcHtmlString ToJson(this HtmlHelper html, object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return MvcHtmlString.Create(serializer.Serialize(obj));
}
Hope this helps.
Because I don't have a 50 point reputation to add a comment to the Jason Goemaat answer, I decided to add my comment here as an answer.
All the credits go to Jason Goemaat.
I wasn't able to make the code work for me. So I had to make a small change.
ko.bindingHandlers['myvalue'] = {
'init': function (element, valueAccessor, allBindingsAccessor) {
//get initial state of the element
var modelValue = valueAccessor();
var elementValue = ko.selectExtensions.readValue(element);
// call existing value init code
ko.bindingHandlers['value'].init(element, valueAccessor, allBindingsAccessor);
//Save initial state
modelValue(elementValue);
},
'update': function (element, valueAccessor) {
// call existing value update code
ko.bindingHandlers['value'].update(element, valueAccessor);
}
};
if I had this line at the top,
// call existing value init code
ko.bindingHandlers['value'].init(element, valueAccessor, allBindingsAccessor);
It was deleting the original state of the element. So whatever value I had in the input field, it was being deleted.
Inside the model I have this:
//publishing comment
self.publishingComment = ko.observable();
And my MVC looks like this
#Html.TextBoxFor(model => model.Comment, new { data_bind = "myvalue: publishingComment" })

Resources