How to stop MVC3 validation script running on pageload? - c#

I have a simple loginform based on the following modelitem
public class LogOnModelItem
{
[Required(ErrorMessage="Brukernavn er påkrevd")]
[DisplayName("Brukernavn")]
public string UserName { get; set; }
[Required(ErrorMessage="Passord er påkrevd")]
[DataType(DataType.Password)]
[DisplayName("Passord")]
public string Password { get; set; }
[DisplayName("Husk meg?")]
public bool RememberMe { get; set; }
}
The view is :
<h2>Login</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<table>
<tr>
<td>#Html.LabelFor(model => model.UserName)</td>
<td>
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</td>
</tr>
<tr>
<td>#Html.LabelFor(model => model.Password)</td>
<td>
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</td>
</tr> <tr>
<td>#Html.LabelFor(model => model.RememberMe)</td>
<td>
#Html.EditorFor(model => model.RememberMe)
#Html.ValidationMessageFor(model => model.RememberMe)
</td>
</tr>
</table>
<p>
<input type="submit" value="Create" />
</p>
}
The controller:
public ActionResult Login(LogOnModelItem lmi)
{
return View(lmi);
}
When I load this up in the browser the validation is apparently run on pageload. This then outlines the username and password in red, with the error message supplied after each line. I have 3 questions for this, in order of importance:
1: How do I make sure the validation is not run until after the user presses submit.
2: How do I display the validation summary?
3: How do I make the "username" field choosen by default and ready to accept input?
Answer to #1:
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LogOnModelItem lmi)
{
return View(lmi);
}
Answer to #2
#Html.ValidationSummary(false)
Answer to #3
<script type="text/javascript">
$(document).ready(function () {
$('#UserName').focus();
});
</script>

1: How do I make sure the validation is not run until after the user presses submit
This should already be the case. If you have a GET action rendering this form and supplying a view model there won't be any validation errors. It's the POST action that would show the validation messages and if you have client validation enabled it could also happen onblur.
2: How do I display the validation summary?
You already did: #Html.ValidationSummary(true)
3: How do I make the "username" field choosen by default and ready to accept input?
You could use javascript:
$(function() {
$('#UserName').focus();
});

Related

How to sent ObjectViewModel from View to Controller in ASP.NET MVC C#?

I am working in an ASP.NET Web Application, using MVC template.
I am trying send or pass an ObjectViewModel from a View.cshtml to a Controller, but in the Controller the ObjectViewModel arrived as null.
The idea is the follow:
I show the view IndexUsersInActiveDirectory.cshtml with a collection of object of type RegisterViewModel. Into this view there is an #Html.ActionLink for send a element of the collection to the Register controller:
IndexUsersInActiveDirectory.csthml
#model IEnumerable<SecureEscuelaWithIdentity.Models.RegisterViewModel>
#{
ViewBag.Title = "IndexUsersInActiveDirectory";
}
<h2>IndexUsersInActiveDirectory</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.ActionLink("Select", "Register", new { item })
</td>
</tr>
}
</table>
The controller Register is the next:
//
// GET: /Account/Register
[Authorize(Roles = "Admin")]
public ActionResult Register(RegisterViewModel model)
{
return View(model);
}
The class RegisterViewModel is:
public class RegisterViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
}
The view Register.cshtml
#model SecureEscuelaWithIdentity.Models.RegisterViewModel
#{
ViewBag.Title = "Register";
}
<h2>#ViewBag.Title.</h2>
#using (Html.BeginForm("RegisterInApp", "Account", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
#Html.ValidationSummary()
<li>#Html.ActionLink("ActiveDirectory", "IndexUsersInActiveDirectory", "Account")</li>
<div class="form-group">
#Html.LabelFor(m => m.UserName, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Email, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
How to send the entire collection element (as model) to the controller?
There are 2 problems
1) You are missing a null on the 4th parameter of ActionLink
#Html.ActionLink("Select", "Register", new { item }, null)
Without that 4th parameter it assumes the 3rd parameter is the HTML attributes and not the querystring parameters.
I find this little 4th parameter "quirk" to be one of the most annoying overrides in MVC.
2) Your item needs to be simple data values that can be passed via the querystring
As your item is not a simple value type, you need to pass back its unique values. e.g.:
#Html.ActionLink("Select", "Register", new { name = item.Name, email = item.Email }, null)
Action links do not do a postback, they just create anchor links in the page, so any values have to be passed in the query string.
Change the receiving action to match:
[Authorize(Roles = "Admin")]
public ActionResult Register(string name, string email)
{
RegisterViewModel model = new RegisterViewModel()
{
Name = name,
Email = email
};
return View(model);
}
But I leave those details up to you :)
The solution proposed by TrueBlueAussie is suitable for scenarios where there is no problem to show querystring in the address bar of the browser.
If so, the #Html.ActionLink could be like this:
#Html.ActionLink("Select", "Register", new {item}, null)
since the controller will take 'item' as a RegisterViewModel object (although in reality be a querystring). Note: not forgetting the 4th parameter that contributed TrueBlueAussie "null".
The Register controller looks like this:
[Authorize (Roles = "Admin")]
public ActionResult Register (RegisterViewModel model)
{
return View (model);
}
It works!
As mention above, this displays the querystring in the Register view, which is not correct in my implementation. I do not want to show this querystring in the address bar.
To achieve this, I added an intermediate controller responsible of receiving the 'item' (from IndexUsersInActiveDirectory.csthml) as a RegisterViewModel object (actually it is a querystring) and add this Object to TempData dictionary with a key called "model".
Here is the controller RegisterTemp:
[Authorize (Roles = "Admin")]
public ActionResult RegisterTemp (RegisterViewModel model)
{
TempData ["model"] = model;
return RedirectToAction ("Register");
}
        
In the view 'IndexUsersInActiveDirectory.csthml' the #ActionLink point to controller RegisterTemp rather than the controller Register, being as follows:
# Html.ActionLink ("Select", "RegisterTemp", new {item}, null)
Importantly RegisterTemp returns a "RedirectToAction" to Register view.
And finally, when the RegisterTemp controller returns RedirectToAction to Register controller, it takes the element with the "model" key from the TempData dictionary in order to return it to the view. Thus, the Register controller is:
//
// GET: / Account / Register
[Authorize (Roles = "Admin")]
public ActionResult Register ()
{
return View (TempData ["model"]);
}
I mentioned that, the solution proposed by #TrueBlueAussie is fully functional for the scenario that is not important to show the querystring.
The solution that I have just detailed now, is for avoid showing that querystring.
If there is a more correct solution to solve my scenario, is welcome.
You must iterate the elements with a "for" loop, as in for
(int i; i <= elements.Length -1; i++)
elements[i].etc;
So the collection don't get lost on post.
Read this for more detail:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

How to add text boxes in MVC when binding a view to an IEnumerable?

I am working on this project. We are using EF and backside data models. We are mapping them to viewmodels that have the same property names. This has created 4 separate view models, which I'm ok with. Now, I have organizations that can have 2 addresses, multiple access codes and multiple domain addresses. In my partial views, I have to bind the view to an IEnumerable to list all of the possible codes/domains/etc. When I'm in the "details" page for an organization, which is using a details page that is populated by partials, I would like to add a text box for adding a new access code, or domain. Unfortunatly, I can't figure out how to achive this.
Here is my code:
ViewModel for OrganizationAccessCodes:
namespace AdminTool.Models.ViewModel
{
public class OrganizationAccessCodeView
{
[AtLeastOneRequired(ErrorMessageResourceType = typeof(AdminResource), ErrorMessageResourceName = "OrganizationAccessCodeRequired")]
[Display(ResourceType = typeof(AdminResource), Name = "OrganizationAccessCode")]
[RegularExpression(#"(0-9)", ErrorMessageResourceType = typeof(AdminResource), ErrorMessageResourceName = "OrganizationAccessCodeFormatError")]
public string AccessCode { get; set; }
public bool Active { get; set; }
public int OrganizationID { get; set; }
}
}
Here is the Details html:
#model AdminTool.Models.ViewModel.OrganizationView
#using AdminTool.App_GlobalResources;
#{
ViewBag.Title = "Details";
}
<div id="idm_main">
<div class="form_section">
<h3>#AdminResource.OrganizationDetailsHeader</h3>
<div class="form_item">
<h4>#AdminResource.OrganizationNameHeader</h4>
<div class="item">
#Html.DisplayFor(model => model.OrganizationName)
</div>
#Html.Partial("_AddressPartial", Model.OrganizationAddress)
</div><br /><br />
<div class="form_item">
<h4>#AdminResource.OrganizationAccessCodeHeader</h4>
#Html.Partial("_AccessCodePartial", Model.AccessCodes)
</div><br /><br />
<div class="form_item">
<h4>#AdminResource.OrganizationDomainsHeader</h4>
#Html.Partial("_EmailDomainPartial", Model.DomainAddresses)
</div>
<div class="clearBoth"></div>
</div>
</div>
and here is the partial for the access code:
#model IEnumerable<AdminTool.Models.ViewModel.OrganizationAccessCodeView>
#foreach (var item in Model)
{
<tr>
<td>
#item.AccessCode
</td>
<td>
#Html.ActionLink("Deactivate", "Edit", new {})
</td>
</tr>
}
<div class="form_item">
<div class="item">
#Html.LabelFor()
</div>
<div class="item">
#Html.TextBoxFor()
#Html.ValidationMessageFor()
</div>
</div>
I'm not completely done with the partial view because of this problem I'm having. I want to know if there is a way (other than Tuple) in order to have a text box for the access code object that is in the view model?
You need to define what your label and textbox are for in your partial view. If the property is called Address:
#foreach (var item in Model)
{
#Html.LabelFor(m => item.Address)
#Html.TextBoxFor(m => item.Address)
}

MVC CheckboxFor never passing view correct value? Binding checkboxFor

I have a MVC project im currently working on and i have a check box in one of my views. When i change the check box it never passes the correct value to my modal?
View CheckboxFor:
#model OBASA.Models.EmployeeModal
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>EmployeeModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Active)
</div>
<div class="editor-field">
#Html.CheckBoxFor(model => model.Active)
#Html.ValidationMessageFor(model => model.Active)
</div>
<p>
<input type="submit" name="Sub" value="Save" />
</p>
</fieldset>
}
Modal:
public class EmployeeModal
{
public bool Active { get; set; }
}
In run time when i check or un check the check box it never passes the value, its always false.
Please can someone tell me how to bind this correctly for MVC.
The code you've posted is correct, though you have not posted your controller code. It needs to look something like this:
public ActionResult Junk()
{
return View();
}
[HttpPost]
public ActionResult Junk(EmployeeModal model)
{
bool bActive = model.Active;
return View(model);
}

Model Key and Foreign Key parametres are null in controller after POST action

I have the following hidden fields inside my View:
#Html.HiddenFor(model => model.cat.Id)
#Html.HiddenFor(model => model.cat.OwnerId)
#Html.HiddenFor(model => model.cat.BirthDate
#Html.HiddenFor(model => model.cat.Weight)
Id is a Key and OwnerId is a ForeignKey. When I try to edit that model, all values are displayed correctly but the problem is when I try to POST those values back. Id and OwnerId are always 0. I have a lot of other values inside my view like dropdown lists, text boxes etc, all those values are posted correctly. The problem is only with Id and OwnerId.
Controller:
[HttpPost]
public ActionResult Edit(EditCatDetailsViewModel model)
{
Debug.WriteLine("Cat Id: " + model.cat.Id); //displays 0
}
I checked source of web site and I see Id and OwnerId have correct values (values aren't 0). My question is why are those values not sent like all other values? Why are they received in controller as 0?
Thank you!
UPDATE:
Cat Edit View:
#model Pro.Web.Models.EditCatDetailsViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit Cat Details</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Cat</legend>
#Html.HiddenFor(model => model.cat.Id)
#Html.HiddenFor(model => model.cat.OwnerId)
#Html.HiddenFor(model => model.cat.BirthDate
#Html.HiddenFor(model => model.cat.Weight)
<div class="editor-label">
#Html.LabelFor(model => model.cat.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.cat.Name)
#Html.ValidationMessageFor(model => model.cat.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.cat.Description)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.cat.Description, new { #style="width:400px; height:600px" })
#Html.ValidationMessageFor(model => model.cat.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.cat.NumberOfKittens)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.cat.NumberOfKittens)
#Html.ValidationMessageFor(model => model.cat.NumberOfKittens)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to Details", "Details")
</div>
At the end I managed to do it like this. I put additional variable inside my EditCatDetailsViewModel which represents a cat id.
This is simplified look of that model now:
public class EditCatDetailsViewModel
{
public Cat cat { get; set; }
public int catId { get; set; }
}
Inside my GET Edit method I've set that Id from Id in Cat model:
// GET: /Cats/Edit/5
public ActionResult Edit(int id = 0)
{
Cat cat = _catRepository.Find(id);
if (cat == null)
{
return HttpNotFound();
}
EditCatDetailsViewModel model = new EditCatDetailsViewModel();
model.cat = cat;
model.catId = cat.Id;
return View(model);
}
And in my View I added additional hidden element:
#Html.HiddenFor(model => model.catId)
Now I can access that Id in POST method:
[HttpPost]
public ActionResult Edit(EditCatDetailsViewModel model)
{
Debug.WriteLine("Cat Id: " + model.catId); //displays correct Id for Cat!!!
model.cat.Id = model.catId;
}
Now it all works and I can save changes to my DB. I am still interested why those values were not posted the first time.
Hi Its seems that hidden field generated via razor does not updates their values on posting them back again. So please try to make your hidden fields via html syntex not with razor like this:
<input type="hidden" id="" name="" value="#Model.model.cat.Id" />
<input type="hidden" id="" name="" value="#Model.model.cat.OwnerId" />
Check your page source and copy the value of name attribute of these hidden fields from there and assign them here and now check I hope you will get those values also in your action method.

TextBoxFor() not generating validation markup

I have a SQL Server 2012 in which I have AWARD table with two columns TITLE and MONTH. TITLE is varchar(256) and cannot be NULL. MONTH is int and can be NULL.
With VS2012 Ultimate and EF 5.0.0, the TextBoxFor helper in MVC4 app is not producing validation (data-val="required" and data-val-required="required message") for the TITLE columne above, but in the same View, MONTH is getting the correct validation markup. The .edmx designer does show TITLE is NonNullable, BUTT, the automatically generated AWARD.cs file does not have the [Required]
attribute for the TITLE column.
What can I try?
#model MyProject.Models.AWARD
#{
ViewBag.Title = "Add Award";
Layout = "~/Views/Shared/_EditorLayout.cshtml";
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Add Award</legend>
<table>
<tr>
<td>
#Html.LabelFor(model => model.TITLE)
</td>
<td>
#Html.TextAreaFor(model => model.TITLE)
<br />#Html.ValidationMessageFor(model => model.TITLE)
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.MONTH)
</td>
<td>#Html.DropDownListFor(model => model.MONTH, new SelectList(MyProject.Models.Helpers.Months, "key","value"), "[Not Selected]")
<br />#Html.ValidationMessageFor(model => model.MONTH)
</td>
</tr>
<tr>
<td>
<input type="submit" value="Add" />
</td>
<td>
#Html.ActionLink("Cancel", "Index", null, new { #class = "cancel-button" })</td>
</tr>
</table>
</fieldset>
}
You shouldn't really be binding your views directly to your data mapping entities. You should create view model classes to wrap the data you pass to and from your view and then populate your data objects from the controller.
You can then perform the required validation on your view model without affecting your generated mapping classes.
Model
public class AwardViewModel
{
[Required, StringLength(30)]
public string Title { get; set; }
....
}
View
#model AwardViewModel
#using (Html.BeginForm()) {
#Html.EditorFor(m => m.Title)
#Html.ValidationMessageFor(m => m.Title)
...
}
Controller
[HttpPost]
public ActionResult Create (AwardViewModel model)
{
/* Create new AWARD in entity context, populate
with relevant fields from model and commit. */
}

Categories