c# – Dynamically create fields and set in the model

Question:

I'm developing a register, where I have my fields according to my model:

public class Autor
{
    [Key]
    public int IdAutor { get; set; }
    public string Nome { get; set; }
    public DateTime DataNascimento { get; set; }
}

that is, my .cshtml file will have the Model inputs .

So far so good, now, for example, if I want to add one more Autor dynamically, without leaving the page, using AJAX , in fact, make the call:

 $(document).ready(function() {

        $("#addItem").click(function () {
            $.ajax({
                type: 'GET',
                url: '@Url.Action("AdicionarNovoAutor", "Autores")',
                success: function (html)
                {
                    $("#addnovo").append(html);
                },
                error: function (html)
                {
                    console.log("Erro");
                }
            });

        });        
});

So when I click on my #addItem button I go to my Controller and return a PartialView of my inputs , the PartialView follows:

@model List<MVC1.Models.Autor>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model[0].Nome, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model[0].Nome, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model[0].Nome, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model[0].DataNascimento, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model[0].DataNascimento, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model[0].DataNascimento, "", new { @class = "text-danger" })
            </div>
        </div>
    </div>
}
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

this code goes to my div, but in my Controller I can't get the ones I added, follow the Controller when I go to Create :

public ActionResult AdicionarNovoAutor(List<Autor> autores)
    {
        autores.Add(new Autor());

        return PartialView("~/Views/Autores/_AutorPartial.cshtml", autores);
    }

In this case, I'm trying to pass a list of Authors as a parameter, I don't know if that's right. I hope you understand my problem, thanks and I look forward to help.

Answer:

Come on, we've got lots of bugs… DEBUGGING/DEBUGING code is the best tool to find bugs

  1. You are making a GET request (via AJAX) to the method AdicionarNovoAutor, this method expects a list of author as a parameter, put in your Javascript code, which makes the AJAX request, you are not going through the already created back control , if you debug your code, you will see that the authors parameter is getting null in the controller. But that's not even a problem, because this, as I'll explain later, is useless…
  2. There's no reason to send the list of authors already filled in by the user back to the controller, just to add new fields (authors), this is a front-end task not the back-end, unless you do some processing of the data doesn't make sense all this data traffic.
  3. If you really need to send the data back just to add one more, change the action to POST, change the javascript to send the data via POST and actually send the data via the data property of the jQuery AJAX module you are using
  4. Your partial view has 4 problems (see items 5, 6, 7 and 8 below).
  5. You are rendering scripts every time you call the partial view (the "Scripts" region at the end), you only need them 1x on your page, ideally put this call in the _Layout or the full View itself, that's not it which is causing your code not to work as expected, but this is an error.
  6. Your partial view model points to a list of Author @model List<MVC1.Models.Autor> , but nowhere in your partial view are you treating it as a list (there is no for, no foreach, or a do/ while), which makes it useless again move to the AdicionarNovoAutor method as was said in item 1.
  7. You are using @Html.BeginForm in the partial view, this means that every time you call the partial view, a new form will be created on the final page, when submitting the data, only the form data will be sent to the controller, like each form only has 1 author, you will lose the data entered in the other fields.
  8. You are creating the fields in the partial view as if it were a simple template, but you want an array/Author list, so you need to tell the HTML that each field in the view belongs to an array by adding square brackets to the end of the field name nomedocampo[]

Let's go to the working example:

Javascript – functions to add new fields to the screen and monitor the click on the #addItem button – novoAuthor.js

$(document).ready(function(){
    $("#addItem").click(adicionaNovosCampos);
    adicionaNovosCampos(); //já chama a função para exibir os campos do primeiro autor
});

function adicionaNovosCampos(){
    var html = "Nome: <input type='text' name='Nome[]' /><br />Data de nascimento: <input type='date' name='DataNascimento[]' />";
    $("#formulario").append(html);
}

HTML – a view principal, nada de partial view – NovoAutor.cshtml

@using(Html.BeginForm("Salvar","Autor", FormMethod.Post)){
    <div id="formulario"></div>
    <button type="button" class="btn btn-info" id="addItem">Adicionar novo autor</button>
    <button type="submit" class="btn btn-success">Salvar</button>
}
@section Scripts {
     @Scripts.Render("~/bundles/jquery")
     <script type="text/javascript" src="novoAutor.js"></script>
}

Controller – with the actions of displaying the View(form) and the method to save the data in a supposed database – Author.cs

public ActionResult NovoAutor() {
    return View();
}   

[HttpPost]
public ActionResult Salvar(Autor[] autores) {
    var db = new DatabaseContext();
    foreach(var autor in autores)
        db.Autor.Add(autor);
    db.SaveChanges();
}

Well, this above is just a practical example (not tested, written now on Stack Overflow) of what it would be like to add new fields to the screen dynamically on demand and how to save and send items to the control as well.

As you can see, it is a very basic example, which does not have any "styling" or data validation, I just made what you had problems to work, it is up to you to understand and modify it to be fully functional for your needs!

Obviously this should serve as a study for you to improve your knowledge, along with this, I recommend reading the following article: ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries

Scroll to Top
AllEscort