ASP.NET MVC with nested view model and Knockout
Asked Answered
I

2

6

Cannot get my brain around how to implement knockout for the following ASP.NET MVC 4 nested view model :

public class MyProfile
{
  public string Name { get; set; }

  public IList<VM1>   List1    { get; set; }
  public IList<VM2>   List2    { get; set; }
  ....
  public IList<VM10>  List10    { get; set; }
}
// example of VM view model
public class VM1
{
   IList<Label> Labels { get; set; }
   IList<Contact1> Contact1 { get; set; }
}

In the view I accept model like this:

@model MyProfile

@using (Html.BeginForm("Index", "Profile", FormMethod.Post, new { id = "profileEditorForm" }))
{

  @Html.ValidationSummary(false)
    <fieldset>
  <legend>User's data</legend>
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" class="required" data-bind="value: Name"/>
    </fieldset>

 @Html.EditorFor(m => @Model.List1, "List1") @* Editpr model for List1*@
 @Html.EditorFor(m => @Model.List2, "List2")
 .....
 @Html.EditorFor(m => @Model.List10, "List10")

 <p>
   <input type="submit" value="Save" data-bind="enable: (List1().length > 0) && (List2().length > 0) && ...(List10().length > 0)" />
<a href="/">Cancel</a>
 </p>
}

The EditorTemplate for List1 would look like this with multiple questions in:

@model IList<FiveW.ViewModels.List1>

<fieldset>

  <table>
    <tbody data-bind="foreach: Contact1">
      <tr>
        <td>
          <label>Email:</label></td>
        <td>
          @* How do you put combobox here with labels here?
          How do you tie selected label to selected property on your Contact1 object *@
          @*<select data-bind="options: Labels, optionsText: 'LabelName', value: selectedLabel, optionsCaption: 'Choose...'"></select></td>
        <td>
          <input type="text" data-bind="value: Name, uniqueName: true" class="required" /></td>
        <td>
          <a href="#" data-bind="click: function() { viewModel.removeContact1(this); }">Delete</a></td>
      </tr>
    </tbody>
  </table>


  <button data-bind="click: addContact1">Add Contact1</button>

</fieldset>

Edit

VM1 through VM10 are the same except the validation logic, so I have to make them different classes (unfortunately, since it TONS of repetition in models, and in the views).

Client side - this is what I am asking about: I need to pass from ASP MVC models containing nested lists and have them presented on the client with knockout (I found it to do best pertaining dynamic lists). It is something similar to gmail contacts - you have home/work/mobile/fax phone - so one list is label for the phone (what phone is it) and should be presented as combobox, another one is dynamic lists of phones that can increase as per user clicks.

End edit

  1. I don't understand how to create a knockout viewModel from this nested model, obviously Name should be part of it, but the rest are lists and they also contain lists.

  2. How to map it?

  3. how to handle it (one goes into dropdownlist that will be the label of the other list, which is variable in length - the only reason to use knockout here).?

  4. once filled out, how to put it all together and ship back to controller action?

  5. how to write that editor model when label is dropdownlist (or combobox) of labels for the name (example is: [label]home/work [name]email, [label]mobile/home/car [name]phone)

If it was simple class with IList inside - its like here. Problem is that there are lists inside lists, Knockout demands that everything is observable, not sure how to express in java script this nesting model.

Please help. Thanks in advance!

Impediment answered 4/3, 2013 at 23:47 Comment(8)
Hey, where is your client-side viewModel declaration? maybe we could start thereCondon
Is each List element a different type? I see you have declared VM1, will there also be VM2, VM3, VM...n as class declarations? are these different or can you create a general class list type and just have 10 instances?Condon
See edit. VM1 are equal except the data annotation (or validation rules in case of fluent validation). Client side is what I am asking about. Two lists - one is combobox (work/home) for the other (phone/email/address/etc). How to express it in Knockout... ThanksImpediment
If the classes are equal (Property names and types are consistent) then you only need one child ViewModel. I'll write up an example and try to answer the five pointsCondon
Thanks a lot, this clarifies things for me, my biggest concern however is still not resolved - how to put combobox inside the editor template that can be selected and posted back to server to say that "Contact1" is of type "Label2" for example - labels can be selected in ComboBox in editor template. THANKS!Impediment
You would need to find a way to pass the htmlattribute to the editor template so that it would have a binding similar to data-bind="List1.Labels[0].Property"Condon
Similiar question here: #13005531, and lengthy article here: dotnetspeak.com/index.php/2012/10/…Condon
Had to rephrase the question to single out the issue I had, rather than asking general question like here. Huge thanks for efforts to answer, unfortunately I find that if you don't formulate your questions properly you get respectively common (non specific) responses. So its my bad. Because of large number of people interested in the question I posed (there were 5 upvotes in first 3 hours), here is the answer: #15260307Impediment
I
0

Had to rephrase the question to single out the issue I had, rather than asking general question like here.

Huge thanks for efforts to answer, unfortunately I find that if you don't formulate your questions properly you get respectively common (non specific) responses. So its my bad.

Because of large number of people interested in the question I posed (there were 5 upvotes in first 3 hours), here is where I asked the same question with stress on specific issues I had and got the right answer and got my knockout to knockout again.

ASP.NET with Knockout variable length list with combobox - how to bind?

Hope this helps somebody as it helped me & thanks again!

Impediment answered 8/3, 2013 at 15:10 Comment(0)
C
0

I wouldn't use mapping. I would just declare the ViewModel client-side like this:

//I'm asuming the properties for Label and Contact, this is just for example purposes
function LabelViewModel(){
    var self = this;
    self.LabelName = ko.observable();
}
function Contact(){
    var self = this;
    self.Name = ko.observable();
    self.LastName = ko.observable();
}

//This is the definition for the List. I believe it shouldn't matter that the class names are different as long as the structure is the same
function ListViewModel(){
    var self = this;
    self.Labels = ko.observableArray();
    self.Contacts = ko.observableArray();
}

function MainViewModel(){
    var self = this;
    self.Name = ko.observable();
    self.List1 = ko.observableArray();
    //....
    self.List10 = ko.observableArray();
}


$(document).ready(function(){
    var viewModel = new MainViewModel(@Html.Raw(JsonConvert.SerializeObject(Model)));
    ko.applyBindings(viewModel);
});

Then, on submit I would try to submit from jquery instead of doing an http post directily:

var viewModelJs = ko.toJS(ko.utils.unwrapObservable(viewModel));
var parameters = JSON.stringify({vm: viewModelJs});

$.ajax('http://yourControllerUrlHere', {
    data: parameters,
    type: "POST",
    contentType: "application/json",
    dataType: "json",
    success: function (result) {
        console.log('Submitted ok');
    },
    error: function (error) {
        console.log(error);
    }
});
Condon answered 5/3, 2013 at 18:30 Comment(3)
in addition to main model, I was looking mainly to EditorTemplates I use: List1 etc through List10. What they do is in the additional Edit - basically show dropdown (preferably combobox) with labels as a selection option and the main input text as Contact#. As example I used gmail contact info: Contact1 is Email, its label can be selected among "work", "home" or "custom"... - how to express combobox as well as its content ([email protected] is labeled "home" email) - hope you understand. Assume all properties as strings. See additional edit - I added EditorTemplate. THANKS MAN!Impediment
You;re saying 'value: Name, uniqueName: true' in your databind. You need to pass the whole path, that's why I told you to look for a way to pass the HtmlAttributes object from the main View. Instead of value:Name it should be something like 'List1()[0].PropertyYouNeedToAccess'Condon
Here is a similar answer that tackles the nested binding problem: #7466923Condon
I
0

Had to rephrase the question to single out the issue I had, rather than asking general question like here.

Huge thanks for efforts to answer, unfortunately I find that if you don't formulate your questions properly you get respectively common (non specific) responses. So its my bad.

Because of large number of people interested in the question I posed (there were 5 upvotes in first 3 hours), here is where I asked the same question with stress on specific issues I had and got the right answer and got my knockout to knockout again.

ASP.NET with Knockout variable length list with combobox - how to bind?

Hope this helps somebody as it helped me & thanks again!

Impediment answered 8/3, 2013 at 15:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.