5

I have a List of objects in my Model, how can I make a form for it? I want to have select box and number input box to add objects and be able to keep adding more before posting form.

If it was just public Cargo cargo I would just make a select box to choose cargo type and input box for amount and that's it. But it's a list so I want to add as much cargo as I want and then post a form. I already have input fields for address (like city, street etc.) in my form but I'm stuck with this list.

Order model (Form model):

public class Order
{
    public int Id { get; set; }
    public Address PickUpAddress { get; set; }
    public Address DropOffAddress { get; set; }
    [...]
    public List<Cargo> Cargo { get; set; }
}

Cargo model:

public class Cargo
{
    public int Id { get; set; }
    public int Amount { get; set; }
    public CargoType CargoType { get; set; }
}
1
  • 1
    This is where I find 3rd party tools like kendo ui invaluable. You can roll your own with some jquery like here and here. Commented Nov 20, 2019 at 18:11

2 Answers 2

1

My solution

  1. I implemented this function manually without any JS code.
  2. The code is very simple. You can refer to my code here directly.

Solving process

  1. We have to insert the order table before we can insert the cargo table. Otherwise, we can't connect the two tables.
  2. We need these three form models.We use the cargocount field to link the two order pages with the cargo page.
    public class CargoViewModel
    {
        public int OrderId { get; set; }
        public int Amount { get; set; }
        public string CargoType { get; set; }

        //Other use
        public int CargoCount { get; set; }
        public List<CargoViewModel> Cargos { get; set; }
    }
    public class OrderViewModel
    {
        public int OrderId { get; set; }
        public string PickUpAddress { get; set; }
        public string DropOffAddress { get; set; }
        public int CargoCount { get; set; }
        public List<CargoViewModel> Cargos { get; set; }
    }
    public class OrdersViewModel
    {
        public List<OrderViewModel> Orders { get; set; } = new List<OrderViewModel>();
    }
  1. When we create an order page, we need to provide the data of cargocount. When we submit the order page, we will save the existing data to the order table, jump to the cargo page, and generate cargocount input tags.
  2. Next, submit the list form. Submit page code
        <form asp-controller="Order" asp-action="CreateCargo" method="post">
                @if (Model.CargoCount != 0)
                {
                    for (int itemCount = 0; itemCount < Model.CargoCount; itemCount++)
                    {
                        <div class="row">
                            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                            <div class="form-group">
                                <div class="form-group" style="width:300px; height:auto; float:left; display:inline">
                                    <label asp-for="@Model.Cargos[itemCount].Amount" class="control-label"></label>
                                    <input asp-for="@Model.Cargos[itemCount].Amount" class="form-control" />
                                    <span asp-validation-for="@Model.Cargos[itemCount].Amount" class="text-danger"></span>
                                </div>
                                <div class="form-group" style="width:300px; height:auto; float:left; display:inline">
                                    <label asp-for="@Model.Cargos[itemCount].CargoType" class="control-label"></label>
                                    <input asp-for="@Model.Cargos[itemCount].CargoType" class="form-control" />
                                    <span asp-validation-for="@Model.Cargos[itemCount].CargoType" class="text-danger"></span>
                                </div>
                            </div>
                        </div>
                    }
                }
                <div class="form-group">
                    <input type="submit" value="Create" class="btn btn-primary" />
                </div>
            </form>

Background data processing code

        [HttpPost]  
        public async Task<IActionResult> CreateCargo(CargoViewModel  model)
        {
            var orderId = await _context.Order.Select(o => o.OrderId).MaxAsync();
            var cargos = model.Cargos;
            foreach (var item in cargos)
            {
                var cargo = new Cargo
                {
                    OrderId = orderId,
                    Amount = item.Amount,
                    CargoType = item.CargoType
                };
                await _context.AddAsync(cargo);
                await _context.SaveChangesAsync();
            }
            return RedirectToAction(nameof(Index));
        }

Using JS implementation

  1. We need this form models.
    public class OrderAndCargoViewModel
    {
        public int OrderId { get; set; }
        public string PickUpAddress { get; set; }
        public string DropOffAddress { get; set; }
        public List<CargoViewModel> Cargos { get; set; }
    }
  1. Next, submit the table form. Submit page code.
        <div style="float:right;">
            <table id="tb">
                <tr>
                    <th> <label  class="control-label">ID</label></th>
                    <th>  <label asp-for="@Model.Cargos.FirstOrDefault().Amount" class="control-label"></label> </th>
                    <th><label asp-for="@Model.Cargos.FirstOrDefault().CargoType" class="control-label"></label></th>
                </tr>
                @{
                    var countId = 0;
                    for (var itemCount = 0; itemCount < 3; itemCount++)
                    {
                        <tr id="trs">
                            <td>@(++countId)</td>
                            <td><input asp-for='@Model.Cargos[itemCount].Amount' class= 'form-control' /></td>
                            <td><input asp-for='@Model.Cargos[itemCount].CargoType' class='form-control' /></td>
                        </tr>
                    }
                }
            </table>
        </div>
        <input id="btnAdd" value="Add" type="button" class="btn btn-primary" onclick="btnAddClick()">

JS Code.

@section scripts{
    <script src="~/js/jquery-3.4.1/jquery-3.4.1.js" type="text/javascript"></script>
    <script src="~/js/jquery-3.4.1/jquery-ui-1.12.1.js" type="text/javascript"></script>
    <script src="~/js/jquery-3.4.1/jquery.unobtrusive-ajax.js" type="text/javascript"></script>
    <script>
        var btnAddClick = function () {
            var trLen = $("#tb tr[id='trs']").length;
            var $lastTr = $("#tb tr[id='trs']").last();
            var tr = "<tr id='trs'>";
            tr += "<td>" + (trLen + 1) + "</td>";
            tr += "<td><input class='form-control' type='number' data-val='true' data-val-required='The Amount field is required.' id='Cargos_"+trLen+"__Amount' name='Cargos["+trLen+"].Amount' value=''></td>";
            tr += "<td><input class='form-control' type='text' id='Cargos_"+trLen+"__CargoType' name='Cargos["+trLen+"].CargoType' value=''>";
            tr += "</tr>";
            $(tr).insertAfter($lastTr);
        }
    </script>
}

Controller Code.

        [HttpPost]
        public async Task<IActionResult> CreateOrderAndCargo(OrderAndCargoViewModel model)
        {
            var order = new Order()
            {
                PickUpAddress = model.PickUpAddress,
                DropOffAddress = model.DropOffAddress
            };
            await _context.AddAsync(order);
            await _context.SaveChangesAsync();

            var orderId = await _context.Order.Select(o => o.OrderId).MaxAsync();
            var cargos = model.Cargos;
            foreach (var item in cargos)
            {
                var cargo = new Cargo
                {
                    OrderId = orderId,
                    Amount = item.Amount,
                    CargoType = item.CargoType
                };
                await _context.AddAsync(cargo);
                await _context.SaveChangesAsync();
            }

            return RedirectToAction(nameof(Index));
        }
  1. Click here to view source codes.

Reference page

  1. About'@'.
  2. JS operation.
Sign up to request clarification or add additional context in comments.

2 Comments

Well i had that idea of making user specify how many cargo is he going to add beforehand but i think this is kinda crude. I would like to have input fields for just one element at the time and then show another field if user pressed "add another one" or something like that.
This must be implemented through JS. Give me some time. I can implement this operation.@kkamil4sz
0

It will solve the problem if you use more than one related model in View pages and give a list parameter in actions.. An example; View;

<input type="text" name="name" id="name1" />
<input type="text" name="name" id="name2" />

Action public actionresult post(string [] name)

1 Comment

But i dont know how many <input/> i need because it depends how many user wants to add.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.