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.
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