locked
Complex View Model with Nested Item List / ModelState RRS feed

  • Question

  • User-121299044 posted

    I'm sorry to bother you, I just have a question regarding how to validate a list of object when is inside other list...  I do have a list of cities, each city has another list of zones, an example could be like this:

    public class City
    {
            public int Id { get; set; }        
            public string CityName { get; set; }
            public virtual List<Zone> Zones { get; set; } = new List<Zone>();
    }
    
    public class Zone
     {
            public int Id { get; set; }
            public string ZoneName { get; set; }
    
            public int CityId { get; set; }
            public virtual City City { get; set; }
    }

    Now, as you can see, the City is the principal and the Zone is the dependent class.  Now I decided to pass what I have into my database to a view with a form to make changes to these Cities and their Zones, so I created a view model like this:

    public class SettingsModel
        {
            public City NewCity { get; set; }
            public List<City> CurrentCities { get; set; }
        }

    The first property is to create a new City that is not in the db.  The other list contains the Cities already in the database.  So I started by creating a list of forms using a for loop (not foreach) that uses an index.  At the end, it produces something like this:

    <form method="post" action="/Administration/UpdateCity">
    ...
    <input type="hidden" id="CurrentCities_0__Id" name="CurrentCities[0].Id" value="7">
    <input type="text"  id="CurrentCities_0__CityName" name="CurrentCities[0].CityName" value="Austin">
    ...
    </form>
    ...
    <form method="post" action="/Administration/UpdateCity">
    ...
    <input type="hidden" id="CurrentCities_1__Id" name="CurrentCities[1].Id" value="4">
    <input type="text"  id="CurrentCities_1__CityName" name="CurrentCities[1].CityName" value="Dallas">
    ...
    </form>

    and as far as I understand, in the receiving action method (POST), I should use the [Bind(Prefix="CurrentCities")] to remove the unnecessary class name of the field name and, should specify a list<City> (or IEnumerable or Array) to tell that the object received is an array.  in other words:

    public IActionResult UpdateCity([Bind(Prefix = "CurrentCities")]List<City> myCity) 
    {
    ...
    }

    My question is: When I try to add a form in the same way for the Zones property of City, I end with the form fields named as:

    <input type="text" id="CurrentCities_0__Zones_0__ZoneName" name="CurrentCities[0].Zones[0].ZoneName" value="Austin Metro Area">
    ...
    <input type="text" id="CurrentCities_1__Zones_0__ZoneName" name="CurrentCities[1].Zones[0].ZoneName" value="San Antonio Metro Area">

    As you can see, now all the fields have an extra index indicator like this "CurrentCities[0].Zones[0]" and I was wondering, how can I get this object in the POST action method? how can I specify a Bind Prefix of this kind? and how could I specify that the item is a collection item of other collection item?? Please help and thank you for your patience!

    Saturday, September 19, 2020 1:30 AM

Answers

  • User-121299044 posted

    @Rena thank you for your answer... the question is not about getting an array of objects, instead is about having an array of objects, create a form to update each one, and send only one to validate, instead of sending the whole array of objects...  in other words, it works fine if we send the whole array of objects, but if we send only one object, it doesn't work.

    In the end, I understood MS is not prepared for that kind of complexity, instead of creating a view with multiple forms, I'm doing a page that received only one object.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, September 21, 2020 9:49 PM

All replies

  • User-474980206 posted

    You should use the post back binding names in the view in the first place which would be cleaner. Your case is beyond the default support and requires a custom binder.

    Saturday, September 19, 2020 3:11 PM
  • User585649674 posted

    Post action should accept "List<CurrentCity>" instead of "List<City>". Then in action method you can convert "currentcity" to "city"

    Sunday, September 20, 2020 2:51 PM
  • User711641945 posted

    Hi sipi.perez@gmail.com,

    If you must using `[Bind(Prefix = "")]` attribute,you need specify the name prefix one by one like below:

    [HttpPost]
    public IActionResult Index([Bind(Prefix = "CurrentCities[0].Zones")] List<Zone> zone1, [Bind(Prefix = "CurrentCities[1].Zones")] List<Zone> zone2, [Bind(Prefix = "CurrentCities[2].Zones")] List<Zone> zone3)
    {
        //...
    }

    Result:

    Another way is that you could change the name of the input like below:

    @model SettingsModel
    <form method="post" asp-action="Index">
        @{ var count = 0;}
        @for (int i = 0; i < Model.CurrentCities.Count(); i++)
        {
            <input asp-for="CurrentCities[i].CityName" />
            @for (int j = 0; j < Model.CurrentCities[i].Zones.Count; j++)
            {
                <input asp-for="CurrentCities[i].Zones[j].ZoneName" name="[@count].ZoneName"/>
                count++;
            }
        }
        <input type="submit" value="submit" />
    </form>

    Controller:

    [HttpPost]
    public IActionResult Index(List<Zone> zone)
    {
        //...
    }

    Result:

    Best Regards,

    Rena

    Monday, September 21, 2020 9:16 AM
  • User-121299044 posted

    @Rena thank you for your answer... the question is not about getting an array of objects, instead is about having an array of objects, create a form to update each one, and send only one to validate, instead of sending the whole array of objects...  in other words, it works fine if we send the whole array of objects, but if we send only one object, it doesn't work.

    In the end, I understood MS is not prepared for that kind of complexity, instead of creating a view with multiple forms, I'm doing a page that received only one object.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, September 21, 2020 9:49 PM