Search code examples
javascriptc#asp.netasp.net-mvcasp.net-core

Write edited collection to back end (ASP.NET core MVC)


I have a Razor modal that gets data from the BE

Here is model

 public class LocationServiceEditDto: EntityDto<int?>
{
    public int ServiceId { get; set; }

    public int? LocationId { get; set; }

    public decimal? Cost { get; set; }

    public int? MaterialUomId { get; set; }

    public string MaterialUomName { get; set; }

    public string LocationName => Location?.Name;

    [JsonIgnore]
    public  LocationNameDto Location { get; set; }

    public ICollection<LocationServicePriceDto> LocationServicePrices { get; set; }
}

at BE I populate collection of LocationServicePrices

And then in View, I show it like this

<div class="modal-body box-wrap">
    <form role="form" novalidate class="form-validation" name="ServicePriceInformationsForm">
        <input type="hidden" asp-for="Id"/>
        <input type="hidden" asp-for="ServiceId"/>
        <div class="form-group">
            <label class="required-label">@L("Location")</label>
            <select class="form-control" required asp-for="LocationId">
                <option value="">@L("SelectALocation")</option>
                @if (Model.LocationId.HasValue)
                {
                    <option selected value="@Model.LocationId">@Model.LocationName</option>
                }
            </select>
        </div>
        <div class="form-group">
            <label class="required-label">@L("MaterialUom")</label>
            <select class="form-control" asp-for="MaterialUomId" id="ServicePrice_MaterialUomId">
                <option value="">Select an option</option>
                @if (Model.MaterialUomId > 0)
                {
                    <option value="@Model.MaterialUomId">@Model.MaterialUomName</option>
                }
            </select>
        </div>
        <div class="form-group">
            <label>@L("Cost")</label>
            <input class="form-control" type="text" asp-for="Cost" data-rule-number="true" data-rule-min="0" data-rule-max="@AppConsts.MaxDecimalDatabaseLength">
        </div>
        @foreach (var tier in @Model.LocationServicePrices)
        {
            <div class="form-group">
                <label>@tier.PricingTierName</label>
                <input class="form-control" type="text" asp-for="@tier.PricePerUnit" data-rule-number="true" data-rule-min="0" data-rule-max="@AppConsts.MaxDecimalDatabaseLength">
            </div>
        }
    </form>
</div>

I want to update every input in foreach and make @Model.LocationServicePrices updated.

For now, when I enter values and send data to BE, collection is null

I saving data like this in .js file

this.save = function () {
        if (!_$form.valid()) {
            _$form.showValidateMessage();
            return;
        }

        var servicePrice = _$form.serializeFormToObject();

        _modalManager.setBusy(true);
        _serviceService.editLocationService(servicePrice).done(function () {
            abp.notify.info('Saved successfully.');
            _modalManager.close();
            abp.event.trigger('app.createOrEditServicePriceModalSaved');
        }).always(function () {
            _modalManager.setBusy(false);
        });
    };

Code for serializeFormToObject method

 $.fn.serializeFormToObject = function () {
    var $form = $(this);
    var fields = $form.find('[disabled]');
    fields.prop('disabled', false);
    var json = $form.serializeJSON();
    fields.prop('disabled', true);
    return json;
};

How I can fix this?


Solution

  • Not sure how does your whole js code work, but model binding system binds the property by name. If your backend receives form data, due to the LocationServicePrices is a list model, you need pass them by name like:name="LocationServicePrices[@i].PricePerUnit".

    @{
        int i = 0;
    }
    @foreach (var tier in @Model.LocationServicePrices)
    {
        <div class="form-group">
            <label>@tier.PricingTierName</label>
            <input class="form-control" type="text" asp-for="@tier.PricePerUnit" 
                name="LocationServicePrices[@i].PricePerUnit" data-rule-number="true" data-rule-min="0" data-rule-max="@AppConsts.MaxDecimalDatabaseLength">
        </div>
        i++;
    }
    

    If your backend receives json string, the correct json should be:

    { 
    "Id": "", 
    "ServiceId": "2031", 
    "LocationId": "2", 
    "MaterialUomId": "6", 
    "Cost": "123", 
    "LocationServicePrices": [
        { "PricePerUnit": "1"},
        { "PricePerUnit": "2"},
        {"PricePerUnit": "3"}, 
        {"PricePerUnit": "4"}, 
        {"PricePerUnit": "5" }
     ]
    }
    

    UPDATE:

    For how to generate the json string. You could use the js code below:

    @model LocationServiceEditDto
    <div class="modal-body box-wrap">
        <form role="form" novalidate class="form-validation" name="ServicePriceInformationsForm">
            <input type="hidden" asp-for="Id" />
            <input type="hidden" asp-for="ServiceId" />
            <div class="form-group">
                <select class="form-control" required asp-for="LocationId">
                    <option value="">sssss</option>
                    <option value="1">aa</option>
                    <option value="2">bb</option>
                    <option value="3">cc</option>
                </select>
            </div>
            <div class="form-group">
                <select class="form-control" asp-for="MaterialUomId" id="ServicePrice_MaterialUomId">
                    <option value="">Select an option</option>
                    @if (Model.MaterialUomId > 0)
                    {
                        <option value="@Model.MaterialUomId">@Model.MaterialUomName</option>
                    }
                </select>
            </div>
            <div class="form-group">
                <input class="form-control" type="text" asp-for="Cost" data-rule-number="true" data-rule-min="0" data-rule-max="7">
            </div>
            @{
                int i = 0;
            }
            @foreach (var tier in @Model.LocationServicePrices)
            {
                <div class="form-group">
                    <label>@tier.PricingTierName</label>
                    <input class="form-control" type="text" asp-for="@tier.PricePerUnit"
                           name="LocationServicePrices[@i].PricePerUnit" data-rule-number="true" data-rule-min="0" data-rule-max="7">
                </div>
                i++;
            }
        </form>
    </div>
    
    @section Scripts
    {
        <script>
            var formData = {};
            $('form[name="ServicePriceInformationsForm"]').serializeArray().forEach(function (item) {
                formData[item.name] = item.value;
            });
    
            // Convert LocationServicePrices data to JSON array
            formData["LocationServicePrices"] = [];
            $('.form-group input[id$="PricePerUnit"]').each(function () {
                formData["LocationServicePrices"].push({ "PricePerUnit": $(this).val() });
            });
    
            console.log(formData)
            console.log(JSON.stringify(formData))
        </script>
    }