Client validation not showing message - asp.net-mvc

I have a MVC4 internet application with a form for creating user accounts. The form validation works but while the input fails validation no error message is displayed. It still prevents submitting until the validation problem is solved but there is no text
Razor View Form
<h2>Create New Account</h2>
<fieldset>
<legend></legend>
#using (Html.BeginForm("CreateUser",null)){
#Html.AntiForgeryToken()
<table class="create">
<tr>
<td colspan="2"><b>New Account</b>
</tr>
<tr>
<td>#Html.DisplayNameFor(model=>model.UserName)</td><td>#Html.TextBoxFor(model=>model.UserName)</td>
<td>#Html.DisplayNameFor(model=>model.EmailAddress)</td><td>#Html.TextBoxFor(model=>model.EmailAddress)</td>
<td><input type="submit" value="Create User" /></td>
</tr>
</table>
}
</fieldset>
#Html.ValidationSummary()
The bundles used include the validation files
bundles.Add(new ScriptBundle("~/bundles/asset").Include(
"~/Scripts/jquery-{version}.js",
"~/Scripts/jquery-ui-{version}.js",
"~/Scripts/jquery.validate*",
"~/Scripts/jquery.unobtrusive*"));
The Model used is an entity model, I have added a partial class to annotate the validation requirements
[MetadataType(typeof(UserProfileMetadata))]
public partial class UserProfile
{
//Empty Class just required for adding class level attribute
}
public class UserProfileMetadata
{
//Fields from user profile requiring annotations
[EmailAddress]
[Required]
[Display(Name = "Email Address")]
public string EmailAddress { get; set; }
[Required]
public string UserName { get; set; }
}
The validation working but now showing the message makes me think it must be a markup error but I just can't see it.

Moving the ValidationSummary inside the form will fix it.
<h2>Create New Account</h2>
<fieldset>
<legend></legend>
#using (Html.BeginForm("CreateUser",null)){
#Html.ValidationSummary()
#Html.AntiForgeryToken()
<table class="create">
<tr>
<td colspan="2"><b>New Account</b>
</tr>
<tr>
<td>#Html.DisplayNameFor(model=>model.UserName)</td> <td>#Html.TextBoxFor(model=>model.UserName)</td>
<td>#Html.DisplayNameFor(model=>model.EmailAddress)</td><td>#Html.TextBoxFor(model=>model.EmailAddress)</td>
<td><input type="submit" value="Create User" /></td>
</tr>
</table>
}
</fieldset>

For anyone stumbling across this who does not wish to be restricted to moving the #Html.ValidationSummary into the form, i.e. it lives in _Layout.cshtml, and you like it there, here's a way around that.
The below method is apparently what is used by Microsoft to populate #Html.ValidationSummary. In it's standard form, it looks for data-valmsg-summary-true in $('this'). this in this case is the calling form. Well, my #Html.ValidationSummary lives in the pageBody <div> on _Layout.cshtml to keep it DRY.
function onErrors(event, validator) { // '#pageBody' is the containing element
var container = $('#pageBody').find("[data-valmsg-summary=true]"),
list = container.find("ul"); if (list && list.length && validator.errorList.length) {
list.empty(); container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
$.each(validator.errorList, function () {
$("<li />").html(this.message).appendTo(list);
});
}
}
So far, I've only changed this:
var container = $('this').find("[data-valmsg-summary=true]")
to this:
var container = $('#pageBody').find("[data-valmsg-summary=true]")
Now, I trigger my validation from a button click. To get onErrors(event, validator) to fire, I used the following jQuery:
$('#btnSave').click(function () {
if ($('form').valid()) {
// Do stuff
} else {
onErrors(event, $('form').data('validator'));
}
});
Voila, #Html.ValidationSummary populates even when using jQuery.unobtrusive.
A big thanks to Leniel Macaferi for pointing me in the right direction here:
http://www.leniel.net/2013/08/customizing-aspnet-mvc-html-validation-summary-with-bootstrap-3-panel.html#sthash.jGRguVuV.qSjYUlhS.dpbs

Related

.NET MVC: Removing items from List in model

So here's my problem. I've been trying to solve this for weeks and nothing, so I'm biting the bullet and asking for help.
Basically, I'm trying to write a computer encyclopedia-cum-inventory management-cum-auditing program. Seeing that MVC is all the rage these days I decided to step out of my comfort zone of classic .NET and try MVC.
I have a model with partially the following fields:
public class SoundCard
{
public Guid Id { get; set; }
...
public virtual List<SoundChipset>? SoundChipsets { get; set; }
...
public virtual List<MidiSynthChipset>? MidiSynthChipsets { get; set; }
...
}
The model is scaffolded into creating a controller and then a set of view pages. The add view works brilliantly and I could add sound and midi chipsets as needed. The edit view is where my problem lies: I could add new sound chipsets and midi chipsets, but could not remove the added ones.
The partial code for the controller for edit is as follows:
public async Task<IActionResult> Edit(Guid id, [Bind("Id,ModelSeries,ModelName,ModelNumber,ReleaseDate,HasCDROMInterface,HasSCSIPort,HasIDEPort,HasNECCDPort,HasMatsushitaCDPort,HasWaveBlasterPort,HasRAMForWaveTable,RAMSizeKB,HasGamePort,HasMPU401Port,numAudioOutPorts,numAudioInPorts,numAudioBiDirectionalPorts,numCoaxInPorts,numCoaxOutPorts,numOpticalInPorts,numOpticalOutPorts,numMidiInPorts,numMidiOutPorts")] SoundCard soundCard)
{
...
string[] selectedSndChipsets = Request.Form["lbSoundChipsetsSelected"].ToArray();
List<SoundChipset> sndChipset = new List<SoundChipset>();
foreach (string uuid in selectedSndChipsets)
{
sndChipset.Add(Factories.SoundChipsetDDLFactory.getSoundChipsetByUUID(_context, uuid));
}
soundCard.SoundChipsets = sndChipset;
string[] selectedMidChipsets = Request.Form["lbMidiChipsetsSelected"].ToArray();
List<MidiSynthChipset> MidChipsets = new List<MidiSynthChipset>();
foreach (string uuid in selectedMidChipsets)
{
MidChipsets.Add(Factories.MidiSynthChipsetDDLFactory.getMidiSynthChipsetByUUID(_context, uuid));
}
soundCard.MidiSynthChipsets = MidChipsets;
_context.Update(soundCard);
await _context.SaveChangesAsync();
...
So, practically recreating the Sound Chipsets and Midi Chipsets lists from scratch every single time. Problem is, the program treats the list as new objects to add to the existing list, it does not erase the current list despite the list being a new one!
I've tried to apply a Clear() command to the list but instead the program tossed an NullReferenceException which is puzzling because the list is supposed to be populated.
For completeness sake, here's part of the code for the edit frontend. It's partially JS to handle moving items between two boxes:
<label asp-for="SoundChipsets" class="control-label"></label>
<table>
<tr>
<th>Available</th>
<th>↔</th>
<th>Selected</th>
</tr>
<tr>
<td>
#Html.ListBox("lbAllSoundChipsets",(IEnumerable<SelectListItem>)ViewBag.SoundChipsets, new {#id="lbAllSoundChipsets", #style="min-width: 250px;"})
</td>
<td>
<input onclick="Javascript:SwitchListBoxItems('lbAllSoundChipsets', 'lbSoundChipsetsSelected');" type="button" value="→" /><br />
<input onclick="Javascript:SwitchListBoxItems('lbSoundChipsetsSelected', 'lbAllSoundChipsets');" type="button" value="←" />
</td>
<td>
#Html.ListBox("lbSoundChipsetsSelected",(IEnumerable<SelectListItem>)ViewBag.SelectedSoundChipsets, new {#id="lbSoundChipsetsSelected", #style="min-width: 250px;"})
</td>
</tr>
</table>
</div>
<div class="form-group">
<label asp-for="MidiSynthChipsets" class="control-label"></label>
<table>
<tr>
<th>Available</th>
<th>↔</th>
<th>Selected</th>
</tr>
<tr>
<td>
#Html.ListBox("lbAllMidiChipsets",(IEnumerable<SelectListItem>)ViewBag.MidiSynthChipsets, new {#id="lbAllMidiChipsets", #style="min-width: 250px;"})
</td>
<td>
<input onclick="Javascript:SwitchListBoxItems('lbAllMidiChipsets', 'lbMidiChipsetsSelected');" type="button" value="→" /><br />
<input onclick="Javascript:SwitchListBoxItems('lbMidiChipsetsSelected', 'lbAllMidiChipsets');" type="button" value="←" />
</td>
<td>
#Html.ListBox("lbMidiChipsetsSelected",(IEnumerable<SelectListItem>)ViewBag.SelectedMidiSynthChipsets, new {#id="lbMidiChipsetsSelected", #style="min-width: 250px;"})
</td>
</tr>
</table>
And the JS code:
function SwitchListBoxItems(sourceListItem, targetListItem) {
var src = document.getElementById(sourceListItem);
var dest = document.getElementById(targetListItem);
if (dest != null && src != null) {
while (src.options.selectedIndex >= 0) {
var lstItemNew = new Option(); // Create a new instance of ListItem
lstItemNew.text = src.options[src.options.selectedIndex].text;
lstItemNew.value = src.options[src.options.selectedIndex].value;
dest.options[dest.length] = lstItemNew;
src.remove(src.options.selectedIndex);
}
}
So yeah, if someone can point me in the right direction to get the system to delete items.
Thanks in advance.

How to make a CREATE view & controller method for a model that has a list field?

I have these 2 models:
public class Invoice
{
public string InvoiceID {get; set; }
public List<InvoiceElement> InvoiceElements {get; set;}
[...other fields...]
}
public class InvoiceElement
{
public string InvoiceElementID {get; set; }
[ForeignKey("Invoice")]
public string InvoiceID { get; set; }
public virtual Invoice Invoice { get; set; }
public string Item {get; set;}
[...other fields...]
}
I am unable to make a CREATE view for new Invoices that lets me add InvoiceElements.
I want to have a "CurrentInvoiceElements" table where to dinamically add rows.
Just trying to making it simple. You can use the name attribute (the attribute that asp.net uses for modal binding) and post a list along with other properties of the class. You can use javaScript to append new elements to your form. Using the above modals you've provided, I have written a simple example using simple jQuery functions.
Razor View:
<button class="btn btn-success" id="add_btn">Add Invoice Element</button>
#using (#Html.BeginForm("SaveInvoice", "Invoice", FormMethod.Post))
{
<!--Other modal attributes inputs goes here -->
<!--You can use normal html helper extensions -->
<table id="element_table">
<thead>
<tr>
<td>Element Id</td>
<td>Item</td>
</tr>
</thead>
<tbody>
<tr>
<td><input name="invoice.InvoiceElements[0].Item" id="InvoiceElements[0].Item" /></td>
<td><input name="invoice.InvoiceElements[0].InvoiceElementID" id="InvoiceElements[0].InvoiceElementID" /></td>
</tr>
</tbody>
</table>
<input type="submit" />
}
JavaScript:
<script type="text/javascript">
$("#add_btn").on('click', function (e) {
var table = $("#element_table");
var idx = $(table).find("tbody>tr").length;
var htmlToAppend = `<tr>
<td><input name="invoice.InvoiceElements[${idx}].Item" id="InvoiceElements[${idx}].Item" /></td>
<td><input name="invoice.InvoiceElements[${idx}].InvoiceElementID" id="InvoiceElements[${idx}].InvoiceElementID" /></td>
</tr>`;
$(table).find("tbody").append(htmlToAppend);
});
</script>
Controller / Action:
[HttpPost]
public ActionResult SaveInvoice(Invoice invoice)
{
/* your logic here */
if(ModelState.IsValid)
_invoiceBusiness.SaveInvoice(invoice);
return View();
}
Please make sure the variable name in the parameter of the action method matches the name used in the name attribute of the input tag. i.e. name = "invoice.***" public ActionResult SaveInvoice(Invoice invoice)
I followed this solution: https://github.com/danludwig/BeginCollectionItem some years ago, and worked fine.
If I'm not mistaken, at the time I managed to do it using only the HTML Helper: HtmlPrefixScopeExtensions. Then just make sure the name you give on your View when you do Html.BeginCollectionItem("name") is exactly the same as your collection on your ViewModel.
That's for binding back to the controller.
With this, you can dynamically add rows using AJAX per example.
I hope it's clear enough. If you don't understand it I may make a Github repository with this.

Binding input model in partial view help needed

I want to pass an input model from a partial view to a controller. I'm rather new to MVC so still trying to understand how the default model binder works.
Via AJAX (listBox) a controller passes back a partial view and inserts into table id=searchResults.
#model ViewModels.LocationViewModel
#using (Ajax.BeginForm("ProcessSearch", "SearchR", new AjaxOptions
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "searchResults",
}))
{
<div>#Html.ListBoxFor(xxx)</div>
<input id="Search" type="submit" value="Search" />
}
Here is the controller and ViewModel that populates the partial view
public class OrderViewModel
{
public string Description { get; set; }
public string Status { get; set; }
}
public ActionResult ProcessSearch(SearchViewModel search)
{
select new OrderViewModel{
Status=f.STATUS,
Description=f.DESCRIPTION}).ToList();
return PartialView(model);
}
In the same main view I have this form that I want I want to bind to yet another view model. I simply don't understand how to implement the default binder from the model of the partial view. I apologize if I didn't explain this correctly. I hope it makes sense.
#using (Html.BeginForm("Testing", "SearchR", FormMethod.Post))
{
<div>#Html.DropDownListFor(yyy)</div>
<input id="Reshop" type="submit" value="Reshop" />
}
<table id="searchResults"></table>
public ActionResult Testing(RSOrderViewModel rOrder)
{
return Content("hey");
}
public class RSOrderViewModel
{
public string yyy { get; set; }
public IEnumerable<OrderViewModel> sovm { get; set; }
}
#model List<ViewModels.OrderViewModel>
#{ViewData.TemplateInfo.HtmlFieldPrefix = "sovm";
}
<table id="searchResults">
<tr>
<th>Order Id</th>
<th>Order Detail</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(x => x[i].OrderId)
</td>
<td>
#Html.DisplayFor(x => x[i].OrderDetail)
</td>
</tr>
}
</table>
The table is outside the second form. So when you POST to the Testing action all that is sent to the controller is the value of the dropdown list. If you want to send the collection that's stored in this table you will have to either use AJAX or put the table inside the form:
#using (Html.BeginForm("Testing", "SearchR", FormMethod.Post))
{
<div>#Html.DropDownListFor(yyy)</div>
<table id="searchResults"></table>
<input id="Reshop" type="submit" value="Reshop" />
}
Now of course putting the table inside the form doesn't mean that it will send anything to the server when you submit the form. You need to put input fields (hidden if you don't want them to be visible to the user) that will contain the values that will be POSTed back. Also those input field names must follow the standard convention for binding to a list.
You haven't actually shown how does the partial view look like but here's an example of how it might look so that the convention is respected. For example let's suppose that you have 2 properties in your OrderViewModel that you want to be bound: OrderId and OrderDetail:
#model IEnumerable<OrderViewModel>
#{
// We set the name prefix for input fields to "sovm"
// because inside your RSOrderViewModel the collection
// property you want to bind to this table is called sovm
// and in order to respect the convention the input names
// must be in the following form "sovm[0].OrderId", "sovm[1].OrderId", ...
ViewData.TemplateInfo.HtmlFieldPrefix = "sovm";
}
<thead>
<tr>
<th>Order Id</th>
<th>Order detail</th>
</tr>
</thead>
<tbody>
#Html.EditorForModel()
</tbody>
and then you could have an editor template which will be rendered for each element of the model (~/Views/Shared/EditorTemplates/OrderViewModel.cshtml):
#model OrderViewModel
<tr>
<td>#Html.EditorFor(x => x.OrderId)</td>
<td>#Html.EditorFor(x => x.OrderDetail)</td>
</tr>
The name of the template is the name of the type used in the collection you want to bind to (IEnumerable<OrderViewModel> sovm { get; set; } => OrderViewModel.cshtml). It must also be placed inside the ~/Views/Shared/EditorTemplates folder if it can be reused between multiple controllers or if it is specific to the current controller inside the ~/Views/XXX/EditorTemplates folder where XXX is the current controller.

How do I return List or Collection to Controller from View in MVC 3?

Someone please help me return this list properly from my view. I don't see why I'm returning null for my fieldModelList I try to pass to the controller...
Here is my view:
#model List<Regions.SOA.UI.CopyBookSchemaCreator.Models.FieldModel>
<script type="text/javascript" src="~/Scripts/jquery-ui-1.8.11.min.js"></script>
#using (Html.BeginForm("GetResponse", "TestMethods", FormMethod.Post))
{
<table id="tblMethods">
<tr>
<th>
Property Name
</th>
<th>
Request
</th>
</tr>
#foreach (FieldModel fieldModel in Model)
{
<tr>
<td>
#Html.DisplayFor(m => fieldModel.PropertyName)
</td>
<td>
#Html.TextBoxFor(m => fieldModel.PropertyValue)
</td>
</tr>
}
</table>
<div>
<input type="submit"/>
</div>
and here is my controller:
[HttpPost]
public ActionResult GetResponse(List<FieldModel> fieldModelList)
{
return GetResponse(fieldModelList);
}
I am hitting the HttpPost method but if I place a breakpoint just inside it, I am returning null for the fieldModelList right off the bat, which I was hoping would be a list of the values I entered into the texboxes on the view that is of model FieldModel...
I think something is wrong with my logic versus my syntax, or as maybe as well as my syntax, but basically what I want to do is return back a list of type FieldModel with each corresponding PropertyName and PropertyValue to the controller. I noticed I am not passing any kind of id parameter in my BeginForm statement in the view. Do I need one here?
Just in case, here is my model class for FieldModel:
namespace Regions.SOA.UI.CopyBookSchemaCreator.Models
{
public class FieldModel
{
[Display(Name = "Property")]
public string PropertyName { get; set; }
[Display(Name = "Value")]
public string PropertyValue { get; set; }
}
}
Phil Haack wrote an article some time ago explaining how to bind collections (ICollection) to view models. It goes into additional detail about creating an editor template, which you could certainly do as well.
Basically, you need to prefix the HTML elements' name attributes with an index.
<input type="text" name="[0].PropertyName" value="Curious George" />
<input type="text" name="[0].PropertyValue" value="H.A. Rey" />
<input type="text" name="[1].PropertyName" value="Ender's Game" />
<input type="text" name="[1].PropertyValue" value="Orson Scott Card" />
Then, your controller could bind the collection of FieldModel
[HttpPost]
public ActionResult GetResponse(List<FieldModel> fieldModelList)
{
return GetResponse(fieldModelList);
}
I'm not 100% sure the following would name the attributes correctly (I'd recommend using the editor template) but you could easily use the htmlAttributes argument and give it a name using the index.
#for(int i = 0;i < Model.Count;i++)
{
<tr>
<td>
#Html.DisplayFor(m => m[i].PropertyName)
</td>
<td>
#Html.TextBoxFor(m => m[i].PropertyValue)
</td>
</tr>
}
Editor Template
If you wanted to go as far as adding an editor template, add a partial view named FieldModel.ascx to /Views/Shared that is strongly typed to a FieldModel
#model Regions.SOA.UI.CopyBookSchemaCreator.Models.FieldModel
#Html.TextBoxFor(m => m.PropertyName) #* This might be a label? *#
#Html.TextBoxFor(m => m.PropertyValue)
And, then the part of your view responsible for rendering the collection would look like:
#for (int i = 0; i < Model.Count; i++) {
#Html.EditorFor(m => m[i]);
}

ASP.NET MVC unobtrusive client validation with multiple models

The ThingController creates a model which contains (amongst other properties) a collection of Things. These can be edited in the view like so:
<form action="#Url.Action("Update", "Thing")" method="post">
<table>
<tr>
<th>Foo</th>
<th>Bar</th>
</tr>
#foreach (var thing in ViewData.Model.Things)
{
<tr class="raw-data">
<td class="raw-data"><input name="things[#rowCount].Foo" class="raw-data" readonly="readonly" type="text" value="#thing.Foo" /></td>
<td class="raw-data"><input name="things[#rowCount].Bar" class="raw-data" type="text" value="#thing.Bar" /></td>
</tr>
rowCount++;
}
</table>
<br />
<input type="submit" value="OK" />
</form>
The controller contains the following action, which allows multiple Things to be updated simultaneously:
public ActionResult Update(ThingModel[] things)
{
...
}
I've added some validation attributes to properties on the Thing class:
[Required]
[Range(0, 500000, ErrorMessage = "Foo must be within 0 and 500,000.")]
public double Foo{ get; set; }
[Required]
[Range(0, 500000, ErrorMessage = "Bar must be within 0 and 500,000.")]
public double Bar { get; set; }
The thing is, I can't figure out how to add unobtrusive validation using the TextBoxFor helpers etc.
At this point I think the correct approach is to manually mark up the input fields with the validation attributes, but I was wondering if anyone can point me to some documentation, a tutorial etc. that demonstrates the use of helpers, multiple models and unobtrusive validation?
I had a similar problem where users could dynamically add multiple emails to their account.
I fixed it with Jquery by adding the validation manually. You should give your form a name and add the validation to all your items. It should be something like this I quess:
$('#frmYourForm').validate();
for (var i = 0; i < 'CountOfAllFields'; i++) {
$('#Things_' + i + '__Foo').rules('add', { required: true, messages: { required: 'The Foo field is required'} });
$('#Things_' + i + '__Bar').rules('add', { required: true, messages: { required: 'The Bar field is required'} });
}
Typed from my memory so don't shoot me if I made an error. I don't know the exact syntax for the Range thingy but you should look around the Jquery.validate file.

Resources