Question

I have a form which has a ViewModel class underlaying it. I have set the [Required] validation on fields of the form. The form still submits even though it does for a moment - before it submits - displays the ErrorMessage set on particular fields.

There is no validation on the StockReceipt model itself, but only the ViewModel. I looked at this question here and learnt that validating the model is optional.

I wonder why the form still submits when there are invalid input?

Here is my View and ViewModel code:

View:

@using (Html.BeginForm("SaveSettings", "StockReceipt", FormMethod.Post,
    new { id = "StockReceiptForm", enctype = "multipart/form-data" }))
{
    <fieldset>
        <div>
            @* @Html.ValidationSummary(false)*@
            <legend>Stock-Receipt Details</legend>

            @*@if (User.IsInRole(Constants.Super))
                {*@

            <div style="display: none;">
                Delivery Note Ref
            </div>
            <div style="display: none;">
                @Html.TextBoxFor(model => model.StockReceiptID, new { @Class = "k-textbox" })
            </div>


            <div class="editor-label">
                Supplier
            </div>
            <div class="editor-field">
                @Html.Kendo().DropDownListFor(model => model.SupplierID).BindTo(Model.SuppliersList).DataTextField("Name").DataValueField("SupplierID").OptionLabel("Select")
                @Html.ValidationMessageFor(model => model.SupplierID)
            </div>

            <div class="editor-label">
                Material
            </div>
            <div class="editor-field">
                @Html.Kendo().DropDownListFor(model => model.MaterialID).BindTo(Model.MaterialsList).DataTextField("Name").DataValueField("MaterialID").OptionLabel("Select")
                @Html.ValidationMessageFor(model => model.MaterialID)
            </div>

            <div class="editor-label">
                Qty
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.Quantity, new { @Class = "k-textbox" })
                @Html.ValidationMessageFor(model => model.Quantity)
            </div>

            <div class="editor-label">
                Of which reserved
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.QuantityReserved, new { @Class = "k-textbox" })
            </div>

            <div class="editor-label">
                Units
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.NumberOfUnits, new { @Class = "k-textbox" })
                @(Html.Kendo().DropDownListFor(model => model.StockUnitsEnum).Name("StockUnitsEnum")
              .BindTo(Enum.GetValues(typeof(StockUnitsEnum))
                  .Cast<StockUnitsEnum>()
                  .Select(p => new SelectListItem
                  {
                      Text = p.ToString(),
                      Value = ((int)p).ToString(CultureInfo.InvariantCulture)
                  })
                  .ToList())
                )

                @Html.ValidationMessageFor(model => model.NumberOfUnits)
            </div>

            <div class="editor-label">
                Batch Reference:
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.BatchReference, new { @Class = "k-textbox" })
                @Html.ValidationMessageFor(model => model.BatchReference)
            </div>

            <div class="editor-label">
                Slab Width
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.SlabWidth, new { @Class = "k-textbox" }) x @Html.TextBoxFor(model => model.SlabHeight, new { @Class = "k-textbox" })
            </div>

            <div class="editor-label">
                Include Transport:
            </div>
            <div class="editor-field">
                @Html.CheckBoxFor(model => model.IncludeTransport)
            </div>

            <div class="editor-label">
                Notes
            </div>
            <div class="editor-field">
                @Html.TextAreaFor(model => model.Notes, new { @Class = "k-textbox" })
            </div>

            <div class="clear">
                Totals
            </div>
            <div class="editor-label">
                Unit Cost
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.UnitCost, new { @Class = "k-textbox" })
                @Html.ValidationMessageFor(model => model.UnitCost)
            </div>

            <div class="editor-label">
                Units
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.NumberOfUnits, new { @Class = "k-textbox" })
                @Html.ValidationMessageFor(model => model.NumberOfUnits)
            </div>

            <div class="editor-label">
                Slab Cost
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.SlabCost, new { @Class = "k-textbox" })
                @Html.ValidationMessageFor(model => model.SlabCost)
            </div>

            <div class="editor-label">
                Location
            </div>
            <div class="editor-field">
                @Html.Kendo().DropDownListFor(model => model.LocationID).BindTo(Model.LocationsList).DataTextField("Name").DataValueField("LocationID").OptionLabel("Select")
            </div>

            <div class="editor-label">
                Purchase-Order Ref.
            </div>
            <div class="editor-field">
                @Html.Kendo().DropDownListFor(model => model.PurchaseOrderID).BindTo(Model.PurchaseOrdersList).DataTextField("PONumber").DataValueField("PurchaseOrderID").OptionLabel("Select")
                @Html.ValidationMessageFor(model => model.PurchaseOrdersList)
            </div>

            <div class="editor-label">
                Invoice Ref.
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.InvoicNo, new { @Class = "k-textbox" })
                @Html.ValidationMessageFor(model => model.InvoicNo)
            </div>
            <br />
            <div class="editor-label">
            </div>
            <div class="editor-field">
                <input type="submit" value="Save" class="k-button" />
            </div>
        </div>
    </fieldset>
}

ViewModel:

public class StockReceiptViewModel
{
    public int StockReceiptID { get; set; }

    [Required(ErrorMessage = "Required")]
    public int SupplierID { get; set; }

    [Required(ErrorMessage = "Required")]
    public int MaterialID { get; set; }

    [Required(ErrorMessage = "Required")]
    public DateTime? ReceiptDate { get; set; }

    [Required(ErrorMessage = "Required")]
    public int Quantity { get; set; }

    public int? QuantityReserved { get; set; }

    [Required(ErrorMessage = "Required")]
    public decimal? SlabWidth { get; set; }

    [Required(ErrorMessage = "Required")]
    public decimal? SlabHeight { get; set; }

    [Required(ErrorMessage = "Required")]
    public int SizeUnits { get; set; }

    [Required(ErrorMessage = "Required")]
    public StockUnitsEnum StockUnitsEnum
    {
        get {return (StockUnitsEnum)SizeUnits;}
        set {SizeUnits = (int)value;}
    }

    [Required(ErrorMessage = "Required")]
    public string BatchReference { get; set; }

    [Required(ErrorMessage = "Required")]
    [DataType(DataType.Currency)]
    public decimal? UnitCost { get; set; }
    [Required(ErrorMessage = "Required")]
    public int? NumberOfUnits { get; set; }
    [Required(ErrorMessage = "Required.")]
    public int PurchaseOrderID { get; set; }
    [Required(ErrorMessage = "Required")]
    public string InvoicNo { get; set; }
    [Required(ErrorMessage = "Required")]
    public decimal SlabCost { get; set; }

    public bool IncludeTransport { get; set; }

    [Required(ErrorMessage = "Required.")]
    public int LocationID { get; set; }

    public int? EnteredBy { get; set; }
    public DateTime OnSystemFrom { get; set; }

    public string Notes { get; set; }

    public List<SupplierViewModel> SuppliersList { get; set; }
    public List<MaterialViewModel> MaterialsList { get; set; }
    public List<LocationsViewModel> LocationsList { get; set; }
    public List<PurchaseOrderViewModel>  PurchaseOrdersList { get; set; }

    public int LastModifiedBy { get; set; }
    public DateTime LastModifiedDate { get; set; }

    public int LiveQuantity { get; set; }

}

The Controller method has the ModelState.Isvalid check as well.

Please help if you can.

Many thanks.

Was it helpful?

Solution 2

I have solved this problem by making sure that the method to where this form is submitting, is returning the viewmodel to the page (view) if ModelState.IsValid is false.

This is provided that the viewModel being returned is actually the same as the submitted one:

   [HttpPost]
    public ActionResult SaveSettings(StockReceiptViewModel stockReceiptVm)
    {
        try
        {
            if (ModelState.IsValid)
            {
                var stockReceipt = new StockReceipt();

                if (stockReceiptVm.StockReceiptID != 0)
                {
                        MapViewModelToModel(stockReceiptVm, stockReceipt);
                        stockReceipt.LastModifiedBy = UserHelper.GetCurrentUserIDByEmail();
                        stockReceipt.LastModifiedDate = DateTime.Now;
                        //update
                        _stockReceiptRepository.UpdateStockReceipt(stockReceipt, stockReceiptVm.StockReceiptID);                        
                }
                else
                {
                        MapViewModelToModel(stockReceiptVm, stockReceipt);
                        stockReceipt.EnteredBy = UserHelper.GetCurrentUserIDByEmail();
                        stockReceipt.OnSystemFrom = Utilities.RemoveTimeFromDate(DateTime.Now);
                            //save new
                        _stockReceiptRepository.InsertStockReceipt(stockReceipt);
                    }

                    return RedirectToAction("Index", "StockReceiptsGrid");                    
            }

            SetupViewDropdownLists(stockReceiptVm);
            return View("Index", stockReceiptVm);
        }
        catch (Exception exc)
        {
            ErrorHelper.WriteToEventLog(exc);
            return RedirectToAction("Index", "Error");
        }
    }

OTHER TIPS

You need to add jquery.unobtrusive and jquery.validate files to your views. Add this to your BundleConfig.cs:

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.unobtrusive*",
                        "~/Scripts/jquery.validate*"));

And then render it on your _Layout View or in View you want validation:

@Scripts.Render("~/bundles/jqueryval")

If you are using default MVC template you should already have this bundle. Just make sure you have reference in your Views. You can check if your js files are loaded by using some web development tools like Firebug in Mozzila or press F12 in Chrome, and in NET tab you can see loaded scripts.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top