How to send dynamically created fields via JSON?

Asked

Viewed 780 times

0

How to send via JSON fields input dynamically created, for example:

The user type in a text field number 4 and the function creates 8 input fields, or type 5 and the function creates 10 input fields, this function already works and is OK!.

This is the part of the function that creates the fields dynamically:

for (var i = 0; i < _qtde; i++) {
var new_date = new Date();
new_date.setMonth(new_date.getMonth() + i);
$("#divParcela").append("<div class='col-xs-6'> <label>Vencimento - parcela 
  " + parseInt(i + 1) + "</label> <input type='text' id='' value='" + 
  $.datepicker.formatDate('dd/mm/yy', new_date) + "' class='form-control' />
  </div> <div class='col-xs-6'><label>Valor - parcela " + parseInt(i + 1) + 
  "</label><input type='text' id='' value='" + _valorParcela.toFixed(3)  + 
  "' class='form-control' /></div>");
 };

Then when you click the button Register, the system takes the values and sends via json to the database, something like:

$("#btnCadastrar").on("click", function () {

        var _movimentacao = {
            "MovimentoFinanceiroID": $("#MovimentoFinanceiroID").val(),
            "NumDocumento": $("#NumDocumento").val(),
            "ItemMovimentoFinanceiro":[]
            };

        _movimentacao.ItemMovimentoFinanceiro.pusch({
                "NumParcela": "Campo_Parcela_criado_dinamicamente",
                "ValorDocumento": "Campo_Valor_criado_dinamicamente"
         });

        $.ajax({
        url: "/MovimentoFinanceiro/IncluirJSON",
        type: "post",
        dataType: "json",
        contentType: "application/json; charset=utf-8",
        processData: false,
        data: JSON.stringify(_movimentacao),
        success: function (data) {
            window.location.href = "/MovimentoFinanceiro/Index";
        },
        error: function (result) {
            alert(result.responseText);
        }
    });
});

This is the Jsonresult method:

public JsonResult IncluirJSON(MovimentoFinanceiroViewModel pMovimentoFinanceiro)
        {
            try
            {
                //Aqui vou implementar rotina para gravar no banco de dados
                return Json("OK", JsonRequestBehavior.AllowGet);
            }
            catch (Exception)
            {
                return Json("ERRO", JsonRequestBehavior.AllowGet);
            }
        }

These two classes are the intermediate model (DTO):

public class MovimentoFinanceiroViewModel
{
    public List<ListDetalheMovimento> ListItens { get; set; }
}

public class ListDetalheMovimento
{
    public decimal ValorParcela { get; set; }
    [Column(TypeName = "DateTime2")]
    public DateTime? DataVencimentoParcela { get; set; }
}

Entity reflecting the database table:

 public class MovimentoFinanceiro
    {
        [Key]
        public int MovimentoFinanceiroID { get; set; }
        public int ItemPlanoContabilID { get; set; }
        public decimal ValorParcela { get; set; }
        public DateTime DataVencimentoParcela { get; set; }
}
  • You need to do this by json same or can do for a normal POST? Maybe the Begincollectionitem can serve you.

  • Yes, in that case it has to be Json

3 answers

2

To send the values of dynamically created fields you need to create elements input with the same name and in his model you need to own a property with the same name used in the attribute name and from a collection like array or List<T>. Just do this and ASP.NET MVC will do the Binding automatic of such data sent into the property.

Also, I noticed that you are sending the data to the action method in JSON format (contentType: "application/json; charset=utf-8"), the ideal would be for you to switch to the contentType: "application/x-www-form-urlencoded; charset=UTF-8" which is the standard format, and which will allow easier serialization of the form data using only the .serialize().

Well, I’ll give you an example to facilitate understanding.

Model:

public class IndexModel
{
    public List<string> Coisas { get; set; }
}

Action method:

 public JsonResult IndexJson(IndexModel model)
 {
      return Json("Ok", JsonRequestBehavior.AllowGet);
 }

View:

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "form" }))
{
    <div class="form-group">
        @Html.LabelFor(x => x.Coisas)
        @Html.TextBoxFor(x => x.Coisas, new { @class = "form-control" })
        @Html.TextBoxFor(x => x.Coisas, new { @class = "form-control" })
        @Html.TextBoxFor(x => x.Coisas, new { @class = "form-control" })
        @Html.TextBoxFor(x => x.Coisas, new { @class = "form-control" })
        @Html.TextBoxFor(x => x.Coisas, new { @class = "form-control" })
    </div>

    <button id="enviar" class="btn btn-primary">Enviar</button>
}

<script src="~/Scripts/jquery-1.10.2.min.js"></script>

<script>
    $('#form').submit(function () {
        $.ajax({
            url: '/Home/IndexJson',
            type: 'POST',
            dataType: 'json',
            data: $('#form').serialize(),
            success: function (data) {
                console.log(data);
            },
            error: function (result) {
                console.log(result);
            }
        });
    });
</script>

In the example I generated the fields by Razor, but in your case just keep doing it by Javascript and remembering to put the same name in all elements input:

<form action="/Home/Index" method="post">
    <div class="form-group">
        <label for="coisa">Coisas</label>
        <input class="form-control" type="text" name="coisa">
        <input class="form-control" type="text" name="coisa">
        <input class="form-control" type="text" name="coisa">
        <input class="form-control" type="text" name="coisa">
        <input class="form-control" type="text" name="coisa">
    </div>
    <button class="btn btn-primary">Enviar</button>
</form>

One thing that had gone unnoticed by me and it would be interesting to add is that in your case it might be better to create a model to be used in place of the MovimentoFinanceiro as a parameter in IncluirJSON, This is a pattern known as Data Transfer Object (DTO), a DTO can be used as a data container between communications within a system, in your case this DTO, which is nothing more than a class mounted by yourself, must contain this collection property, in addition to the other properties of the MovimentoFinanceiro.

This way you don’t need to change the MovimentoFinanceiro which apparently maps a table in the database, therefore it is impossible to change its structure.

  • I get it, very good alternative, but I believe it won’t work for my case due to the detail of Model a kind of collection as array or List<T>, my model is not List<T>, as I added in the question.

  • @Adrianosuv changed the answer to talk about it, from a look at the last two paragraphs, I hope it was clear, anything ask.

  • Hello @Zignd I implemented the DTO class and the data is not being loaded, ie are sent to the JsonResult as null, what did I do wrong ?

  • @Adrianosuv Inspecione a request being made to the server, does it really have the data? Also note that the properties names in the sent JSON must be the same as the properties of the DTO class.

  • OK @Zignd ! , the data was sent to the server only when the model TDO NO was a guy List<T>, or when I changed my model TDO for type List<T> it was not sent, very likely I was doing something wrong and as I am with the tight deadlines I found another solution that served me, I do not know if it is the best, but at the moment it is what I need to meet the very short deadlines, I will add the solution in question.

1

In your case, I believe that using Action Filters is a good way, since you want to receive the content of your JSON already as a correctly filled instance in your Action.

To make it happen the idea is:

1) You must have a pattern when naming your fields. By default, the browser understands that when there are multiple fields of the same name with square brackets (for example "Value[]"), that all values of these fields will be divided by a comma in the request.

2) Create a class that extends from Actionfilterattribute and make the method Onactionexecuting() read the parameters passed according to the defined pattern. Through the context of this filter, you can handle all these fields and return your filled Viewmodel.

3) Mark your Action with the class created above.

You can even implement this by just passing a string with your JSON and doing the proper treatment in the filter. However, taking advantage of @Zignd’s good idea to pass on the serialized form information, I made a small example based on how he was already doing.

A View, where I took some of the code from the @Zignd response:

<form id="form">
    <input type="text" name="ValorParcela[]">
    <input type="text" name="DataVencimentoParcela[]">

    <input type="text" name="ValorParcela[]">
    <input type="text" name="DataVencimentoParcela[]">

    <input type="text" name="ValorParcela[]">
    <input type="text" name="DataVencimentoParcela[]">

    <input type="text" name="ValorParcela[]">
    <input type="text" name="DataVencimentoParcela[]">

    <input type="text" name="ValorParcela[]">
    <input type="text" name="DataVencimentoParcela[]">
</form>
<button id="submit">Enviar</button>

<script src="~/Scripts/jquery-1.10.2.min.js"></script>

<script>
    $('#submit').on('click', function () {
        $.ajax({
            url: '/Home/IncluirJSON',
            type: 'POST',
            dataType: 'json',
            data: $('#form').serialize(),
            success: function (data) {
                console.log(data);
            },
            error: function (result) {
                console.log(result);
            }
        });
    });
</script>

The classes related to the View Model:

public class MovimentoFinanceiroViewModel
{
    public MovimentoFinanceiroViewModel()
    {
        this.DetalhesMovimento = new List<DetalheMovimento>();
    }

    public List<DetalheMovimento> DetalhesMovimento { get; set; }
}

public class DetalheMovimento
{
    public decimal ValorParcela { get; set; }        
    public DateTime? DataVencimentoParcela { get; set; }
}

The class of the filter:

public class MovimentoFinanceiroFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        /*
        Exemplo de como são recebidos os campos:

        filterContext.HttpContext.Request.Form["ValorParcela[]"] = "1,2,3,4,5"
        filterContext.HttpContext.Request.Form["DataVencimentoParcela[]"] = "1,2,3,4,5"
         */

        string sValorParcela = filterContext.HttpContext.Request["ValorParcela[]"];
        string sDataVencimentoParcela = filterContext.HttpContext.Request["DataVencimentoParcela[]"];

        var valoresParcela = sValorParcela.Split(new char[] { ',' });
        var datasVencimentoParcela = sDataVencimentoParcela.Split(new char[] { ',' });

        var viewModel = new MovimentoFinanceiroViewModel();

        for (int i = 0; i < valoresParcela.Length; i++)
        {
            decimal valorParcela = 0;
            Decimal.TryParse(valoresParcela[i], out valorParcela);

            DateTime dataVencimentoParcela = DateTime.MinValue;
            DateTime.TryParse(datasVencimentoParcela[i], out dataVencimentoParcela);

            viewModel.DetalhesMovimento.Add(new DetalheMovimento { ValorParcela = valorParcela, DataVencimentoParcela = dataVencimentoParcela });
        }

        filterContext.ActionParameters["pMovimentoFinanceiro"] = viewModel;
    }
}

And finally, the Action:

[MovimentoFinanceiroFilterAttribute]
public JsonResult IncluirJSON(MovimentoFinanceiroViewModel pMovimentoFinanceiro)
{
    try
    {
        //Aqui vou implementar rotina para gravar no banco de dados
        return Json("OK", JsonRequestBehavior.AllowGet);
    }
    catch (Exception)
    {
        return Json("ERRO", JsonRequestBehavior.AllowGet);
    }
}
  • Good morning @Diego Jeronymo, very good your solution but as I had commented in the previous post with Zignd I opted for another solution and when you posted your suggestion I had already made an implementation that met, but very good your tip.

0

It may not be the best solution, but it met my needs:

   $("#btnCadastrar").on("click", function () {
            var _movimentacao = {
                "MovimentoFinanceiroID": $("#MovimentoFinanceiroID").val(),
                "NumDocumento": $("#NumDocumento").val(),
                "ItemMovimentoFinanceiro": []
            };

            var _contaLinha = 0;

            $("input[name='ValorParcela[]']").each(function () {                
            var _valor = new Array();
            var _dtVencimento = new Array();
            _valor.push($(this).val());               
            _dtVencimento[0] = new Object();
            _dtVencimento[0].DataVencimentoParcela = $("input[name='DataVencimentoParcela[]']")[_contaLinha].value;
            _movimentacao.ItemMovimentoFinanceiro.push({
                "ItemMovimentoFinanceiro": 0,
                "MovimentoFinanceiroID": $("#MovimentoFinanceiroID").val(),
                "qtdeParcela": $("#qtdeParcela").val(),
                "ValorParcela": JSON.parse(_valor),
                "DataVencimentoParcela": _dtVencimento
            });
            _contaLinha = _contaLinha + 1;
        });

        $.ajax({
            url: "/MovimentoFinanceiro/IncluirJSON",
            type: "post",
            dataType: "json",
            contentType: "application/json charset=utf-8",
            processData: false,
            data: JSON.stringify(_movimentacao),
            success: function (data) {
                console.log(data);
            },
            error: function (result) {
                alert(result.responseText);
            }
        });
    });

Browser other questions tagged

You are not signed in. Login or sign up in order to post.