2

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.

Steve
  • 352
  • 2
  • 6
  • 24
  • 3
    Use `@Html.ValidationMessageFor(model => model.Title)` instead. – Orel Eraki Apr 22 '16 at 12:45
  • Is the purpose of this view to display a collection of existing `Asset` and have a form to create a new `Asset`? In which case none of the code in your form will work correctly, especially not the validation since your model does not have properties named `Title`, `Description` etc so there is nothing to validate. –  Apr 22 '16 at 13:20
  • @StephenMuecke this view will display records in table and i have Add Asset option where one modal popup will come to insert the asset. (http://imgur.com/GqImWXR) and (http://imgur.com/NgkLiD7) – Steve Apr 22 '16 at 13:38
  • 1
    OK, Now I understand what your trying to do (and I'm assuming your submitting the form via ajax so you can keep adding and editing?) You need a different approach since your creating form controls that have no relationship to your model and consequently no validation will occur (always, always use the strongly typed `xxxFor()` helper methods). I need some sleep, but I'll add an answer in the morning showing how to fix this and a number of other issues with your code. –  Apr 22 '16 at 13:43
  • But you might want to look at this [DotNetFiddle](https://dotnetfiddle.net/Yy78S3) –  Apr 22 '16 at 13:45
  • @StephenMuecke sure i will wait and i hope you know i'm new to MVC and will not mind my silly mistakes :) – Steve Apr 22 '16 at 13:45
  • @StephenMuecke that fiddle you have mentioned, validations are not working there too – Steve Apr 22 '16 at 13:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/109959/discussion-between-stephen-muecke-and-steve). –  Apr 22 '16 at 21:53

1 Answers1

2

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

Community
  • 1
  • 1
  • DotNetFiddle provided by you..it will create 2 tables for me in my database. but i want only one table for that view. so should i go for Partial views – Steve Apr 25 '16 at 05:43
  • What do you mean _it will create 2 tables for me in my database_? It wont create any tables. It just saves the data from your form into one table and then updates the view with what you saved. Or do you mean its creating a table for `AssetVM`? If so, then `AssetVM` needs to be in a separate folder (say `ViewModels`) not in your data models folder. Its a view model and hos nothing to do with EF and your data models. –  Apr 25 '16 at 05:56
  • is it good practice if i create Partial view for all pages in which i have to show & add records like this page. – Steve Apr 25 '16 at 06:35
  • You can use a partial is you want (but that partial should still be based on a view model, not a data model) –  Apr 25 '16 at 06:38
  • Thank you very much stephen..your suggestions works well. Cant i use the same file upload option which i'm using right now. And also Delete option is going to controller after click on the delete icon but you ssaid it should be Post not Get(didn't get you) – Steve Apr 25 '16 at 10:14
  • You can if your doing a normal submit, but I'm assuming you must be using ajax to be able to add and edit multiple items in the view (otherwise some of your code would not really make sense). –  Apr 25 '16 at 10:21
  • And yes, a Delete action should be a POST (unless your going to a GET method that generates another Confirm Delete view that POST's to another method). Anything that changes data in your database should be a POST. If its a GET, its url is added to the browser history, and a user can just enter the url in the address bar anyway. At best that's going to make an unnecessary call to delete something which does not exist, and at worst it may throw an exception depending on your code. –  Apr 25 '16 at 10:22
  • actually i'm changing the column value after click on delete in db. SO what should be the best case to do that. – Steve Apr 25 '16 at 10:25
  • Again, your changing data (I assume you have a `IsDeleted` or `IsActive` property?) so it should be a POST - just replace your link with `@Html.BeginForm("AssetDelete", "yourController", new { id = item.ID }) { }` (and style the button to look like a link if you want). –  Apr 25 '16 at 10:38
  • are you talking about this link `@Html.ActionLink("Delete", "AssetDelete", new { id = item.ID }, new { @class = "ActionDelete", onclick = "return confirm('Are You Sure delete this record?');", })` which i should replace with what you provide – Steve Apr 25 '16 at 10:55
  • Yes, just replace the `Delete` link with a form (and you can still use javascript to display the confirmation message and cancel the submit if necessary) –  Apr 25 '16 at 10:56
  • oh yes..ya it was displaying url..now working great..so it should be done for Edit downloading also. Thanks a ton man. Genius you are – Steve Apr 25 '16 at 11:08