How to add fields dynamically and link them to the viewmodel in Asp.Net

1

I have a 1: N relationship between Person and Contact. In my view, I can list all the contacts of a client when it is opened.

Ineedtoimplementthe"New Contact" button so that when clicked, dynamically add the fields (Type of Contact, Contact, Main Contact, Details and Delete Button) ... I tried to do with Java Script and I managed, but I have had problems with validations that did not work and the "combobox ContactType" that does not populate ... This is because I could not use the asp.net core asp- "validation-for" and "asp-items" tags in the block JavaScript ...

I made a FOR to receive the database data and the validations and combobox work correctly. The problem is in the JS block that does not work. How do I make it work in JavaScript?

@model Retaguarda.Application.ViewModels.Pessoa.PessoaViewModel
@{
    ViewData["Title"] = "_PessoaContato";
}
<div class="form-horizontal">
    <div class="form-group row">
        <div class="col-md-12">
            <div class="col-md-12" id="div-contatos">

                @if (Model.PessoasContatosViewModel != null)
                {
                    @for (int i = 0; i < Model.PessoasContatosViewModel.Count; i++)
                    {
                        <div class="row align-items-center">
                            <div class="col-md-2">
                                @Html.HiddenFor(model => model.PessoasContatosViewModel[i].Id, new { @class = "hid-id" })
                                <label asp-for="PessoasContatosViewModel[i].ContatoTipoId" class="control-label sel-contatoTipo">Tipo de Contato</label>
                                <select asp-for="PessoasContatosViewModel[i].ContatoTipoId" asp-items="Model.ContatosTipos" data-plugin="selectpicker" title="Selecione uma opção" class="form-control show-tick show-menu-arrow sel-contatoTipo"></select>
                                <span asp-validation-for="PessoasContatosViewModel[i].ContatoTipoId" class="text-danger"></span>
                            </div>
                            <div class="col-md-4">
                                <label asp-for="PessoasContatosViewModel[i].Contato" class="control-label txt-contato">Contato</label>
                                <input type="text" asp-for="PessoasContatosViewModel[i].Contato" class="form-control txt-contato" />
                                <span asp-validation-for="PessoasContatosViewModel[i].Contato" class="text-danger"></span>
                            </div>
                            <div class="col-md-2">
                                <label class="control-label">&nbsp;</label>
                                <div class="checkbox-custom checkbox-default">
                                    <input type="checkbox" asp-for="PessoasContatosViewModel[i].ContatoPrincipal" class="ckb-contatoPrincipal" checked autocomplete="off" />
                                    <label asp-for="PessoasContatosViewModel[i].ContatoPrincipal" class=" ckb-contatoPrincipal">Contato Principal</label>
                                </div>
                            </div>
                            <div class="col-md-3">
                                <label asp-for="PessoasContatosViewModel[i].Detalhes" class="control-label txt-detalhes">Detalhes</label>
                                <textarea asp-for="PessoasContatosViewModel[i].Detalhes" class="form-control txt-detalhes"></textarea>
                                <span asp-validation-for="PessoasContatosViewModel[i].Detalhes" class="text-danger"></span>
                            </div>
                            <div class="col-md-1">
                                <button type="button" class="btn btn-icon btn-default btn-outline btn-remover-contato" data-id="@Model.PessoasContatosViewModel[i].Id." style="margin-top: 30px;"><i class="icon wb-trash" aria-hidden="true"></i></button>
                            </div>
                        </div>
                    }


                }

            </div>
        </div>
    </div>
    <div class="form-group row">
        <div class="col-md-12">
            <div class="col-md-2">
                <button id="btn-add-contato" type="button" class="btn btn-icon btn-default btn-outline"><i class="icon wb-plus" aria-hidden="true"></i> Novo Contato</button>
            </div>
        </div>
    </div>
</div>



<script>
    $(function () {
        var qtdContatos = 0;

        $("#btn-add-contato").click(function (e) {
            e.preventDefault();

            var blocoContato = '<div class="row align-items-center">' +
                '  <div class="col-md-2" >' +
                '     <label name="PessoaContatoViewModel[' + qtdContatos + '].ContatoTipoId" class="control-label sel-contatoTipo">Tipo de Contato</label>' +
                '    <select name="PessoaContatoViewModel[' + qtdContatos + '].ContatoTipoId" data-plugin="selectpicker" title="Selecione uma opção" class="form-control show-tick show-menu-arrow sel-contatoTipo"></select>' +
                ' </div >' +
                '<div class="col-md-4">' +
                ' <label name="PessoaContatoViewModel[' + qtdContatos + '].Contato" class="control-label txt-contato">Contato</label>' +
                '  <input type="text" name="PessoaContatoViewModel[' + qtdContatos + '].Contato" class="form-control txt-contato" />' +
                ' </div>' +
                ' <div class="col-md-2">' +
                ' <label class="control-label">&nbsp;</label>' +
                ' <div class="checkbox-custom checkbox-default">' +
                ' <input type="checkbox" name="PessoaContatoViewModel[' + qtdContatos + '].ContatoPrincipal" class="ckb-contatoPrincipal" checked autocomplete="off" />' +
                '<label for="PessoaContatoViewModel[' + qtdContatos + '].ContatoPrincipal class="ckb-contatoPrincipal">Contato Principal</label>' +
                '</div>' +
                ' </div>' +
                ' <div class="col-md-3">' +
                '<label name="PessoaContatoViewModel[' + qtdContatos + '].Detalhes" class="control-label txt-detalhes">Detalhes</label>' +
                '<textarea name="PessoaContatoViewModel[' + qtdContatos + '].Detalhes" class="form-control txt-detalhes"></textarea>' +
                '</div>' +
                '<div class="col-md-1">' +

                '<button type="button" class="btn btn-icon btn-default btn-outline btn-remover-contato" style="margin-top: 30px;"><i class="icon wb-trash" aria-hidden="true"></i></button>' +
                ' </div>' +
                ' </div>';

            $("#div-contatos").append(blocoContato);
            qtdContatos++;
        });

        $("#div-contatos").on("click", ".btn-remover-contato", function (e) {
            e.preventDefault();

            $(this).parent().parent().remove();

            qtdContatos--;

            $("#div-contatos .row").each(function (indice, elemento) {
                $(elemento).find(".sel-contatoTipo").attr("name", "PessoaContatoViewModel[" + indice + "].ContatoTipoId");
                $(elemento).find(".txt-contato").attr("name", "PessoaContatoViewModel[" + indice + "].Contato");
                $(elemento).find(".ckb-contatoPrincipal").attr("name", "PessoaContatoViewModel[" + indice + "].ContatoPrincipal");
                $(elemento).find(".txt-detalhes").attr("name", "PessoaContatoViewModel[" + indice + "].Detalhes");
            });

        });

    });
</script>


@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Controller:

[HttpGet]
[Authorize(Policy = "CanWritePessoaData")]
[Route("pessoa-gerenciar/editar-pessoa/{id:int}")]
public IActionResult Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var pessoaViewModel = _pessoaAppService.GetJoinById(id.Value);

    if (pessoaViewModel == null)
    {
        return NotFound();
    }       

    pessoaViewModel.PessoaGenericoViewModel.Filiais = PreencherFiliais();
    pessoaViewModel.ContatosTipos = PreencherContatodosTipos();
    return View(pessoaViewModel);

}

ViewModels:

public class PessoaViewModel
{
    [Key]
    public int Id { get; set; }

    [DisplayName("Natureza")]
    [Required(ErrorMessage = "Escolha uma Natureza")]
    public PessoaNatureza PessoaNatureza { get; set; }
    [DisplayName("Natureza")]
    public string PessoaNaturezaDescricao { get; set; }
    [DisplayName("Naturezas")]
    public IEnumerable<SelectListItem> PessoasNaturezas { get; set; }

    public PessoaGenericoViewModel PessoaGenericoViewModel { get; set; }
    public IEnumerable<PessoaGenericoViewModel> PessoasGenericosViewModel { get; set; }

    public IEnumerable<SelectListItem> ContatosTipos { get; set; }
    public List<PessoaContatoViewModel> PessoasContatosViewModel { get; set; }

    public PessoaFisicaViewModel PessoaFisicaViewModel { get; set; }
    public PessoaJuridicaViewModel PessoaJuridicaViewModel { get; set; }

    public PessoaViewModel()
    {
        PessoasNaturezas = ExtensaoDeEnumerador.EnumParaSelectListGenerico<PessoaNatureza>("U", PessoaNatureza.ToString()).OrderBy(x => x.Text);
        PessoaFisicaViewModel = null;
        PessoaJuridicaViewModel = null;
    }
}


public class PessoaContatoViewModel
{
    [Key]
    public int Id { get; set; }

    public int PessoaId { get; set; }

    [DisplayName("Tipo de Contato")]
    [Required(ErrorMessage = "Escolha um Tipo de Contato")]
    public int ContatoTipoId { get; set; }
    [DisplayName("Tipo de Contato")]
    public string ContatoTipoDescricao { get; set; }
    [DisplayName("Tipos de Contato")]
    public IEnumerable<SelectListItem> ContatosTipos { get; set; }

    [DisplayName("Contato")]
    [Required(ErrorMessage = "O campo Contato é obrigatório")]
    [MaxLength(100, ErrorMessage = "O campo {0} deve ter no máximo {1} caracteres")]
    public string Contato { get; set; }

    [DisplayName("Detalhes")]
    [MaxLength(150, ErrorMessage = "O campo {0} deve ter no máximo {1} caracteres")]
    public string Detalhes { get; set; }

    [DisplayName("Contato Principal")]
    public bool ContatoPrincipal { get; set; }
}

    
asked by anonymous 01.09.2018 / 12:47

1 answer

2

If you add pure html just like you are doing, it will not take important things from your model, like DataAnnotations for example. A "mixed" solution would be to create an Action that returns a new line from your model and through an Ajax call to that action, pick up the properly rendered content, and add.

To do this, make the block you want to add dynamically in PartialView :

ContactPartial:

<div class="row align-items-center">
    <div class="col-md-2">
        @Html.HiddenFor(model => model.PessoasContatosViewModel[@ViewBag.indice].Id, new { @class = "hid-id" })
        <label asp-for="PessoasContatosViewModel[@ViewBag.indice].ContatoTipoId" class="control-label sel-contatoTipo">Tipo de Contato</label>
        <select asp-for="PessoasContatosViewModel[@ViewBag.indice].ContatoTipoId" asp-items="Model.ContatosTipos" data-plugin="selectpicker" title="Selecione uma opção" class="form-control show-tick show-menu-arrow sel-contatoTipo"></select>
        <span asp-validation-for="PessoasContatosViewModel[@ViewBag.indice].ContatoTipoId" class="text-danger"></span>
    </div>
    <div class="col-md-4">
        <label asp-for="PessoasContatosViewModel[@ViewBag.indice].Contato" class="control-label txt-contato">Contato</label>
        <input type="text" asp-for="PessoasContatosViewModel[@ViewBag.indice].Contato" class="form-control txt-contato" />
        <span asp-validation-for="PessoasContatosViewModel[@ViewBag.indice].Contato" class="text-danger"></span>
    </div>
    <div class="col-md-2">
        <label class="control-label">&nbsp;</label>
        <div class="checkbox-custom checkbox-default">
            <input type="checkbox" asp-for="PessoasContatosViewModel[@ViewBag.indice].ContatoPrincipal" class="ckb-contatoPrincipal" checked autocomplete="off" />
            <label asp-for="PessoasContatosViewModel[@ViewBag.indice].ContatoPrincipal" class=" ckb-contatoPrincipal">Contato Principal</label>
        </div>
    </div>
    <div class="col-md-3">
        <label asp-for="PessoasContatosViewModel[@ViewBag.indice].Detalhes" class="control-label txt-detalhes">Detalhes</label>
        <textarea asp-for="PessoasContatosViewModel[@ViewBag.indice].Detalhes" class="form-control txt-detalhes"></textarea>
        <span asp-validation-for="PessoasContatosViewModel[@ViewBag.indice].Detalhes" class="text-danger"></span>
    </div>
    <div class="col-md-1">
        <button type="button" class="btn btn-icon btn-default btn-outline btn-remover-contato" data-id="@Model.PessoasContatosViewModel[@ViewBag.indice].Id." style="margin-top: 30px;"><i class="icon wb-trash" aria-hidden="true"></i></button>
    </div>
</div>

Create an Action that returns this Partial :

public PartialViewResult NovoContato(int indice)
{
     ViewBag.indice= indice;
     return PartialView("ContatoPartial", new PessoasContatosViewModel());
}

Change the javascript to make an Ajax call to trigger Action , and add the content to the page:

var itemIndex = $("#container input.iHidden").length; 
        var novoIndice = qtdContatos +1;      //suponho q já tenha calculado
        e.preventDefault();
        $.get("@Url.Action("NovoContato", "NomeDaController")/"+novoIndice ,function(data){
            $("#div-contatos").append(data);
        });    

The new line will be rendered by ASP.Net , and the ajax call will not allow a Post of the page.

    
05.09.2018 / 14:24