locked
Adding and Editing Dynamic List using Partial View RRS feed

  • Question

  • User-1741097413 posted

    So I've this scenario, An objectA contains a list of objectB. And each item of the objectB has an object (file) that is optional.

    I've the following Model for ObjectA and View Model for the create and edit.

    public class ObjectA
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public IList<ObjectB> objectB { get; set; }
        }
    
    public class CreateEditObjectAViewModel
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public IList<ObjectBViewModel> ObjectBList { get; set; }
        }

    The View Model of the list is the following.

    public class ObjectB
        {
            public int Id { get; set; }
            public string Text { get; set; }
            public virtual Image Image { get; set; } 
        }
    
    public class ObjectBViewModel
        {
            public int Id { get; set; }
            public string Text { get; set; }
            public IFormFile ImageFile { get; set; } //for an image object
        }

    Create view: 

    @model TestApplication.Models.ViewModels.CreateEditObjectAViewModel
    
    <form asp-action="Create" method="post" asp-controller="Test" enctype="multipart/form-data">
        <div class="form-group row">
            <div class="col-2">
                <label asp-for="Name" class="col-form-label"></label>
            </div>
            <div class="col-5">
                <input asp-for="Name" class="form-control" />
            </div>
            <span asp-validation-for="Name" class="text-danger"></span>
        </div>
        <div id="editorRows">
            @foreach (var item in Model.ObjectBList)
            {
                <partial name="_ObjectBEditor" model="item" /> 
            }
        </div>
        <a id="addItem" asp-action="BlankRow" asp-controller="Test">Add Row...</a> <br />
        <input type="submit" value="Finished" />
    </form>
    
    @section scripts {
        <script>
            $("#addItem").click(function () {
                $.ajax({
                    url: this.href,
                    cache: false,
                    success: function (html) { $("#editorRows").append(html); }
                });
                return false;
            });
        </script>
    }

    The Partial View:

    @model TestApplication.Models.ViewModels.ObjectBViewModel
    @using HtmlHelpers.BeginCollectionItemCore;
    
    <div class="editorRow">
        @using (Html.BeginCollectionItem("list"))
        {
            <span>Text: </span> @Html.EditorFor(m => m.Text);
            <span> Image: </span> @Html.TextBoxFor(m => m.ImageFile, new { @type = "file" });
        }
        <a href="#" class="deleteRow">delete</a>
    </div>
    
    @section Scripts {
        <script>
            $(document).ready(function () {
                $("a.deleteRow").on("click", function () {
                    $(this).parents("div.editorRow:first").remove();
                    return false;
                });
            });
        </script>
    }

    Finally the controller

            public IActionResult BlankRow()
            {
                return PartialView("_ObjectBEditor", new ObjectBViewModel());
            }
    
            [HttpPost]
            public async Task<IActionResult> AddTwo(CreateEditObjectAViewModel model)
            {
                if (ModelState.IsValid)
                {
                    List<ObjectB> ObjectBs = new List<ObjectB>(); 
                    foreach (var item in model.ObjectList)
                    {
                        var objectb = new ObjetB
                        {
                            Id = item.Id,
                            Text = item.Text,
                            //Image. = item.ImageFileImage
                        };
                        ObjectsBs.Add(objectb);
                    }
    
                    ObjectAmodelToSave = new ObjectA()
                    {
                        Name = model.Name,
                        ObjectBList = ObjectBs,
                    };
                    _db.Story.Add(modelToSave);
                    await _db.SaveChangesAsync();
                    return RedirectToAction("Index");
                }
                return View(model); 
            }
    

    What should happen is, in each row there should be a text and an image file (optional), and each image must be linked correctly to its text. Am I doing it in this logic?

    And how am I going to deal with image file in each list item?

    Thanks.

    Saturday, August 22, 2020 3:58 AM

Answers

  • User-17257777 posted

    Hi rosaud,

    I think there is no need to use Html.BeginCollectionItem. I made a demo based on your codes, you can have a reference.

    Model:

    public class CreateEditObjectAViewModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public IList<ObjectBViewModel> ObjectBList { get; set; }
    }
    
    public class ObjectBViewModel
    {
        public int Id { get; set; }
        public string Text { get; set; }
        public IFormFile ImageFile { get; set; } //for an image object
    }

    Controller:

    public IActionResult Index()
    {
        CreateEditObjectAViewModel model = new CreateEditObjectAViewModel
        {
            Id = 1,
            Name = "AAA",
            ObjectBList = new List<ObjectBViewModel>
            {
                new ObjectBViewModel{ Id = 1, Text = "aaa"},
                new ObjectBViewModel{ Id = 2, Text = "bbb"}
            }
        };
    
        return View(model);
    }
    
    public IActionResult AddRow()
    {
        return PartialView("_ObjectBEditor", new ObjectBViewModel());
    }
    
    [HttpPost]
    public IActionResult Create(CreateEditObjectAViewModel model)
    {
               
        return View();
    }
    

    Index View:

    @model CreateEditObjectAViewModel
    
        <form asp-action="Create" method="post" asp-controller="Home" enctype="multipart/form-data">
            <div class="form-group row">
                <div class="col-2">
                    <label asp-for="Name" class="col-form-label"></label>
                </div>
                <div class="col-5">
                    <input asp-for="Name" class="form-control" />
                </div>
    
            </div>
            <div id="editorRows">
                @foreach (var item in Model.ObjectBList)
                {
                    <partial name="_ObjectBEditor" model="item" />
                }
            </div>
            <a id="addItem" asp-action="AddRow" asp-controller="Home">Add Row...</a> <br />
            <input type="submit" id="submit" value="Finished" />
        </form>
    
    @section scripts {
        <script>
            $("#submit").click(function (e) {
                e.preventDefault();
                var formData = new FormData();
                $("input[name='ImageFile']").each(function (i) {
                    var ReadyToUpload = $(this)[0].files;
                    if (ReadyToUpload.length > 0) {
                        formData.append("ObjectBList[" + i + "].ImageFile", ReadyToUpload[0]);
                    }
                });
                $("input[name='Text']").each(function (i) {
                    var TextName = $(this).val();
                    formData.append("ObjectBList[" + i + "].Text", TextName);
                    
                });
                formData.append("Name", $("#Name").val());
    
                $.ajax({
                    method: 'post',
                    url: "Home/Create",
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function () {
    
                    }
                });
                
            });
    
            $("#addItem").click(function () {
                $.ajax({
                    url: this.href,
                    cache: false,
                    success: function (html) { $("#editorRows").append(html); }
                });
                return false;
            });
    
            $("a.deleteRow").on("click", function () {
                $(this).parents("div.editorRow:first").remove();
                return false;
            });
        </script>
    }

    Partial View:

    @model ObjectBViewModel
    
    <div class="editorRow">
    
        <span>Text: </span> @Html.EditorFor(m => m.Text)
        <span> Image: </span> @Html.TextBoxFor(m => m.ImageFile, new { @type = "file" })
    
        <a href="#" class="deleteRow">delete</a>
    </div>
    
    @section Scripts {
        <script>
            $(document).ready(function () {
                $("a.deleteRow").on("click", function () {
                    $(this).parents("div.editorRow:first").remove();
                    return false;
                });
            });
        </script>
    }

    Result:

    Best Regards,

    Jiadong Meng

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, August 24, 2020 10:16 AM

All replies

  • User-17257777 posted

    Hi rosaud,

    I think there is no need to use Html.BeginCollectionItem. I made a demo based on your codes, you can have a reference.

    Model:

    public class CreateEditObjectAViewModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public IList<ObjectBViewModel> ObjectBList { get; set; }
    }
    
    public class ObjectBViewModel
    {
        public int Id { get; set; }
        public string Text { get; set; }
        public IFormFile ImageFile { get; set; } //for an image object
    }

    Controller:

    public IActionResult Index()
    {
        CreateEditObjectAViewModel model = new CreateEditObjectAViewModel
        {
            Id = 1,
            Name = "AAA",
            ObjectBList = new List<ObjectBViewModel>
            {
                new ObjectBViewModel{ Id = 1, Text = "aaa"},
                new ObjectBViewModel{ Id = 2, Text = "bbb"}
            }
        };
    
        return View(model);
    }
    
    public IActionResult AddRow()
    {
        return PartialView("_ObjectBEditor", new ObjectBViewModel());
    }
    
    [HttpPost]
    public IActionResult Create(CreateEditObjectAViewModel model)
    {
               
        return View();
    }
    

    Index View:

    @model CreateEditObjectAViewModel
    
        <form asp-action="Create" method="post" asp-controller="Home" enctype="multipart/form-data">
            <div class="form-group row">
                <div class="col-2">
                    <label asp-for="Name" class="col-form-label"></label>
                </div>
                <div class="col-5">
                    <input asp-for="Name" class="form-control" />
                </div>
    
            </div>
            <div id="editorRows">
                @foreach (var item in Model.ObjectBList)
                {
                    <partial name="_ObjectBEditor" model="item" />
                }
            </div>
            <a id="addItem" asp-action="AddRow" asp-controller="Home">Add Row...</a> <br />
            <input type="submit" id="submit" value="Finished" />
        </form>
    
    @section scripts {
        <script>
            $("#submit").click(function (e) {
                e.preventDefault();
                var formData = new FormData();
                $("input[name='ImageFile']").each(function (i) {
                    var ReadyToUpload = $(this)[0].files;
                    if (ReadyToUpload.length > 0) {
                        formData.append("ObjectBList[" + i + "].ImageFile", ReadyToUpload[0]);
                    }
                });
                $("input[name='Text']").each(function (i) {
                    var TextName = $(this).val();
                    formData.append("ObjectBList[" + i + "].Text", TextName);
                    
                });
                formData.append("Name", $("#Name").val());
    
                $.ajax({
                    method: 'post',
                    url: "Home/Create",
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function () {
    
                    }
                });
                
            });
    
            $("#addItem").click(function () {
                $.ajax({
                    url: this.href,
                    cache: false,
                    success: function (html) { $("#editorRows").append(html); }
                });
                return false;
            });
    
            $("a.deleteRow").on("click", function () {
                $(this).parents("div.editorRow:first").remove();
                return false;
            });
        </script>
    }

    Partial View:

    @model ObjectBViewModel
    
    <div class="editorRow">
    
        <span>Text: </span> @Html.EditorFor(m => m.Text)
        <span> Image: </span> @Html.TextBoxFor(m => m.ImageFile, new { @type = "file" })
    
        <a href="#" class="deleteRow">delete</a>
    </div>
    
    @section Scripts {
        <script>
            $(document).ready(function () {
                $("a.deleteRow").on("click", function () {
                    $(this).parents("div.editorRow:first").remove();
                    return false;
                });
            });
        </script>
    }

    Result:

    Best Regards,

    Jiadong Meng

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, August 24, 2020 10:16 AM
  • User-1741097413 posted

    Thank you so much Jiadong Meng, this is more than enough.
    I really appreciate your effort. 

    Regards.

    Tuesday, August 25, 2020 1:28 AM