I'm introducing KnockoutJS into an existing app. My plan is to modify/utilize the existing partial views that we've already created and bind them to the JS view models with Knockout's declarative attributes. When I make an AJAX call to an action, ideally I'd like the action to return both the HTML of the partial view and JSON object. Then I can fill a div with the HTML, convert the JSON to a Knockout object and bind it to the HTML. But I can't figure out how to return both from the action.
I need the full view model because I'll be updating it and eventually sending it back to the server.
I thought about having the action return the partial view (already bound to the model), and within the partial view, include javascript to convert the .Net model into a Knockout object. But I feel that scattering the JS around like that is messy and unmaintainable. I'd rather have everything close to the original ajax call.
I guess another alternative is to make two action calls. One for the JSON, and another for the partial view. But there has to be a slicker way.
Any ideas on how best to do this?
You could create a hidden
<input>
on the partial with a value set to the JSON string of the ViewModel. Then before you render the partial view, grab the JSON value from that field, and parse it. Then remove it from the partial view, insert it into your page, and doko.applyBindingsToDescendants(viewModel, $("#parentElement")[0])
I'm not entirely sure how I feel about this approach, and it's just a theory. I haven't tested this out but I suspect it would work. One booty trap you'd have to look out for is the browser trying to cache your GET request. In your ajax request you'd want to do:
Or just do a
$.post
request. (reference)So that's one option.
I'm sure there are a variety of ways to do this. I manually render the view from the controller, and then pass the rendered view back as part of my JSON response.
This preserves the responsibilities of each entity. Views are still located using the view engine and they can be reused. The controller knows little or nothing about the view beyond its name and model type.
Manual Rendering
In your action method:
Note that I'm returning an anonymous type. You can return any (serializable) type you want, as long as it has a string property for the rendered view.
Testing
Testing an action that uses manual rendering requires a slight modification. This is due to rendering the view a bit earlier than it would be rendered in the MVC pipeline.
Manual Rendering
Automatic Rendering
In other words, our manual rendering process kicks off a variety of other operations that make it difficult to test (such as interacting with the build manager to compile the view).
Assuming you wish to test the action method and not the actual contents of the view, you can check whether or not the code is executing in a hosted environment.
Checking
HostingEnvironment.IsHosted
is inexpensive (under the hood, it is simply a null check).