Best Practice for Saving in ASP.NET w/KnockOut

875 views
Skip to first unread message

Nate

unread,
Jun 13, 2011, 4:37:32 PM6/13/11
to KnockoutJS
I would like some clarification on the best way to save a page's data.
All examples have a page that only contain components that are bound
as observables to a knockout viewModel. In my world, I have a page
that functions as a wizard. This page has fields populated from the
controller using strongly typed helpers. These helpers render fields
that are not being observed by Knockout. I only have a listbox being
observed which loads data into a table. This is the only data I need
to have as observables. The rest of the page can be submitted back to
the server as a normal MVC result back to the action method. What is
the best way to send this observed data back to the server and also
send the other non-observed data back to the server at the same time?
I suspect that I am approaching this wrong and I may simply lack
understanding of the best way to do this.
What I did was create a jquery function that loads the json data to a
hidden field and then grab the data for processing on the server side.
The non-observed data simply gets posted back to the server as would
be done in any ASP.NET MVC web page. I suspect that this is incorrect
and that there is a better way. Any ideas? Thanks

Stacey Thornton

unread,
Jun 13, 2011, 4:43:29 PM6/13/11
to KnockoutJS
This is kind of the great question in the world of everyone using
ASP.NET MVC and Knockout together right now.

ASP.NET MVC does not natively accept javascript objects (I don't know
enough about JS to say I am willing to put my reputation on that, but
you get what I mean), but it does take HTTP forms (View Models) and
JSON. So you have two options (that I am aware of).

1. The most frequently taken path is to send your data back to the
server as JSON and either use a javascript framework like jQuery to
ajax load in partial views when it is updated, or to post as JSON and
return full views back that perform re-deserialization of the data.
(both methods are very difficult to wrap your head around, but they
work well once you get the hang of it)

2. Similar to method 1, you can use javascript to pass JSON models to
Action Methods that return either JsonResult or you can interpret the
returned data yourself and update your view accordingly.

If you try to update data with Knockout, and then post the MVC View
Model to the page, you won't usually get the results you are
expecting. It would be nice if there was a better way to do this, but
one step at a time. So you are not taking an incorrect approach from
what I have seen. I've not seen any examples of anyone using Knockout
and MVC together in any way different than JSON server postback, but I
have heard rumors of it.

rpn

unread,
Jun 14, 2011, 11:36:24 AM6/14/11
to knock...@googlegroups.com
For this scenario where you mostly want to use ASP.NET MVC in the "normal" way, but manage a little bit of it with Knockout, I had one alternate idea.

Suppose you have a model like:

	public class Widget
	{
		public int WidgetId { getset; }
		
		public string Name { getset; }
 
		public IEnumerable<WidgetChild> Children { getset; }
	}
	public class WidgetChild
	{
		public int WidgetChildId { getset; }
 
		public int WidgetId { getset; }
 
		public string Name { getset; }
	}

Now, you might want to do all of the normal CRUD operations on your Widget (possibly just using scaffolding).  However, you want to manage the Widget's children with Knockout (observableArray of children with add/remove methods).  In this case, maybe the Widget has a bunch of other properties and you don't want to bring them into Knockout and you don't want to use ko.utils.postJson (have to pass in all of the "normal" form fields to include as well).

One option is to define a class for a JsonModelBinder and then indicate that the specific types that are managed by Knockout are handled by it.

So, the JsonModelBinder might look like (just based on Steve's [FromJson] attribute in this post):
	public class JsonModelBinder : IModelBinder
	{
		private readonly static JavaScriptSerializer serializer = new JavaScriptSerializer();
 
		public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
		{
			var stringified = controllerContext.HttpContext.Request[bindingContext.ModelName];
			if (string.IsNullOrEmpty(stringified))
			{
				return null;
			}
 
			return serializer.Deserialize(stringified, bindingContext.ModelType);
		}
	}
Then, in your Global.asax.cs, indicate that certain types are to be handled by this model binder:
ModelBinders.Binders.Add(typeof(IEnumerable<WidgetChild>), new JsonModelBinder());

Now, on the client-side, do all of your normal Knockout work, but also place a hidden field like:
<input name="Children" id="Children" type="hidden" data-bind="value: ko.toJSON(Children)" />
If your children's properties are not observable, then you would need to handle the submit and update this field.  Otherwise, you would be good to go with the normal post, as this input would reflect the current state of your Children.
Now, your controller action will take in a Widget and will have its Children populated properly:
        [HttpPost]
        public ActionResult Create(Widget widget)
        {
            //add Widget to database
        }
Several options on this stuff, but I was thinking that this would be a fairly clean way to handle cases where Knockout is managing just a portion of your model.


 







Stacey Thornton

unread,
Jun 14, 2011, 11:46:04 AM6/14/11
to KnockoutJS
What would be really neat is if we had a .dll backing for Knockout
that had some .NET objects in it that could work in tandem with it.
Like an IObservable<T> and IObservableArray<T> interfaces that wrap
around other objects. . .

On Jun 14, 10:36 am, rpn <rnieme...@gmail.com> wrote:
> For this scenario where you mostly want to use ASP.NET MVC in the "normal"
> way, but manage a little bit of it with Knockout, I had one alternate idea.
>
> Suppose you have a model like:
>
>         public class Widget
>         {
>                 public int WidgetId { get; set; }
>
>                 public string Name { get; set; }
>
>                 public IEnumerable<WidgetChild> Children { get; set; }
>         }
>
>         public class WidgetChild
>         {
>                 public int WidgetChildId { get; set; }
>
>                 public int WidgetId { get; set; }
>
>                 public string Name { get; set; }
>         }
>
> Now, you might want to do all of the normal CRUD operations on your Widget
> (possibly just using scaffolding).  However, you want to manage the Widget's
> children with Knockout (observableArray of children with add/remove
> methods).  In this case, maybe the Widget has a bunch of other properties
> and you don't want to bring them into Knockout and you don't want to use
> ko.utils.postJson (have to pass in all of the "normal" form fields to
> include as well).
>
> One option is to define a class for a JsonModelBinder and then indicate that
> the specific types that are managed by Knockout are handled by it.
>
> So, the JsonModelBinder might look like (just based on Steve's [FromJson]
> attribute in this post<http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-...>
> ):

Stacey Thornton

unread,
Jun 14, 2011, 12:15:18 PM6/14/11
to KnockoutJS
Is it possible to change the Model Binder so that if it does not find
JSON, it will try default model binding? I tried this out and I am
running into the problem where sometimes I want it to have that Model
Binder, and sometimes I don't. For the top-level object I can use the
[FromJson] attribute, but I can't do that with the child collections.

On Jun 14, 10:46 am, Stacey Thornton <stacey.cielia.l...@gmail.com>
wrote:

rpn

unread,
Jun 14, 2011, 2:40:37 PM6/14/11
to knock...@googlegroups.com
I'll have to look into that when I get a chance.  I believe that I have seen this done before, but usually it depends on the content-type of the post, which doesn't help us in this case.  I would think that it might be possible though.

Otherwise, I could see doing something like:

        [HttpPost]
        public ActionResult Create(Widget widget, [FromJson] IEnumerable<WidgetChild> children)
        {
            //save to database
        }
Then, you could add manually add the children to the widget.

rpn

unread,
Jun 14, 2011, 2:44:04 PM6/14/11
to knock...@googlegroups.com
You would need a FromJson attribute in that case that could use the JsonModelBinder class.

	public class FromJsonAttribute : CustomModelBinderAttribute
	{
		
private readonly static JavaScriptSerializer serializer = new JavaScriptSerializer
();
 
		public override IModelBinder GetBinder()
		{
			return new JsonModelBinder();
		}
	}

Kalman Speier

unread,
Jun 16, 2011, 7:17:55 AM6/16/11
to knock...@googlegroups.com
FYI, the JsonValueProviderFactory is now registered by default in ASP.NET MVC 3. So I think you wont need the JsonModelBinder if you are using MVC 3...
Message has been deleted

Stacey Thornton

unread,
Jun 16, 2011, 8:23:21 AM6/16/11
to KnockoutJS
The JsonValueProviderFactory in MVC 3.0 is good, yes - but it only
works if you submit a JSON post where the content type is
specifically
set as JSON. The Model Binder we are talking about is used if you
actually do an HttpPost back to the Controller, in which case the
JSON
is actually packaged into an <input> field, and the
JsonValueProviderFactory doesn't parse that. It tries to read it as
an
object, and it fails.

On Jun 16, 6:17 am, Kalman Speier <kalman.spe...@gmail.com> wrote:
> FYI, the JsonValueProviderFactory is now registered by default in
> ASP.NETMVC 3. So I think you
> wont need the JsonModelBinder if you are using MVC 3...
>

Kalman Speier

unread,
Jun 16, 2011, 8:42:43 AM6/16/11
to knock...@googlegroups.com
Ok fair enough and what is the reason of use an input field?

Stacey Thornton

unread,
Jun 16, 2011, 8:50:06 AM6/16/11
to KnockoutJS
I do not understand the nature of your question, but I'll attempt an
answer.

RPN's use of the input field is a theory, more or less. Since the
model binder will bind to input fields, it stands to logically reason
that if you bind an invisible input field to a collection that gets
JSON serialized, and the model binder can deserialize the json result,
then it will work on normal model binding postback without using the
$.ajax or ko.utils.postJson functions.

As for why you would want to post instead of ajax, there are a lot of
reasons.

1. Very often you want to redirect someone after posting. This is
obnoxious to do in ajax when you can do it so much more easily with
"return RedirectToAction". It becomes more valuable to do it in the
controller if you have to redirect based on the parameters passed.
2. Sometimes you do not get results instantly. It is not unthinkable
to imagine that an object submitted by a Knockout viewModel will go
into a queue where it will be pushed into a database later. Imagine if
you are a massive site and handle millions of requests, demanding the
database for every one of them would be stupid. You would create a
queue and flush them to the database when it was convenient and cost
effective.
3. ajax posting can interfere with certain security protocols like the
[ValidateAntiforgeryToken] filter and HttpModules that monitor
postback to prevent redundant postback (I myself had to write just
such a module to stop a user from hammering the submit button over and
over and slipping past SERVER side validation. (Yes, you can slip past
server side validation if you smash the submit button fast enough))

There are others, but these are the three I could think of off the top
of my head.

On Jun 16, 7:42 am, Kalman Speier <kalman.spe...@gmail.com> wrote:
> Ok fair enough and what is the reason of use an input field?
>

rpn

unread,
Jun 16, 2011, 9:47:45 AM6/16/11
to knock...@googlegroups.com
This was just some ideas for making Knockout play nicely with the "normal" ASP.NET MVC flow  where you submit a form and the controller decides the next View that the user sees.  In a CRUD situation this could be the same view with errors shown from server-side validation or an entirely new view (back to the "home" page).

The ideas above would be for a case where Knockout is only managing a small portion of your editing (potentially a list of items) and you don't need to or want to pull everything into your KO view model at this point.
Reply all
Reply to author
Forward
0 new messages