MVC 3 edit view for certain fields only - asp.net-mvc-3

I have this model:
public class ExchangeRate
{
[Key]
public int ExchangeRateID { get; set; }
[Required]
[Display(Name = "Currency:")]
public string Currency { get; set; }
[Required]
public decimal Rate { get; set; }
}
The "Create" view is working fine, but when I am in the edit view, I only want the Currency property to be displayed, and not editable. How should I do this? If I create another "view-only" model for this class, then I would omit the "Currency" property and would not be able to display it.
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>ExchangeRate</legend>
#Html.HiddenFor(model => model.ExchangeRateID)
<div class="editor-label">
#Html.LabelFor(model => model.Currency)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Currency)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Rate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Rate)
#Html.ValidationMessageFor(model => model.Rate)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
Changing #Html.EditorFor(model => model.Currency) to #Html.DisplayFor(model => model.Currency) doesn't work because the model state becomes invalid when it posts back to the controller.

You could add
#Html.HiddenFor(model => model.Currency)
in your form and then use
#Html.DisplayFor(model=> model.Currency)
to display the readonly value of the currency property. That way when you post the value will be sent along in the posted model.

You're looking for:
[HiddenInput(DisplayValue=true)]
Then show the editor, not the display (use EditorFor).
This displays the value read-only, but adds a hidden input so that the posted state is valid.

To display the currency but not edit it try
#Html.Label("Currency", Model.Currency)
if you also need to post the currency value back to the controller try
#Html.HiddenFor(m => m.Currency)
I hope this helps.

Related

Can I restrict client-side validation to specific fields?

I have a Form where I successfully use unobtrusive-validation with the [remote] annotation.
I have other fields in the Form with [required] annotation in the model but I don't want client-side validation for these fields.
I only want server-side validation for [required] fields
I haven't found a solution and I wonder if it's easily feasible?
EDIT :
A part of my code
Part of my model :
I would like the first field : "Email" use Unobtrusive-validation and the second one : "PasswordHash" only use server-side validation even if it has [required] annotation.
Finally, i don't want an AJAX error message for all my form 's fields.
[Required(ErrorMessageResourceType = typeof(Project.Resources.Models.Accounts.User), ErrorMessageResourceName = "EMAIL_REQUIRED")]
[Display(Name = "EMAIL_DISPLAY", ResourceType = typeof(Project.Resources.Models.Accounts.User))]
[Remote("EmailExists", "User", "An account with this email address already exists.")]
public string Email { get; set; }
[Required(ErrorMessageResourceType = typeof(Project.Resources.Models.Accounts.User), ErrorMessageResourceName = "PASSWORDHASH_REQUIRED")]
[DataType(DataType.Password)]
[Display(Name = "PASSWORDHASH_DISPLAY", ResourceType = typeof(Project.Resources.Models.Accounts.User))]
public string PasswordHash { get; set; }
Part of the action in Controller
Server-side validation :
[HttpPost]
public ActionResult Register(User user)
{
if (ModelState.IsValid )
{
DataContext.User.Add(user);
DataContext.SaveChanges();
return RedirectToAction("Index");
}
return View(user);
}
Ajax validation :
public JsonResult EmailExists(string email)
{
User user = DataContext.User.SingleOrDefault(x => x.Email == email);
return user == null ?
Json(true, JsonRequestBehavior.AllowGet) :
Json(
string.Format("an account for address {0} already exists.",
email), JsonRequestBehavior.AllowGet);
}
Part of the view :
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PasswordHash)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PasswordHash)
#Html.ValidationMessageFor(model => model.PasswordHash)
</div>
<p>
<input type="submit" name="op" id="edit-submit" value="#Project.Resources.Views.User.Register.SUBMIT_FORM" class="form-submit art-button" />
</p>
}
When I click on the sumbit button i would like a server-side validation.
And for some specific fields like Email i would like an Ajax Validation.
there might be better ways, but one way to accomplish this is to do the following
#using (Html.BeginForm("Register", "Home", FormMethod.Post, new { id = "registerForm" }))
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PasswordHash)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PasswordHash)
#Html.ValidationMessageFor(model => model.PasswordHash)
</div>
<p>
<input type="submit" name="op" id="edit-submit" value="#Project.Resources.Views.User.Register.SUBMIT_FORM" class="form-submit art-button" />
</p>
}
<script type="text/javascript">
// add the rules for the fields that you want client-side validation on
// leave out the fields that you dont want client-side validation. they will be validated
// on server side.
$("#registerForm").validate({
rules: {
Email: { required: true }
},
messages: {
Email: { required : "Email is required" }
}
});
</script>

Receiving a NullReference Exception error during HttpPost Action when using a ViewModel

I am trying to create what I feel is a very simple form submission using a ViewModel. I have worked on this off and on all day and for some reason cannot understand why when my app gets to my HttpPost action my EmailViewModel is empty. I get a "NullReference Exception Occurred" "Object reference not set to an instance of an object" error.
Can you take a look at my code and tell me where I am being crazy?
Here is my httpPost action:
[HttpPost]
public ActionResult SendStudentAnEmail(EmailViewModel email)
{
Debug.Write(email.Subject); // First NullReferenceException
Debug.Write(email.Body);
Debug.Write(email.Email);
etc. . .
My ViewModel:
namespace MyApp.ViewModels
{
public class EmailViewModel
{
public string Email { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
}
and My View:
#model MyApp.ViewModels.EmailViewModel
#{
ViewBag.Title = "SendStudentAnEmail";
}
<h2>SendStudentAnEmail</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)
<fieldset>
<legend>EmailViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Subject)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Subject)
#Html.ValidationMessageFor(model => model.Subject)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Body)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Body)
#Html.ValidationMessageFor(model => model.Body)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Thank you.
*UPDATE*
If I change my HttpPost Action to use FormCollection, I can use the values just fine, I can even re-cast the FormCollection values back to my EmailViewModel. Why is this?
[HttpPost]
public ActionResult SendStudentAnEmail(FormCollection emailFormCollection)
{
Debug.Write(emailFormCollection["email"]);
Debug.Write(emailFormCollection["subject"]);
Debug.Write(emailFormCollection["body"]);
var email = new EmailViewModel
{
Email = emailFormCollection["email"],
Subject = emailFormCollection["subject"],
Body = emailFormCollection["body"]
};
. . . . then the rest of my code works just how I wanted. . .
Why do I have to cast from FormCollection over to my EmailViewModel? Why isn't it giving me the NullReference Exception if I attempt to simply push an EmailViewModel into my Action?
Your EmailViewModel class has a property called Email of type string. And your controller action takes an argument called email of type EmailViewModel. This confuses the default model binder. So either rename the property inside the view model or the action argument:
[HttpPost]
public ActionResult SendStudentAnEmail(EmailViewModel model)
{
Debug.Write(model.Subject);
Debug.Write(model.Body);
Debug.Write(model.Email);
...
}

ASP.Net MVC 3 Data Annotation

I am building an ASP.Net MVC 3 Web application using Entity Framework 4.1. To perform validation within one of my Views which accepts a ViewModel. I am using Data Annotations which I have placed on the properties I wish to validate.
ViewModel
public class ViewModelShiftDate
{
public int shiftDateID { get; set; }
public int shiftID { get; set; }
[DisplayName("Start Date")]
[Required(ErrorMessage = "Please select a Shift Start Date/ Time")]
public DateTime? shiftStartDate { get; set; }
[DisplayName("Assigned Locum")]
public int assignedLocumID { get; set; }
public SelectList AssignedLocum { get; set; }
}
View
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<br />
<div class="editor-label">
#Html.LabelFor(model => model.shiftStartDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.shiftStartDate, new { #readonly = "readonly" })
#Html.ValidationMessageFor(model => model.shiftStartDate)
</div>
<br />
<div class="editor-label">
#Html.LabelFor(model => model.assignedLocumID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.assignedLocumID, Model.AssignedLocum)
#Html.ValidationMessageFor(model => model.assignedLocumID)
</div>
<br />
<p>
<input type="submit" value="Save" />
</p>
<br />
}
The SelectList 'AssignedLocum' is passed into my View for a DropDownList, and the item selected is assigned to the property 'assignedLocumID'.
As you can see from my ViewModel, the only required field is 'shiftStartDate', however, when I hit the Submit button in my View, the drop down list 'AssignedLocum' also acts a required field and will not allow the user to submit until a value is selected.
Does anyone know why this property is acting as a required field even though I have not tagged it to be so?
Thanks.
Try to use default value for dropdown (for example "Please select")
#Html.DropDownListFor(model => model.assignedLocumID, Model.AssignedLocum, "Please select")

Telerik MVC 3 (Razor) Q1 2012 Editor EditorFor() Binding Returns Null Value and Unobtrusive Validation Doesn't Work

I'm currently working on my first MVC3 application at work (using the Razor view engine), and decided to use the open source Telerik Q1 2012 controls since they will provide a lot of the functionality I need (and look nice as well). Right now the issue I'm having is using the Telerik Editor control and binding to my view model. I have standard Html.EditorFor() controls on the page that return the value in the ViewModel correctly, but the property bound to Telerik Editor is null. Their documentation is completely useless (it only mentions EditorFor one time), and it doesn't seem like they answer too many questions on the forum either. My main question is, how do I bind the Telerik MVC3 Editor to a model and have it set the property that's bound to it? My code for the view model is below (thanks for any help you can provide, and keep in mind, I'm brand new to MVC, I'm doing this project on my own to get familiar with it and introduce some new technologies to the group):
public class SupportViewModel
{
[Display(Name = "Ticket Subject")]
[MaxLength(30)]
[Required(ErrorMessage = "The ticket subject is required.")]
public string TicketSubject { get; set; }
[Display(Name = "Support Issue")]
[Min(1, ErrorMessage = "You must select a support issue.")]
public int SupportIssueID { get; set; }
[Display(Name = "Ticket Priority")]
[Min(1, ErrorMessage = "You must select a ticket priority.")]
public int TicketPriorityID { get; set; }
//public string EmployeeID { get; set; }
public bool IsClosed { get; set; }
[Required(ErrorMessage = "The detail message is required.")]
public string DetailMessage { get; set; }
}
View Code:
#model RadixMVC.ViewModels.SupportViewModel
#{
ViewBag.Title = "Create New Support Ticket";
}
<h2>Radix Support: Create New Support Ticket</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)
<fieldset style="width: 500px">
<legend>Create New Support Ticket</legend>
<div class="editor-label">
#Html.LabelFor(model => model.TicketSubject)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TicketSubject)
#Html.ValidationMessageFor(model => model.TicketSubject)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SupportIssueID)
</div>
<div class="editor-field">
#Html.DropDownList("SupportIssueID", string.Empty)
#Html.ValidationMessageFor(model => model.SupportIssueID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TicketPriorityID)
</div>
<div class="editor-field">
#Html.DropDownList("TicketPriorityID", string.Empty)
#Html.ValidationMessageFor(model => model.TicketPriorityID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.IsClosed)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.IsClosed)
#Html.ValidationMessageFor(model => model.IsClosed)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DetailMessage)
</div>
<div class="editor-field">
#*#Html.EditorFor(model => model.DetailMessage)*#
#Html.ValidationMessageFor(model => model.DetailMessage)
<br />
#{ Html.Telerik().EditorFor(model => model.DetailMessage)
.Name("DetailMessageEditor")
.HtmlAttributes(new { style = "height: 200px" })
.Encode(false)
.Render();
}
</div>
<div>
<br />
<input type="submit" value="Create Ticket" title="Submits a new support ticket" />
<input type="submit" onclick="parent.location='#Url.Action("Index", "Support", "Index")'" value="Cancel" title="Return to Support Home" />
</div>
</fieldset>
}
And finally, Controller Code:
[HttpPost]
public ActionResult Create(SupportViewModel vm)
{
if (ModelState.IsValid)
{
SupportTicket SupportTicket = new SupportTicket()
{
SupportTicketID = Guid.NewGuid(),
EmployeeID = "123456",
TicketOpenDate = DateTime.Now,
TicketModifiedDate = DateTime.Now,
IsClosed = vm.IsClosed,
TicketSubject = vm.TicketSubject,
SupportIssueID = vm.SupportIssueID,
TicketPriorityID = vm.TicketPriorityID
};
TicketDetail TicketDetail = new TicketDetail()
{
TicketDetailID = Guid.NewGuid(),
SupportTicketID = SupportTicket.SupportTicketID,
TicketOrder = 1,
EmployeeID = "123456",
DetailDate = DateTime.Now,
DetailMessage = vm.DetailMessage
};
SupportTicket.TicketDetails.Add(TicketDetail);
db.SupportTickets.Add(SupportTicket);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.SupportIssueID = new SelectList(db.SupportIssues, "SupportIssueID", "Name", vm.SupportIssueID);
ViewBag.TicketPriorityID = new SelectList(db.TicketPriorities, "TicketPriorityID", "Name", vm.TicketPriorityID);
return View(vm);
}
I was able to get this working. The documentation is either very outdated or just doesn't explain how to do this very well (probably both). But I was able to get this working by making the following change to my Razor syntax:
<div class="editor-label">
#Html.LabelFor(model => model.DetailMessage)
</div>
<div class="editor-field">
#*#Html.EditorFor(model => model.DetailMessage)*#
#Html.ValidationMessageFor(model => model.DetailMessage)
<br />
#{ Html.Telerik().EditorFor(model => model.DetailMessage)
//.Name("DetailMessageEditor")
.HtmlAttributes(new { style = "height: 200px" })
.Encode(true)
.Render();
}
</div>
Removing the "Name" property from the control solved the problem of not getting anything back, but when I tried to save, I immediately got an error (something to do with XSS, cross-site scripting), and I assumed it was because the HTML wasn't being encoded. I changed the Encode property to true, and now all is good.
Came across a similar thing today.
I was using a View Model and the property I wanted to bind to was was a property of a child object in the View Model.
When I submitted, the value in the RTE was not being bound. When I looked in Request.From object I could see the the value was being returned in the correct format for it to bound in the usual way so I was a little confused.
Anyway, if you want it to bind you need to give the exact name of your property to the the RTE so in your case
.Name("DetailMessage")
should work but
.Name("DetailMessageEditor")
will not.
Im my case i had to name the RTE
.Name("object.Property")
where object is the child object on the View model where my property lives to get it to work
Hope this helps someone.

custom validator in asp.net mvc3

I have created a custom validator in my asp.net mvc3 application like this:
{
if (customerToValidate.FirstName == customerToValidate.LastName)
return new ValidationResult("First Name and Last Name can not be same.");
return ValidationResult.Success;
}
public static ValidationResult ValidateFirstName(string firstName, ValidationContext context)
{
if (firstName == "Nadeem")
{
return new ValidationResult("First Name can not be Nadeem", new List<string> { "FirstName" });
}
return ValidationResult.Success;
}
and I have decorated my model like this:
[CustomValidation(typeof(CustomerValidator), "ValidateCustomer")]
public class Customer
{
public int Id { get; set; }
[CustomValidation(typeof(CustomerValidator), "ValidateFirstName")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
my view is like this:
#model CustomvalidatorSample.Models.Customer
#{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
#using (#Html.BeginForm())
{
#Html.ValidationSummary(false)
<div class="editor-label">
#Html.LabelFor(model => model.FirstName, "First Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.LastName, "Last Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.LastName)
</div>
<div>
<input type="submit" value="Validate" />
</div>
}
But validation doesn't fire. Please suggest solution.
Thanks
How do you know the validation doesn't fire? Are you setting a break point in your controller?
You are not displaying any validation errors in your view. You need to add the following lines to the view.
#Html.ValidationMessageFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.LastName)
You will want to remove the custom validation from the class. Leave it on the properties though.

Resources