Vince
Vince

Reputation: 1416

How to serialize a form with an array of input

I'm trying to serialize a form that can contains an array of object and send it to my application service and i cannot find any way to make it work...

Here is my Save Method

var brewer = _$form.serializeFormToObject();

abp.services.app.brewer.create(brewer).done(() => {
    ...
});

// brewerService
serviceNamespace.create = function(brewerDto, ajaxParams) {
    return abp.ajax($.extend({
        url: abp.appPath + 'api/services/app/brewer/Create',
        type: 'POST',
        data: JSON.stringify(brewerDto)
    }, ajaxParams));
};

Here is my form

<form name="AddBrewerForm" role="form" novalidate class="form-validation">
    <ul class="nav nav-tabs tab-nav-right" role="tablist">
        <li role="presentation" class="active">
            <a href="#brewer-detail" data-toggle="tab">@L("BrewerDetails")</a>
        </li>

        <li role="presentation">
            <a href="#beers-list" data-toggle="tab">@L("Beers")</a>
        </li>
    </ul>

    <div class="tab-content">
        <div role="tabpanel" class="tab-pane animated fadeIn active" id="brewer-detail">
            <div class="row clearfix" style="margin-top:10px;">
                <div class="col-sm-12">
                    <div class="form-group form-float">
                        <div class="form-line">
                            <input id="name" type="text" name="Name" required class="validate form-control" />
                            <label for="name" class="form-label">Name</label>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div role="tabpanel" class="tab-pane animated fadeIn" id="beers-list">
            <div id="beer-list-content">
                <div class="brewer-beer-card row form-group form-float" style="border: 1px solid black;">
                    <div class="col-md-10">
                        <div class="form-line">
                            <input id="beer-0" type="text" name="Beers[0].Name" required class="validate form-control" />
                            <label for="beer-0" class="dynamic-beer-label form-label">@L("Name")</label>
                       </div>
                    </div>
                </div>

                <div class="brewer-beer-card row form-group form-float" style="border: 1px solid black;">
                    <div class="col-md-10">
                        <div class="form-line">
                            <input id="beer-1" type="text" name="Beers[1].Name" required class="validate form-control" />
                            <label for="beer-1" class="dynamic-beer-label form-label">@L("Name")</label>
                       </div>
                    </div>
                </div>
            </div>

            <div class="row" style="margin-top: 10px;">
                <div class="col-sm-12 ">
                    <button type="button" id="add-beer-button" class="btn btn-primary waves-effect">
                        @L("Add")
                    </button>
                </div>
            </div>
        </div>
    </div>
</form>

ApplicationServiceDto

public class InsertBrewerDto
{
    [Required]
    [StringLength(128)]
    public string Name { get; set; }

    public List<NewBrewerBeerDto> Beers { get; set; }
}

public class NewBrewerBeerDto
{
    [Required]
    public string Name { get; set; }
}

jquery plugin of ABP for serialization

$.fn.serializeFormToObject = function () {
    //serialize to array
    var data = $(this).serializeArray();

    //add also disabled items
    $(':disabled[name]', this).each(function () {
        data.push({ name: this.name, value: $(this).val() });
    });

    //map to object
    var obj = {};
    data.map(function (x) { obj[x.name] = x.value; });

    return obj;
};

I Keep receiving null for my arrays of Beers...

So I've tried to use the $(form).serialize() but i'm getting this error: Your request is not valid!

The following errors were detected during validation.

So there is no error shown...

Here is the Log file WARN 2017-09-30 10:53:13,458 [31 ] nHandling.AbpApiExceptionFilterAttribute - Method arguments are not valid! See ValidationErrors for details. Abp.Runtime.Validation.AbpValidationException: Method arguments are not valid! See ValidationErrors for details. at Abp.Runtime.Validation.Interception.MethodInvocationValidator.ThrowValidationError() at Abp.Runtime.Validation.Interception.MethodInvocationValidator.Validate() at Abp.WebApi.Validation.AbpApiValidationFilter.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.WebApi.Auditing.AbpApiAuditFilter.d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.WebApi.Security.AntiForgery.AbpAntiForgeryApiFilter.d__10.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.WebApi.Authorization.AbpApiAuthorizeFilter.d__7.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.AuthenticationFilterResult.d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ExceptionFilterResult.d__0.MoveNext() WARN 2017-09-30 10:53:13,458 [31 ] nHandling.AbpApiExceptionFilterAttribute - There are 1 validation errors: WARN 2017-09-30 10:53:13,458 [31 ] nHandling.AbpApiExceptionFilterAttribute - (brewerDto)

Javscript Console Error:

{code: 0, message: "Your request is not valid!", details: "The following errors were detected during validation. ↵ - ↵", validationErrors: Array(1)} code : 0 details : "The following errors were detected during validation. ↵ - ↵" message : "Your request is not valid!" validationErrors : Array(1) 0 : members : Array(1) 0 : "brewerDto" length : 1 message : ""

EDIT1

JS for getting console

    var b1 = _$form.serialize();
    var b2 = _$form.serializeArray();
    var b3 = _$form.serializeFormToObject();

    console.log(b1);
    console.log(b2);
    console.log(b3);

b1

Name=123&Beers%5B0%5D.Name=123&Beers%5B1%5D.Name=321

b2

(3) [{…}, {…}, {…}]
0:{name: "Name", value: "123"}
1:{name: "Beers[0].Name", value: "123"}
2:{name: "Beers[1].Name", value: "321"}
length:3

b3

{Name: "123", Beers[0].Name: "123", Beers[1].Name: "321"}
Beers[0].Name :"123"
Beers[1].Name : "321"
Name : "123"

Upvotes: 1

Views: 3739

Answers (2)

aaron
aaron

Reputation: 43073

Try this:

$.fn.serializeFormToObjectWithArraysOfObjects = function () {
    var obj = $(this).serializeFormToObject();
    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        var result = key.match(/(.*)\[(\d)\].(.*)/);
        if (result) {
          var outer = result[1];
          var index = result[2];
          var inner = result[3];
          obj[outer] = obj[outer] || [];
          obj[outer][index] = obj[outer][index] || {};
          obj[outer][index][inner] = obj[key];
          delete obj[key];
        }
      }
    }
    return obj;
};

Output:

{Name: "123", Beers: [{Name: "123"}, {Name: "321"}]}

Upvotes: 2

Felipe Valdes
Felipe Valdes

Reputation: 2217

Have you considered using JSON and iterating over the form inputs manually?

There are many ways to serialise forms into a JSON Object, and they depend on the actual markup and the form names, this example relies on the "id" parameter to be set(and as best practise, make them equal to the field name:

<input name="example" id="example" />

There are other strategies, but I like this one because we can be explicit about what we want to send to the server:

var list_of_fields = ["f1","f2","f3"];

and we can apply simple validation rules here before sending them.

Some example code:

var list_of_fields = ["f1","f2","f3"];
var serialised_values = {}
for(var i=0;i<list_of_fields.length;i++){
    var dom_object = document.getElementById(list_of_fields[i]);
    if(dom_object.tagName == "INPUT" || dom_object.tagName == "TEXTAREA"){
        serialized_values[dom_object.name] = dom_object.value;
    }
    if(dom_object.tagName == "SELECT"){
        serialized_values[dom_object.name] = dom_object.options[dom_object.selectedIndex].value;
    }

    //other field types go here

    //validation can go here too...
}
console.log(serialized_values);

Upvotes: 1

Related Questions