ASp.NET MVC: TryUpdateModel doesn't update all properties - asp.net-mvc

I've got the following action:
public ActionResult Create()
{
var entity = new Employee();
TryUpdateModel(entity, new[] { "Person.Name", "Code", "CompanyID" });
if (ModelState.IsValid)
{
var result = Service.MergeEmployee(entity);
return RedirectToAction("List", new { success = true });
}
return View("Edit", new SupplierEmployeeModel() { Employee = entity });
}
What happens is that the property "Person.Name" doesn't get filled by the TryUpdateModel.
This is my form:
<fieldset>
<p>
<label for="Name"><%=Strings.NAME %></label>
<%= Html.TextBox("Person.Name", Model.Employee.Person.Name, new { Class = "text" })%>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="CompanyID"><%=Strings.SUPPLIER %></label>
<%= Html.DropDownList("CompanyID") %>
<%= Html.ValidationMessage("CompanyID", "*")%>
</p>
<p>
<label for="Code"><%=Strings.CODE %></label>
<%= Html.TextBox("Code", Model.Employee.Code)%>
<%= Html.ValidationMessage("Code", "*") %>
</p>
<p>
<%= Html.Hidden("ID", Model.Employee.ID)%>
</p>
<div id="tabs-DE-actions" class="ui-dialog-buttonpane ui-helper-clearfix" style="display: block;">
<button class="ui-state-default ui-corner-all" type="submit"><%=Strings.SAVE%></button>
</div>
</fieldset>
Any thoughts on why this is happening?
Thanks

Make sure the Person object is initialized in the Employee constructor; if it's null to begin with it is probably not updated properly.
public Employee()
{
Person = new Person();
}

Try this:
TryUpdateModel(entity,"Person", new[] { "Name", "Code", "CompanyID" });

In order to fill in Person.Name, the model binder has to create a new Person. Have you given the model binder enough info to do that? Alternately, try creating the Person yourself before binding.

Related

Display Error message on same view

I'm trying to delete a record from the database using MVC 2. currently delete function works fine but there are some records with foreign key relations so i don't wont them to be deleted and when user try to delete such a record i want to show a error message on the delete view without navigating to another view.
Controller:
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
try
{
// TODO: Add delete logic here
StockRepository rep = new StockRepository();
Stock stock = rep.GetStock(id);
rep.Delete(stock);
rep.Save();
return RedirectToAction("Index");
}
catch
{
//need to display an error message if unable to delete
return View();
}
}
View:
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>Fields</legend>
<div class="display-label">StockID</div>
<div class="display-field"><%: Model.StockID %></div>
<div class="display-label">ClientName</div>
<div class="display-field"><%: Model.ClientName %></div>
<div class="display-label">ItemName</div>
<div class="display-field"><%: Model.ItemName %></div>
<div class="display-label">ItemCount</div>
<div class="display-field"><%: Model.ItemCount %></div>
<div class="display-label">Price</div>
<div class="display-field"><%: String.Format("{0:F}", Model.Price) %></div>
<div class="display-label">OtherExpences</div>
<div class="display-field"><%: String.Format("{0:F}", Model.OtherExpences) %></div>
<div class="display-label">TotalStockValue</div>
<div class="display-field"><%: String.Format("{0:F}", Model.TotalStockValue) %></div>
<div class="display-label">DeliveryDate</div>
<div class="display-field"><%: String.Format("{0:d}", Model.DeliveryDate) %></div>
<div class="display-label">Description</div>
<div class="display-field"><%: Model.Description %></div>
</fieldset>
<% using (Html.BeginForm()) { %>
<p>
<input type="submit" value="Delete" /> |
<%: Html.ActionLink("Back to List", "Index") %>
</p>
<% } %>
Using Viewdata
View
<%
if (ViewData["dbError"] != null)
{
%>
//display ViewData dbError
<%
}
%>
Controllor
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
try
{
// TODO: Add delete logic here
StockRepository rep = new StockRepository();
Stock stock = rep.GetStock(id);
rep.Delete(stock);
rep.Save();
return RedirectToAction("Index");
}
catch
{
//need to display an error message if unable to delete
**ViewData["dbError"] = "Error message here";**
return View();
}
}

How to create a custom Html.ValidationMessage?

This is my Controller:
/// <summary>
/// Activity
/// </summary>
/// <returns></returns>
public ActionResult CreateActivity()
{
AriaCRMEntities aria = new AriaCRMEntities();
var unit = from u in aria.ActivityGroupIDs select u;
List<SelectListItem> lst = new List<SelectListItem>();
foreach (var u in unit)
{
lst.Add(new SelectListItem { Text = u.ActivityGroupName.ToString(), Value = u.ActivityGroupID_FK.ToString() });
}
ViewData["activity"] = lst;
return View();
}
[HttpPost]
public ActionResult CreateActivity(FormCollection model)
{
if (ModelState.IsValid)
{
Activity activ = new Activity();
if (!string.IsNullOrEmpty(model["ActivityGroupID_FK"]))
{
AriaCRMEntities aria = new AriaCRMEntities();
activ.ActivityGroupID_FK = Int32.Parse(model["ActivityGroupID_FK"]);
activ.ActivityType = model["ActivityType"];
activ.ActivityDes = model["ActivityDes"];
aria.Activities.AddObject(activ);
aria.SaveChanges();
return RedirectToAction("Create");
}
}
return View(model);
}
This is my View :
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true,) %>
<fieldset>
<legend>Fields</legend>
<br />
<%:Html.DropDownListFor(model=>model.ActivityGroupID_FK , (IEnumerable<SelectListItem>)ViewData["activity"], "انتخاب نوع فعالیت")%><br />
<%Html.ValidationMessageFor (model=>model.ActivityGroupID_FK,"dddddddddd"); %>
<div class="editor-label">
<%: Html.LabelFor(model => model.ActivityType) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ActivityType) %>
<%: Html.ValidationMessageFor(model => model.ActivityType,"dfsaaa") %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.ActivityDes) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ActivityDes) %>
<%: Html.ValidationMessageFor(model => model.ActivityDes) %>
</div>
<p>
<input type="submit" value="Create" id="btn"/>
</p>
</fieldset>
<% } %>
But now I need to validate <%:Html.DropDownListFor%>. How do I create custom validation?
If you want to display a validation error message next to your drop-down list, you can do this from your controller like so:
ModelState.AddModelError("ActivityGroupID_FK", "The selected value is invalid.");
Update
I just noticed the validation message for your DropDownList looks like this:
<%Html.ValidationMessageFor (model=>model.ActivityGroupID_FK,"dddddddddd"); %>
You might want to change that like so:
<%: Html.ValidationMessageFor(model=>model.ActivityGroupID_FK,"dddddddddd") %>

ASP.NET MVC Model State Validations

<%= Html.ValidationSummary("Account creation was unsuccessful. Please correct the errors and try again.") %>
</div>
<% using (Html.BeginForm("Register", "Account" , FormMethod.Post))
{ %>
<div>
<fieldset>
<legend>Account Information</legend>
<p>
<label for="username">User Name:</label>
<%= Html.TextBox("username") %>
<%= Html.ValidationMessage("username") %>
</p>
<p>
<label for="FirstName">First Name</label>
<%= Html.TextBox("firstName") %>
<%= Html.ValidationMessage("firstName") %>
</p>
<p>
<label for="LastName">Last Name</label>
<%= Html.TextBox("lastName") %>
<%= Html.ValidationMessage("lastName") %>
</p>
<p>
<label for="email">Email:</label>
<%= Html.TextBox("email") %>
<%= Html.ValidationMessage("email") %>
</p>
<p>
<label for="password">Password:</label>
<%= Html.Password("password") %>
<%= Html.ValidationMessage("password") %>
</p>
<p>
<label for="confirmPassword">Confirm password:</label>
<%= Html.Password("confirmPassword") %>
<%= Html.ValidationMessage("confirmPassword") %>
</p>
<p>
<label for="Role">Role:</label>
<%= Html.DropDownList("Role",((SelectList)ViewData["Roles"]),"--Select One---") %>
</p>
<p>
<input type="submit" value="Register" />
</p>
</fieldset>
</div>
<% } %>
private ModelStateDictionary _modelState;
public AccountController() : this(null, null)
{
_modelState = new ModelStateDictionary();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Register(string username, string firstName, string lastName, string password, string confirmPassword, string email, string role)
{
try
{
if (string.IsNullOrEmpty(password))
_modelState.AddModelError("password", "passowrd field is empty");
if (string.IsNullOrEmpty(confirmPassword))
_modelState.AddModelError("confirmPassword", "Confim Passowrd field is empty");
if (string.IsNullOrEmpty(username))
_modelState.AddModelError("username", "UserName field is empty");
if (string.IsNullOrEmpty(email))
_modelState.AddModelError("email", "Email field cannot be empty");
Regex regEmail = new Regex(#"\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*");
if (!regEmail.IsMatch(email))
_modelState.AddModelError("email", " The email id submitted is not in valid format");
if (string.IsNullOrEmpty(firstName))
_modelState.AddModelError("firstName", "First name field is empty");
if (string.IsNullOrEmpty(lastName))
_modelState.AddModelError("lastName", "Last name field is empty");
if (!password.Equals(confirmPassword, StringComparison.InvariantCultureIgnoreCase))
_modelState.AddModelError("password", "Password do not match");
if (_modelState.IsValid)
{
int id = _UsrService.GetRoleId(role);
Data.User usr = new User(username, firstName, lastName, email, DateTime.Now, null, id);
string retRegister = _UsrService.RegisterUser(usr, password, confirmPassword, "none", "none");
if (retRegister.Equals("true"))
{
UserRolesControl contrl = new UserRolesControl(Users(), Roles());
return View("Control", contrl);
}
else
{
ModelState.AddModelError("_Form", retRegister);
ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
var roles = _UsrService.GetRoles().ToList();
ViewData["Roles"] = new SelectList(roles);
return View();
}
}
else
{
var roles = _UsrService.GetRoles().ToList();
ViewData["Roles"] = new SelectList(roles);
return View();
}
}
catch (Exception ex)
{
return View();
}
}
Above is a registrations form, I am working on validations on it. It does run through fine in the controller method , but it does not display the error messages when it send back to register page. It there anything wrong with my code?
What's _modelState? Why not use ModelState instead?
Or just Data Annotations for client side validation as well.
In this code, you are not returning the ModelState, that is why no errors are showing. Just use ModelState instead of _modelState, and you should be all set.:
if (_modelState.IsValid)
{
//blah
}
else
{
var roles = _UsrService.GetRoles().ToList();
ViewData["Roles"] = new SelectList(roles);
return View();
}

Form File Upload with other TextBox Inputs + Creating Custom Form Action attribute

I am attempting to create a form where a user is able to enter your typical form values textboxes etc, but also upload a file as part of the form submission. This is my View code it can be seen that the File upload is identified by the MCF id:
<% using (Html.BeginForm("Create", "Problem", FormMethod.Post, new { id = "ProblemForm", enctype = "multipart/form-data" }))
{%>
<p>
<label for="StudentEmail">Student Email (*)</label>
<br />
<%= Html.TextBox("StudentEmail", Model.Problem.StudentEmail, new { size = "30", maxlength=26 })%>
<%= Html.ValidationMessage("StudentEmail", "*") %>
</p>
<p>
<label for="Type">Communication Type (*)</label>
<br />
<%= Html.DropDownList("Type") %>
<%= Html.ValidationMessage("Type", "*") %>
</p>
<p>
<label for="ProblemDateTime">Problem Date (*)</label>
<br />
<%= Html.TextBox("ProblemDateTime", String.Format("{0:d}", Model.Problem.ProblemDateTime), new { maxlength = 10 })%>
<%= Html.ValidationMessage("ProblemDateTime", "*") %>
</p>
<p>
<label for="ProblemCategory">Problem Category (* OR Problem Outline)</label>
<br />
<%= Html.DropDownList("ProblemCategory", null, "Please Select...")%>
<%= Html.ValidationMessage("ProblemCategory", "*")%>
</p>
<p>
<label for="ProblemOutline">Problem Outline (* OR Problem Category)</label>
<br />
<%= Html.TextArea("ProblemOutline", Model.Problem.ProblemOutline, 6, 75, new { maxlength = 255 })%>
<%= Html.ValidationMessage("ProblemOutline", "*") %>
</p>
<p>
<label for="MCF">Mitigating Circumstance Form</label>
<br />
<input id="MCF" type="file" />
<%= Html.ValidationMessage("MCF", "*") %>
</p>
<p>
<label for="MCL">Mitigating Circumstance Level</label>
<br />
<%= Html.DropDownList("MCL") %>
<%= Html.ValidationMessage("MCL", "*") %>
</p>
<p>
<label for="AbsentFrom">Date Absent From</label>
<br />
<%= Html.TextBox("AbsentFrom", String.Format("{0:d}", Model.Problem.AbsentFrom), new { maxlength = 10 })%>
<%= Html.ValidationMessage("AbsentFrom", "*") %>
</p>
<p>
<label for="AbsentUntil">Date Absent Until</label>
<br />
<%= Html.TextBox("AbsentUntil", String.Format("{0:d}", Model.Problem.AbsentUntil), new { maxlength = 10 })%>
<%= Html.ValidationMessage("AbsentUntil", "*") %>
</p>
<p>
<label for="AssessmentID">Assessment Extension</label>
<br />
<%= Html.DropDownList("AssessmentID") %>
<%= Html.ValidationMessage("AssessmentID", "*") %>
<%= Html.TextBox("DateUntil", String.Format("{0:d}", Model.AssessmentExtension.DateUntil), new { maxlength = 16 })%>
<%= Html.ValidationMessage("DateUntil", "*") %>
</p>
<p>
<label for="Details">Assessment Extension Details</label>
<br />
<%= Html.TextArea("Details", Model.AssessmentExtension.Details, 6, 75, new { maxlength = 255 })%>
<%= Html.ValidationMessage("Details", "*") %>
</p>
<p>
<label for="RequestedFollowUp">Requested Follow Up</label>
<br />
<%= Html.TextBox("RequestedFollowUp", String.Format("{0:d}", Model.Problem.RequestedFollowUp), new { maxlength = 16 })%>
<%= Html.ValidationMessage("RequestedFollowUp", "*") %>
</p>
<p>
<label for="StaffEmail">Staff</label>
<br />
<%= Html.ListBox("StaffEmail", Model.StaffEmail, new { #class = "multiselect" })%>
<%= Html.ValidationMessage("StaffEmail", "*")%>
</p>
<p>
<input class="button" type="submit" value="Create Problem" />
</p>
This is my controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase file, string[] StaffEmail)
{
if (ModelState.IsValid)
{
try
{
Student student = studentRepository.GetStudent(problem.StudentEmail);
Staff currentUserStaffMember = staffRepository.GetStaffWindowsLogon(User.Identity.Name);
var fileName = Path.Combine(Request.MapPath("~/App_Data"), Path.GetFileName(file.FileName));
file.SaveAs(#"C:\Temp\" + fileName);
if (problem.RequestedFollowUp.HasValue)
{
String meetingName = student.FirstName + " " + student.LastName + " " + "Mitigating Circumstance Meeting";
OutlookAppointment outlookAppointment = new OutlookAppointment(currentUserStaffMember.Email, meetingName, (DateTime)problem.RequestedFollowUp, (DateTime)problem.RequestedFollowUp.Value.AddMinutes(30));
}
problemRepository.Add(problem);
problemRepository.Save();
if (assessmentExtension.DateUntil != null)
{
assessmentExtension.ProblemID = problem.ProblemID;
assessmentExtensionRepository.Add(assessmentExtension);
assessmentExtensionRepository.Save();
}
ProblemPrivacy problemPrivacy = new ProblemPrivacy();
problemPrivacy.ProblemID = problem.ProblemID;
problemPrivacy.StaffEmail = currentUserStaffMember.Email;
problemPrivacyRepository.Add(problemPrivacy);
if (StaffEmail != null)
{
for (int i = 0; i < StaffEmail.Length; i++)
{
ProblemPrivacy probPrivacy = new ProblemPrivacy();
probPrivacy.ProblemID = problem.ProblemID;
probPrivacy.StaffEmail = StaffEmail[i];
problemPrivacyRepository.Add(probPrivacy);
}
}
problemPrivacyRepository.Save();
return RedirectToAction("Details", "Student", new { id = student.Email });
}
catch
{
ModelState.AddRuleViolations(problem.GetRuleViolations());
}
}
return View(new ProblemFormViewModel(problem, assessmentExtension, staffMember));
}
This form was working correctly before I had to switch to using a non-AJAX file upload, this was due to an issue with Flash when enabling Windows Authentication which I need to use.
It appears that when I submit the form the file is not sent and I am unsure as to why? I have also been unsuccessful in finding an example online where a file upload is used in conjunction with other input types.
Another query I have is that for Create, and Edit operations I have used a PartialView for my forms to make my application have higher code reuse. The form action is normally generated by just using:
Html.BeginForm()
And this populates the action depending on which Url is being used Edit or Create. However when populating HTML attributes you have to provide a action and controller value to pass HTML attributes.
using (Html.BeginForm("Create", "Problem", FormMethod.Post, new { id = "ProblemForm", enctype = "multipart/form-data" }))
Is it possible to somehow populate the action and controller value depending on the URL to maintain code reuse? Thinking about it whilst typing this I could set two values in the original controller action request view data and then just populate the value using the viewdata values?
Any help on these two issues would be appreciated, I'm new to asp.net mvc :-)
Thanks,
Jon
ANSWER
Ok guys worked out the issue and its incredibly simple I didn't have the HTML name attribute on the file component of my form:
<input id="MCF" name="MCF" type="file" />
Now this binds to my method signature!
With the first issue, it looks like your action method signature is wrong. Because your fileInput has an ID of MCF, the HttpPostedFileBase parameter should have the same name so that the model binder knows to bind to that action method parameter.
E.g.
public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase mcf, string[] StaffEmail)
As for the second issue... you could try something like this:
<form method="post" id="ProblemForm" action="<%= Url.Action(ViewContext.RouteData.Values["action"].ToString()) %>" enctype="multipart/form-data">
The current controller will also be in RouteData.Values but if you're after the area, that'll be in RouteData.DataTokens.
HTHs,
Charles
Ok guys worked out the issue and its incredibly simple I didn't have the HTML name attribute on the file component of my form:
<input id="MCFile" name="MCFile" type="file" />
I have changed my method signature to match the name:
public ActionResult Create(Problem problem, AssessmentExtension assessmentExtension, Staff staffMember, HttpPostedFileBase MCFFile, string[] StaffEmail)
Now this binds to my method signature!

ASP.NET MVC - Loading dropdownlists based on selected value

I have a view that looks somewhat similar to this
<% using (Html.BeginForm()) {%>
<%= Html.DropDownList("Category") %>
<%= Html.DropDownList("SubCategory") %>
<input type="submit" value="Print" />
<%= Html.ActionLink("Cancel", "Index") %>
<% } %>
I was wondering if anyone knew how i could load the subcategory based on the selected Category?
In webforms i'd just use the autopostback event to do this, but i'm a bit confused on how to do this using the mvc framework.
Thanks in advance
transform your view like this:
<% using (Html.BeginForm()) {%>
<%= Html.DropDownList("Category", Model.SelectList, new {onchange = "actualize(this);"}) %>
<div id="selectdiv">
<% Html.RenderPartial("SubCategories"); %>
</div>
<input type="submit" value="Print" />
<%= Html.ActionLink("Cancel", "Index") %>
<% } %>
<script type="text/javascript">
function actualize(obj)
{
$.ajax({
url: url,
async: true,
type: 'POST',
data: { id: obj.value },
dataType: 'text',
success: function(data) { $("#selectdiv").html(data); },
error: function() {
console.log('Erreur');
}
});
}
</script>
create a control called SubCategories.aspx and include in it:
<%= Html.DropDownList("SubCategory",Model.SelectList) %>
create a Model class
public class MyModel
{
public SelectList SelectList {get;set;}
}
create a controller action
public ActionResult SubCategories(int id)
{
MyModel model = new MyModel();
model.SelectList = new SelectList(YourRepository.GetSubCategories(id),"Id","Name");
return View(model);
}
Place the dropdown list within a PartialView. Then when you post back do a return PartialView("viewName", model). Then in the return of your jQuery simply replace the partial view with the new html that is returned.
So you're view;
<div id="myPartialView">
<% Html.PartialView("PartialViewName", model); %>
</div>
Then your jQuery does something like
$('#myPartialView').html = retHtml;
Your C#
return PartialView("PartialViewName", model);
Untested but that's the approach i think you want to take.

Resources