2

Being fairly new to web dev and MVC4, I am encountering the same design issue repeatedly and was hoping someone could tell me what the right/supported/etc.. solution was in the MVC4 world:

Basically, I've drunk the view model koolaid and have view models for every view in my project, most of which are partial subviews dynamically updating on something of a single page application. All proceeds well generating/rendering the view, and then a user updates a number of values on the client side and it's time to update the server.

As an example, let's say it's a simple container view model:

public class Data {
  public List<Prop> Props { get; set; }
}

public class Prop {
  public string Id { get; set; }
  public int Value { get; set; }
}

So let's just say the user is adding new props to the container. How do I get the modified object back to the server?

So far: If it's important that the server be in sync realtime, I can make a call with each addition/update on the server and then either keep things in sync on the client or simply have the server return the updated view. For simple scenarios like that, all is well.

But I find myself often in the case where I want the client to be able to manipulate the object (through the view/js/etc) and I don't really need to update on the server until they are done and submit. What I really want is to be able to pass the object down with the rendered view, interact with it via Javascript, and then pass the object back to the controller when all is done. How do I that? (Apologies it took a while to get to the point!)

Alternatives I've seen:

-- Quick & Dirty (Encode viewmodel properties to JavaScript in Razor): Which sure will put the object in javascript on the client, though it seems hack-ish to just be serializing the whole object into the client side html without any validation, etc. (Though I do realize eventually that's how the object is making it's way down, but it seems like you're bypassing the whole MVC4 object handling/parsing.)

-- Upshot.Js seemed promising with MS support at one time, but it seems that has died: Current status of Upshot.js

-- Breeze.js (http://www.breezejs.com/) seems to be an attempt to take over there, though my concern there is that it's fairly new and not much broad adoption yet.

Ultimately the thing that makes me feel like I'm missing what must be a somewhat obvious alternative is that all the pieces are clearly built into MVC4 already. When you use a form view for example, your fields are databound to the controls, and when the form is submitted, a parallel JSON object is created, sent to the controller, and then parsed into your POCO ViewModel object for you. That's basically the roundtrip I'm looking for (though retaining the full JSON object clientside). What's the "proper" way to handle that?

Community
  • 1
  • 1
Gene
  • 1,587
  • 4
  • 18
  • 38
  • Have you looked at [Knockout.js](http://learn.knockoutjs.com/)? It's integrated right into MVC4. – Evan Davis Oct 14 '12 at 17:26
  • I have. But it seemed to me that one needs to duplicate their model is a knockout model on the client side. Or do I misunderstand? Ultimately I think you're right that a client side framework will be the solution, but I haven't found one that "leverages" the server side viewmodel. Even in this simple example, you would still create the model client side/apply bindings/etc.. correct? (But you could then send that model back to the server and if you've named everything the same, the mapping back to the server object should be clear - so that's definitely close.) – Gene Oct 14 '12 at 18:01
  • Actually, I think you're right, I'm gonna need to explore this some more. For example: http://stackoverflow.com/questions/10866285/how-can-i-generate-client-side-view-models-for-knockout-in-an-asp-net-mvc-projec (@Mathletics: If you want to post your comment as an answer I'll accept and will come back with what I find as a comment.) – Gene Oct 14 '12 at 18:19

2 Answers2

2

Knockout is actually exactly what I'm looking for (or another client side MV* framework), I simply didn't know how to use it.

Credit to @Mathletics and a couple of other SO solutions for point me in the right direction, especially: How to use knockout.js with ASP.NET MVC ViewModels?

The key is really to use the ko.mapping library, and Json encode or serialize the model on the server side. In particular, this script block is working well for me in strongly typed partial view:

<script type="text/javascript">
function viewModel() {
    // Additional Knockout Here    
};

var jsonModel = '@Html.Raw(JsonConvert.SerializeObject(this.Model))';
var mvcModel = ko.mapping.fromJSON(jsonModel);

var myViewModel = new viewModel();
var g = ko.mapping.fromJS(myViewModel, mvcModel);

ko.applyBindings(g);
</script>

And then you're all set up to use Knockout as per normal:

<div class="row">    
    <div class="span3">
        <h3>Bound Value:</h3>
        <span data-bind="text: PropertyName"></span>
    </div>
</div>

(This requires knockout.js, knockout.mapping.js, and Newtonsoft.Json - though Json.Encode would work equally well.)

Passing the objects back is then easy. Update your object as normal, and when you pass it back to the controller, you already have you Json object ready to go and will be parsed as per normal to be passed back in as a POCO view model on the server side, completing the round trip.

Community
  • 1
  • 1
Gene
  • 1,587
  • 4
  • 18
  • 38
0

From your description I can understand that you need to pass collection of objects to the controller and then update the collection. Model binding to collection will make things easier for you.

When binding to collection of single fields it becomes easier however when you want to bind to collection of objects then it becomes a little tricky. There are 2 options sequential binding and non- sequential binding.

Sequential binding:

Here is controller action where the form will be posted (HTTP POST action)

public ActionResult UpdateProps(ICollection<Prop> Props) {
    return View(Props);
}

form method="post" action="/Home">    
    <input type="text" name="[0].Id" value="1001" />
    <input type="text" name="[0].Value" value="Prop1" />        

    <input type="text" name="[1].Id" value="" />
    <input type="text" name="[1].Value" value="NewProp" /> 

    <input type="submit" />
</form>

When the form will be posted the values will go something like

[0].Id=1001&[0].Value=Prop1&[1].Id=&[1].Value=NewProp

You can see the indexing [0] and [1]. The model binder will rightly group the objects based on the indexing and you will get the expected collection. After the post your action will get 2 objects in ICollection Props. One with ID 1001 and another one which is newly added.

Now you will notice that if you add new prop dynamically then you need to index it properly using script. Or if that is hard for you then instead of indexing the elements you can post them in the above format. You need to parse the elements and post the form using script. The data should be arranged with index. The parsing gives you another advantage that you can post only the required elements i.e newly added or modified instead of posting them all thus saving the payload.

In non Sequential binding instead of series of number sequence the index is unique value like below:

[PropId1001].Id=1001&[PropId1001].Value=Prop1&[PropIdTemp233].Id=""&[PropIdTemp233].Value=NewProp

Abhijit-K
  • 3,569
  • 1
  • 23
  • 31
  • I appreciate the response Abhijit. In a sense though, manually laying out the fields like that, just so they can be constructed back into the object I started with, is what I'm trying to avoid. I'd prefer to keep client side javascript objects updated as the user acts, and send those back, taking advantage of the framework mapping there. (Just as if you create an object yourself and send it over in JSON with the appropriately named fields.) – Gene Oct 14 '12 at 16:25
  • I did not follow when you say "manually laying out the fields like that". which fields you mean over here? The forms fields with "name"? – Abhijit-K Oct 14 '12 at 16:32
  • Apologies. What I mean is that I don't want to treat it as a form while I "dehydrate" my view model into flat html fields/controls, and then "rehydrate" back to server side objects. Let me investigate some more and I'll update the question. I appreciate your response though Abhijit. – Gene Oct 14 '12 at 17:05
  • The flat HTML view is the final output that goes to browser not what is in the mvc view. On the server side inside view obviously one will use a foreach loop to parse the model generate the input fields. Then use a counter to index the name attribute. So that when the form is posted the list model is populated. – Abhijit-K Oct 14 '12 at 17:12
  • Understood Abhijit. I think we're talking past each other, that's my fault. Let's hold off for another answer and I'll update my question shortly with more info. Appreciate your help. – Gene Oct 14 '12 at 17:22