Search code examples
asp.net-mvcvalidationclient-side-validation

Validation message is not showing on client side


Here is my Model

public partial class Asset
{
    public long ID { get; set; }
    [RegularExpression("^[0-9]*$", ErrorMessage = "Title must be numeric")]
    public string Title { get; set; }
    public string Description { get; set; }
}

and in my view

<div class="Content-inner-pages">
<div class="TopHeading TopHeading2">
    <h2>Assets</h2>
    @* @Html.ActionLink("Create", "Create")*@
    <a class="CreateBtn AssetsBtn" href="Javascript:void(0);" onclick="javascript: HideUpdateButton();">Add Asset</a>
    <div class="clearfix"></div>
</div>
<input type="hidden" id="hdnIsNew" value="1" />
<input type="hidden" id="hdnRecId" />
<!-- Slide Popup panel -->
<div class="cd-panel from-right AddAssetForm">
    <header class="cd-panel-header">
        <h3>Add Asset</h3>
        <a href="javascript:void(0);" onclick="javascript: DisplayClear();" class="cd-panel-close">Close</a>
    </header>
    <div class="cd-panel-container">
        <div class="cd-panel-content">
            <!-- Add Reminder -->
            <div class="form-horizontal form-details popup-box">
                @using (Html.BeginForm("AssetsPage", "SuperAdmin", FormMethod.Post, new { enctype = "multipart/form-data" }))
                {
                    <div class="form-group">
                        <label class="col-md-5 control-label">
                            Asset Title
                        </label>
                        @Html.TextArea("ID", "", new { @class = "form-control", @id = "ID", @style = "display:none;" })
                        <div class="col-md-7">
                            @Html.TextBox("Title", "", new { @class = "form-control", @id = "Title", required = "required" })
                            @Html.ValidationMessage("Title", "*")

                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-md-5 control-label">Description</label>
                        <div class="col-md-7">
                            @Html.TextArea("Description", "", new { @class = "form-control", @id = "Description", required = "required" })
                            @Html.ValidationMessage("Description", "*")
                        </div>
                    </div>

                    <div class="form-group">
                        <label class="col-md-5 control-label">Attachment</label>
                        <div class="col-md-7">
                            <input type="file" name="file" id="filena" class="custom-file-input" required="required">
                            @Html.ValidationMessage("file", "*")

                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-md-7 col-md-offset-5">

                            <input type="submit" id="SaveBtn" value="Save" name="actiontype" class="btn-class btn-success">
                            <input type="submit" id="UpdateBtn" value="Update" name="actiontype" class="btn-class btn-success">

                        </div>
                    </div>
                }
            </div><!-- End Add Reminder -->

        </div> <!-- cd-panel-content -->
    </div> <!-- cd-panel-container -->
</div> <!-- cd-panel -->

<div class="box">
    <div class="box-content Custom-DataTable">
        <table id="AdministationAssets" class="table table-hover dt-responsive CustomDatable AdministationAssetsTable" cellspacing="0" width="100%">
            <thead>
                <tr>
                    <th style="width:5%;">Assets</th>
                    <th style="width:15%;">
                        @Html.DisplayNameFor(model => model.Title)
                    </th>
                    <th style="width:50%;">
                        @Html.DisplayNameFor(model => model.Description)
                    </th>
                    <th style="width:8%;">Options</th>
                </tr>
            </thead>
            <tbody>

                @foreach (var item in Model)
                {
                    <tr>
                        <td id="target" class="">
                            @{
                    switch (item.Extenstion.ToLower())
                    {
                        case "doc":
                            <i class="fa fa-file-word-o text-primary AssetIcon"></i>
                            break;
                        case "docx":
                        <i class="fa fa-file-word-o text-primary AssetIcon"></i>
                            break;
                        case "xls":
                        <i class="fa fa-file-excel-o text-success AssetIcon"></i>
                            break;
                        case "xlsx":
                        <i class="fa fa-file-excel-o text-success AssetIcon"></i>
                            break;
                        case "ppt":
                        <i class="fa fa-file-powerpoint-o text-danger AssetIcon"></i>
                            break;
                        case "jpg":
                        <i class="fa fa-file-photo-o text-warning AssetIcon"></i>
                            break;
                        case "png":
                        <i class="fa fa-file-photo-o text-warning AssetIcon"></i>
                            break;
                        case "pdf":
                        <i class="fa fa-file-pdf-o text-danger AssetIcon"></i>
                            break;
                        case "zip":
                        <i class="fa fa-file-archive-o text-muted AssetIcon"></i>
                            break;
                        case "htm":
                        <i class="fa fa-file-code-o text-info AssetIcon"></i>
                            break;
                        case "txt":
                        <i class="fa  fa-file-text-o text-info AssetIcon"></i>
                            break;
                        case "mov":
                        <i class="fa  fa-file-movie-o text-warning AssetIcon"></i>
                            break;
                        case "mp3":
                        <i class="fa fa-file-audio-o text-warning AssetIcon"></i>
                            break;

                        default:
                        <i class="fa fa-file AssetIcon"></i>
                            break;
                    }
                            }
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Description)
                        </td>
                        <td>
                            @Html.ActionLink("Download", "DownloadAsset", new { id = item.ID }, new { @class = "ActionInvoice" })
                            @Html.ActionLink("Edit", "AddEditRecord", new { id = item.ID }, new { @class = "ActionEdit AssetEdit", onclick = "javascript:GetEditDetails(" + item.ID + ")" })
                            @Html.ActionLink("Delete", "AssetDelete", new { id = item.ID }, new { @class = "ActionDelete", onclick = "return confirm('Are You Sure delete this record?');", })

                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
</div>

the thing is validation is happening for Required field and Regular expression numeric number but error message is not being display for Regular Expression as i want to show that error: Title must be numeric. please let me know where i am doing wrong here while applying validation.


Solution

  • The reason you do not get any validation is that the model in your view is

    @model IEnumerable<Asset>
    

    and you generating inputs for properties which do not exist in the model

    @Html.TextBox("Title")
    

    creates <input name="Title" id = "Title" value="" /> but IEnumerable<Asset> does not have a property named Title so no data-val-* attributes are generated and therefore no rules are added to the $.validator to generate client side validation.

    Note that the only validation your getting is as a result of adding the new { required = "required" } attribute which is HTML-5 validation only and will not give you the essential server side validation.

    You can solve this by creating a view model

    public class AssetVM
    {
        public long? ID { get; set; }
        [Required(ErrorMessage = "Please enter a title")]
        [RegularExpression("^[0-9]*$", ErrorMessage = "Title must be numeric")]
        public string Title { get; set; }
        [Required(ErrorMessage = "Please enter a description")]
        public string Description { get; set; }
        public IEnumerable<Asset> Assets { get; set; }
    }
    

    and in the controller, initialize a new AssetVM and populate the Assets property with the collection and return it to the view.

    var model = new AssetVM()
    {
        Assets = .... // your query
    };
    return View(model);
    

    and in the view

    @model AssestVM
    ....
    @using (Html.BeginForm("AssetsPage", "SuperAdmin", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        @Html.HiddenFor(m => m.ID)
        ....
        @Html.TextBoxFor(m => m.Title, new { @class = "form-control"})
        @Html.ValidationMessage(m => m.Title)
        ....
    }
    ....
    @foreach(var asset in Model.Assets)
    {
        // build your table
    }
    

    Another alternative would be to keep your existing @model IEnumerable<Asset> and create a partial view that returns your form for an Asset and in the main view and then use @Html.Partial("_Asset", new Asset() ) to generate the form in the main view.

    Side notes:

    1. Use @Html.HiddenFor() to generate the input for the ID, not a textarea styled as hidden
    2. There is no need to use new { id = "###" } - the HtmlHelper methods already add the id attribute and your just overwriting the value with the same value
    3. Remove the new { required = "required" } attribute Use Unobtrusive Javascript rather than polluting your markup with behavior
    4. Since you are also uploading a file, the view model should also contain a property public HttpPostedFileBase File { get; set; } and in the form @Html.TextBoxFor(m => m.File, new { type = "file" }). You should also consider a property for the file's display name so that can be output in the view
    5. Consider adding a property to the view model for the class name to avoid the ugly switch statement in the view (set in in the controller)
    6. A Delete action should be a POST, not a GET

    For a working example of how this could be implemented, refer to this DotNetFiddle, although in your case, because you are also uploading a file, you would need to post the form using FormData as described in this answer