Search code examples
c#razorasp.net-mvc-5jquery-inputmask

InputMask Sent Wrong Format Even After Unmasked


Im puzzled with this 2 days already.

I have model for a Total field as decimal

    [Display(Name = "Harga")]
    [Range(0, 999999999999, ErrorMessage = "Harga tidak boleh kurang dari 0")]
    public decimal Total { get; set; }

It's rendered in Razor View like this :

    <div class="col-md-6">
        @Html.LabelFor(model => model.Total)
        @Html.TextBoxFor(model => model.Total, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.Total, "", new { @class = "text-danger" })
    </div>

I use Inputmask like this :

        Inputmask.extendAliases({
            'myNum': {
                alias: 'currency',
                digits: 0,
                digitsOptional: false,      // Make digits required
                integerDigits: 12,          // Max digits before grouping
                prefix: '',                 // No prefix
                groupSeparator: '.',         // Use dot as thousands separator
                radixPoint: ',',            // Use comma as decimal separator
                groupSize: 3,               // Group size for thousands separator
                rightAlign: false,          // Left align
                autoUnmask: true            // Automatically unmask on get
            }
        });

        Inputmask("myNum").mask("#Total");

        $('form').each(function () {
            var $el = $(this);
            $el.data('validator', null);
            $.validator.unobtrusive.parse($el);
        });

And use some custom validator for removing thousand separator

    $.validator.methods.range = function (value, element, param) {
        var globalizedValue = value.replace("", ".");
        return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
    };

    $.validator.methods.number = function (value, element) {
        return this.optional(element) || /-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
    };

This is working perfectly if I have the form in Modal, but in this case, I don't have it in bootstrap modal and it's give an error :

The value '5.000' is not valid for Harga.

If I remove the Inputmask and use it as reguler textbox and put 5000 on it, it's saved without an error. So I assume there is nothing wrong with the controller. I tried to track the value before saved using this :

        document.getElementById('myForm').addEventListener('submit', function (e) {
            var totalField = document.getElementById('Total');
            var rawValue = totalField.value;

            // Clean and format the value before submission
            var cleanedValue = rawValue.replace(/\./g, '').replace(',', '.');
            totalField.value = cleanedValue;

            console.log("Raw Value Before Submission: " + rawValue);
            console.log("Cleaned Value Before Submission: " + cleanedValue);

            // Double-check that the value is correctly updated in the input field
            setTimeout(function () {
                console.log("Final Value Being Sent: " + totalField.value);
            }, 100);
        });

And it give completely right in Log

Cleaned Value Before Submission: 5000
Final Value Being Sent: 5000

But it still return same error.

The value '5.000' is not valid for Harga.

I looked into the header and it's giving 5.000

__RequestVerificationToken=EHhUEqDKh_ay_erHlTmYcu4aCjmlUvFuPjvbFxUD_3goKKGLFTY1XBbinAyvkeqxcMF9aGtv-11R8SrsowaxJt7d6m6jA3hdQ-ufkKSy8EzV_Yy1ZaS-LzFOdHSaT3oJIM6jk-I7u3lRsGkSFe0eow2&Id=1&MasterBusinessUnitId=2&Type=Sale&MasterRegionId=6&Code=0002%2FSF%2FKAR%2FBKS%2FVIII%2F24&Active=true&Active=false&MasterDestinationId=1&MasterCostId=1&Total=5.000

Somehow the value changed after sent to the server but it is correct before submission. I'm still not sure what is caused that, I puzzled with this like forever now.

This is complete Razor View if it helps. Thank You.

@model eShop.Models.ShippingFee

@{
    ViewBag.Title = "Edit Biaya Pengiriman";
    ViewBag.Current = "ShippingFees";
}

@section Styles {
    @Styles.Render("~/style/mvcgrid")
    @Styles.Render("~/style/MvcDatalist")
    @Styles.Render("~/style/Select2")
}

<style>
    .tablegrid-hover tbody tr:hover {
        background-color: rgba(0, 0, 0, 0.1);
    }
</style>

@Html.Partial("MvcDatalist/_Dialog")

<div class="content-wrapper">
    <section class="content">
        <div class="row">
            <div class="col-md-4">
                <div class="box box-primary">
                    <div class="box-header with-border">
                        <i class="fa fa-plus"></i>
                        <h3 class="box-title">Edit - Biaya Pengiriman</h3>
                    </div>
                    <!-- /.box-header -->
                    <div class="box-body">
                        <form id="myForm" action="@Url.Action("Edit", "ShippingFees")" method="post">

                            @Html.AntiForgeryToken()
                            @Html.HiddenFor(model => model.Id)
                            <input type="hidden" id="MasterBusinessUnitId" name="MasterBusinessUnitId" value="@ViewBag.MasterBusinessUnitId" />
                            <input type="hidden" id="Type" name="Type" value="@eShop.Models.EnumCostType.Sale" />
                            <div class="form-horizontal">
                                @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                                <div class="form-group">
                                    <div class="col-md-12">
                                        @Html.LabelFor(model => model.MasterRegionId)
                                        @Html.DatalistFor(model => model.MasterRegionId, new eShop.Models.MasterRegionDatalist(), new { @class = "datalist2" })
                                        @Html.ValidationMessageFor(model => model.MasterRegionId, "", new { @class = "text-danger" })
                                    </div>
                                </div>
                                <div class="form-group">
                                    <div class="col-md-10">
                                        @Html.LabelFor(model => model.Code)
                                        @Html.EditorFor(model => model.Code, new { htmlAttributes = new { @class = "form-control", @style = "text-transform:uppercase" } })
                                        @Html.ValidationMessageFor(model => model.Code, "", new { @class = "text-danger" })
                                    </div>
                                    <div class="col-md-2 text-center">
                                        @Html.LabelFor(model => model.Active)
                                        <div class="checkbox text-center">
                                            @Html.EditorFor(model => model.Active)
                                            @Html.ValidationMessageFor(model => model.Active, "", new { @class = "text-danger" })
                                        </div>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <div class="col-md-12">
                                        @Html.LabelFor(model => model.MasterDestinationId)
                                        @Html.DatalistFor(model => model.MasterDestinationId, new eShop.Models.MasterDestinationUnitBusinessDatalist())
                                        @Html.ValidationMessageFor(model => model.MasterDestinationId, "", new { @class = "text-danger" })
                                    </div>
                                </div>
                                <div class="form-group">
                                    <div class="col-md-6">
                                        @Html.LabelFor(model => model.MasterCostId)
                                        @Html.DatalistFor(model => model.MasterCostId, new eShop.Models.MasterCostDatalist())
                                        @Html.ValidationMessageFor(model => model.MasterCostId, "", new { @class = "text-danger" })
                                    </div>
                                    <div class="col-md-6">
                                        @Html.LabelFor(model => model.Total)
                                        @Html.TextBoxFor(model => model.Total, new { @class = "form-control" })
                                        @Html.ValidationMessageFor(model => model.Total, "", new { @class = "text-danger" })
                                    </div>
                                </div>
                                <div class="form-group">
                                    <div class="col-md-12">
                                        <div class="col-md-6 text-left">
                                            <a href="#" id="cancel" class="btn btn-danger" style="margin-top:10px">
                                                Cancel
                                            </a>
                                        </div>
                                        <div class="col-md-6 text-right">
                                            <input type="submit" value="Save" class="btn btn-primary" style="margin-top:10px" />
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>


            <div class="col-md-8">
                <div class="nav-tabs-custom">
                    <ul class="nav nav-tabs">
                        <li class="active"><a href="#tab_2" data-toggle="tab">Customer</a></li>
                    </ul>
                    <div class="tab-content">
                        <div class="tab-pane active form-horizontal" id="tab_2">
                            <div class="text-right col-md-12">
                                <button id="btnAdd" class="btn btn-success mx-1" href="@Url.Action("DetailsCreate", "ShippingFees")">
                                    <i class="fa fa-plus"></i> Add Customer
                                </button>
                                <button id="DetailsBatchDelete" class="btn btn-danger mx-1">
                                    <i class="fa fa-trash"></i> Delete
                                </button>
                            </div>

                            <div class="form-group">
                                <div class="box-body" style="overflow:auto; white-space: nowrap">
                                    @Html.AjaxGrid(Url.Action("DetailsGrid", routeValues: new { Id = Html.ValueFor(model => model.Id) }), new { id = "detail-table" })
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
</div>
<div class="modal fade" id="detailsModal" role="dialog" aria-hidden="true" data-backdrop="static">
    <div id="detailsContainer">
    </div>
</div>

@section Scripts {
    @Scripts.Render("~/bundles/mvcgrid")
    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/script/Select2")
    @Scripts.Render("~/bundles/unobtrusive")
    @Scripts.Render("~/bundles/MvcDatalist")
    @Scripts.Render("~/script/tableHeadFixer")
    @Scripts.Render("~/script/InputMask")

    <script>
        $.validator.methods.range = function (value, element, param) {
            var globalizedValue = value.replace("", ".");
            return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
        };

        $.validator.methods.number = function (value, element) {
            return this.optional(element) || /-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
        };

        [].forEach.call(document.getElementsByClassName('datalist'), function (element) {
            new MvcDatalist(element);
        });

        $('.select2').select2();

        [].forEach.call(document.getElementsByClassName('mvc-grid'), function (element) {
            new MvcGrid(element);
        });

        $('#btnAdd').click(function (event) {
            event.preventDefault();
            if ($("#MasterRegionId").val() === "") {
                bootbox.alert("Wilayah belum dipilih!");
            }
            else {
                var url = $(this).attr("href") + "?shippingFeeId=" + $("input#Id").val();
                $.get(url, function (data) {
                    $('#detailsContainer').html(data);
                    $('#detailsModal').modal('show');

                    [].forEach.call(document.getElementsByClassName('datalist2'), function (element) {
                        new MvcDatalist(element);
                    });

                    $('form').each(function () {
                        var $el = $(this);
                        $el.data('validator', null);
                        $.validator.unobtrusive.parse($el);
                    });

                });
            }
        });

        function CreateSuccess(data) {
            if (data !== "success") {
                $('#detailsContainer').html(data);
                return;
            }
            $('#detailsModal').modal('hide');
            $('#detailsContainer').html("");

            var grid = new MvcGrid(document.querySelector('#detail-table'));
            grid.reload();
        }

        function EditSuccess(data) {
            if (data !== "success") {
                $('#detailsContainer').html(data);
                return;
            }
            $('#detailsModal').modal('hide');
            $('#detailsContainer').html("");

            var grid = new MvcGrid(document.querySelector('#detail-table'));
            grid.reload();

        }

        var token = $('input[name="__RequestVerificationToken"]').val();
        var headers = {};
        headers['__RequestVerificationToken'] = token;

        $(document).ready(function ()
        {
            Inputmask.extendAliases({
                'myNum': {
                    alias: 'currency',
                    digits: 0,
                    digitsOptional: false,      // Make digits required
                    integerDigits: 12,          // Max digits before grouping
                    prefix: '',                 // No prefix
                    groupSeparator: '.',         // Use dot as thousands separator
                    radixPoint: ',',            // Use comma as decimal separator
                    groupSize: 3,               // Group size for thousands separator
                    rightAlign: false,          // Left align
                    autoUnmask: true            // Automatically unmask on get
                }
            });

            Inputmask("myNum").mask("#Total");

            $('form').each(function () {
                var $el = $(this);
                $el.data('validator', null);
                $.validator.unobtrusive.parse($el);
            });

            document.getElementById('myForm').addEventListener('submit', function (e) {
                var totalField = document.getElementById('Total');
                var rawValue = totalField.value;

                // Clean and format the value before submission
                var cleanedValue = rawValue.replace(/\./g, '').replace(',', '.');
                totalField.value = cleanedValue;

                console.log("Raw Value Before Submission: " + rawValue);
                console.log("Cleaned Value Before Submission: " + cleanedValue);

                // Double-check that the value is correctly updated in the input field
                setTimeout(function () {
                    console.log("Final Value Being Sent: " + totalField.value);
                }, 100);
            });

            $("#DetailsBatchDelete").click(function () {
                var selectedIDs = new Array();
                $('#detail-table tbody tr').find('td:first :checkbox').each(function () {
                    if ($(this).prop('checked')) {
                        selectedIDs.push($(this).val());
                    }
                });

                if (selectedIDs.length <= 0) {
                    bootbox.alert("Pilih salah satu data yang akan dihapus.");
                }
                else {
                    bootbox.confirm("Apakah anda yakin akan menghapus " + selectedIDs.length + " data?", function (result) {
                        if (result) {
                            var options = {};
                            options.url = "/ShippingFees/DetailsBatchDelete";
                            options.type = "POST";
                            options.headers = headers;
                            options.data = JSON.stringify(selectedIDs);
                            options.contentType = "application/json";
                            options.dataType = "json";
                            options.success = function (msg) {
                                bootbox.alert(msg);

                                var grid = new MvcGrid(document.querySelector('#detail-table'));
                                grid.reload();
                            };
                            options.error = function () {
                                bootbox.alert("Terjadi kesalahan saat menghapus.");
                            };
                            $.ajax(options);
                        }
                    });
                }
            });

            $("#cancel").click(function () {
                var data = { id: $("input#Id").val() };
                var options = {};
                options.url = "/ShippingFees/Cancel";
                options.type = "POST";
                options.headers = headers;
                options.data = JSON.stringify(data);
                options.contentType = "application/json";
                options.dataType = "json";
                options.success = function (msg) {
                    window.location.href = '@Url.Action("Index", "ShippingFees")';
                };
                options.error = function () {
                    window.location.href = '@Url.Action("Index", "ShippingFees")';
                };
                $.ajax(options);
            });
        });

        document.addEventListener('reloadend', function (e) {
            if (e.detail.grid.element.id == 'detail-table') {
                $('#CheckAll').click(function () {
                    var checkedStatus = this.checked;
                    $('#detail-table tbody tr').find('td:first :checkbox').each(function () {
                        $(this).prop('checked', checkedStatus);
                    });
                });

                $('#detail-table').on("click", ".edit-data", function (event) {
                    event.preventDefault();
                    var url = $(this).attr("href");
                    $.get(url, function (data) {
                        $('#detailsContainer').html(data);
                        $('#detailsModal').modal('show');

                        [].forEach.call(document.getElementsByClassName('datalist2'), function (element) {
                            new MvcDatalist(element);
                        });

                        $('form').each(function () {
                            var $el = $(this);
                            $el.data('validator', null);
                            $.validator.unobtrusive.parse($el);
                        });
                    });
                });

                $('.fix-table').tableHeadFixer({ left: 2 });
            }
        });
    </script>
}

And this is the controller :

    // POST: MasterItems/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    [Authorize(Roles = "ShippingFeesEdit")]
    public ActionResult Edit([Bind(Include = "Id,Code,MasterRegionId,MasterCostId,MasterDestinationId,Total,Notes,Active,Created,Updated,UserId")] ShippingFee obj)
    {
        obj.Updated = DateTime.Now;
        obj.UserId = User.Identity.GetUserId<int>();

        var errors = ModelState.Values.SelectMany(v => v.Errors);
        foreach (var error in errors)
        {
            Console.WriteLine(error.ErrorMessage); // Log errors to console
        }

        var masterRegionIds = db.Users.Find(User.Identity.GetUserId<int>())
            .MasterRegions.Select(x => x.Id).Distinct().ToList();

        if (masterRegionIds.Count > 0 && masterRegionIds.Contains(obj.MasterRegionId))
        {
            if (ModelState.IsValid)
            {
                obj = GetModelState(obj);
            }

            if (ModelState.IsValid)
            {
                if (!string.IsNullOrEmpty(obj.Notes)) obj.Notes = obj.Notes.ToUpper();

                db.Entry(obj).State = EntityState.Unchanged;
                db.Entry(obj).Property("Code").IsModified = true;
                db.Entry(obj).Property("Notes").IsModified = true;
                db.Entry(obj).Property("MasterRegionId").IsModified = true;
                db.Entry(obj).Property("MasterCostId").IsModified = true;
                db.Entry(obj).Property("MasterDestinationId").IsModified = true;
                db.Entry(obj).Property("Total").IsModified = true;
                db.Entry(obj).Property("Active").IsModified = true;
                db.Entry(obj).Property("Updated").IsModified = true;
                db.Entry(obj).Property("UserId").IsModified = true;

                using (DbContextTransaction dbTran = db.Database.BeginTransaction())
                {
                    try
                    {
                        db.SaveChanges();

                        db.SystemLogs.Add(new SystemLog { Date = DateTime.Now, MenuType = EnumMenuType.ShippingFees, MenuId = obj.Id, MenuCode = obj.Code, Actions = EnumActions.EDIT, UserId = User.Identity.GetUserId<int>() });
                        db.SaveChanges();

                        dbTran.Commit();

                        return RedirectToAction("Index");
                    }
                    catch (DbEntityValidationException ex)
                    {
                        dbTran.Rollback();
                        throw ex;
                    }
                }
            }

            MasterRegion masterRegion = db.MasterRegions.Find(obj.MasterRegionId);

            ViewBag.MasterBusinessUnitId = masterRegion.MasterBusinessUnitId;
            return View("../Selling/ShippingFees/Edit", obj);
        }

        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

I tried with manually post the form using this :

        $("form").submit(function (e) {
            e.preventDefault();

            var rawValue = $("#Total").inputmask('unmaskedvalue');
            rawValue = rawValue.replace(/\./g, '');
            $("#Total").val(rawValue);

            $.ajax({
                type: 'POST',
                url: $(this).attr('action'),
                data: $(this).serialize(),
                success: function (response) {
                    window.location.href = '@Url.Action("Index", "ShippingFees")';
                },
                error: function (xhr, status, error) {
                    alert("An error occurred. Please try again.");
                }
            });
        });

And it's saved succesfully without an error. But I can't use it for some reason because the ActionResult have some validation and different return in other controller.


Solution

  • The InputMask plugin retains the mask that has been set on form submission by default . Try setting the removeMaskOnSubmit property to true when initializing InputMask in your $(document).ready() function:

            Inputmask.extendAliases({
            'myNum': {
                (...)
                removeMaskOnSubmit: true
            }
        });
    

    Refer to the InputMask plugin's documentation for more info