Two fields with the same name - c#

I have a ViewModel class to encapsulate "Personal" and "Business" models. My problem is that both models have a property called "Email" and the model binding is not able to make a distinction between the two.
I read that [Bind(Prefix = ... is used to resolved this issue, but I have not been able to see a concise example on how to achieve this.
public class BusinessFormViewModel
{
public Business Business { get; set; }
public ContactPerson ContactPerson { get; set; }
public BusinessFromView(Business business, ContactPerson contactPerson)
{
Business = business;
ContactPerson = contactPerson;
}
}
How do I use the Bind Prefix to fix this?

I believe that if the form elements that are posted have prefixes included in the name, the binding will be done properly. This is how the templated helpers (i.e. EditorFor) renders the controls, and my nested viewmodels are bound properly. For example, in your case, your view would have form elements something like this:
...
<input type="text" name="Business.Email" value="<%=this.Model.Business.Email %>" />
...
<input type="text" name="ContactPerson.Email" value="<%=this.Model.ContactPerson.Email %>" />
...
Or, using templated helpers (in mvc 2):
...
<%= Html.TextBoxFor(m => m.Business.Email) %>
...
<%= Html.TextBoxFor(m => m.ContactPerson.Email) %>
...
And your controller would simply take a BusinessFormViewModel as a parameter, as so:
public BusinessFromView(BusinessFormViewModel businessForm)
{
Business = businessForm.Business;
ContactPerson = businessForm.ContactPerson;
}

Related

How to pass back model to controller using razor dropdownlist

I have a DropDownListFor control that I am wanting to show a display value that resides in a property within a model/class (this is the Rule class.) The view's model is actually a collection of these model/classes. However, when I select the item from the DropDownList, I want to send back the entire model as a parameter. I have this working perfectly with the following code, but the Name property within the parameter is coming back as null. The other properties all have appropriate values.
View Code:
#model List<StockTrader.Web.Data.Rule>
#{
ViewBag.Title = "Configure Rules";
}
<h2>#ViewBag.Title</h2>
<h4>Choose a rule to edit:</h4>
<form method="post" id="rulesform" action="SaveRules">
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"))
<div style="margin-bottom: 15px;">
<label>Value:</label><br />
<input type="number" name="Value" style="margin-bottom: 15px;" /><br />
<button>Save Value</button>
</div>
Controller Code:
public ActionResult SaveRules(Rule model)
{
//do something
}
Rule Class:
public class Rule
{
public int RuleID { get; set; }
public string Name { get; set; }
public int Value { get; set; }
public bool IsDeleted { get; set; }
}
We do have Kendo controls, so if another control would be more appropriate, that is an option.
I would be glad to provide anymore code or information you might need.
Any thoughts or ideas?
EDIT:
So it turns out this is what I needed to do, the accepted answer got me to this point so I'm going to leave it checked.
View Code (w/script included):
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"), new { id = "ruleid", #onchange = "CallChangefunc(this)" })
#Html.HiddenFor(m => m.First().Name, new { id = "rulename" })
function CallChangefunc(e) {
var name = e.options[e.selectedIndex].text;
$("#rulename").val(name);
}
You will need a hidden field for it,and use dropdownlist on change event on client side to update hidden field:
#Html.DropDownListFor(m => m.First().RuleID, new SelectList(Model.AsEnumerable(), "RuleID", "Name"),new { id= "ruleid" })
#Html.HiddenFor(m=>m.First().Name,new { id="rulename" })
and jquery code:
$("#ruleid").change(function(){
$("#rulename").val($(this).text());
});
Second option isif Rule collection is coming from database you can fetch RuleName by using id to by querying db in action.
it can be achieved by using UIHint
On your model class, on the RuleID property, add an annotation for UIHint. It basically lets you render a partial (cshtml) for the property. So, on the partial, you can have the template for generating the dropdwon with required styling. When Page is generated. Now you can use the same Html.DropDownListFor for RuleID and UI generates a dropdown for it.
This will avoid having additional jQuery code to get the dropdown value, and code is more concise and testable.

MVC Razor Html.Partial sub model

I have a problem concerning Partial views in MVC Razor. Any help is highly appreciated, it's most likely something I've missed, but I could find nothing while searching that had the same problem replicated.
So I'm binding my view to a view model.
public class Person
{
public string Name { get; set; }
public virtual ContactInformation ContactInformation { get; set; }
}
And then I have a view with a partial to render the contact information model.
<div>
#Model.Name
</div>
<div>
#Html.Partial("_ContactInformation", Model.ContactInformation)
</div>
However, the "_ContactInformation" view is rendered without ContactInformation in the nameattribute of the <input>s
Usually razor binds the name attribute to something like: name="ContactInformation.Address". But since it's a partial it gets rendered as name="Address".
Am I missing something or is this the intended way for it to work?
You have two options. Option 1 - specify the prefix explicitly:
#Html.Partial("_ContactInformation", Model.ContactInformation, new ViewDataDictionary
{
TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "ContactInformation" }
})
Options 2 is to turn partial view into an editor template for your model and than use EditorFor helper method, that should be able to add prefixes for you:
#Html.EditorFor(m => m.ContactInformation)

Hide property of model in dynamic view

I have a dynamic view, this will display any model that has been passed to it.
#model dynamic
#using (Html.BeginForm("Edit", null, FormMethod.Post, new { id="FrmIndex" }))
{
#Html.ValidationSummary(true);
#Html.EditorForModel()
<input type="submit" value="Edit" />
}
Say one of my model is PartyRole
public partial class PartyRole
{
[Key, Display(Name = "Id"]
[UIHint("Hidden")]
public int PartyRoleId { get; set; }
[UIHint("TextBox")]
public string Title { get; set; }
}
I dont want to show Id in edit mode, so I am hiding it in Hidden.cshtml editorfortemplate as below:
#Html.HiddenFor(m => Model)
This is hiding the editor, but not the label "Id".
And I cannot use the answers provided here, How to exclude a field from #Html.EditForModel() but have it show using Html.DisplayForModel()
because IMetadataAware requires System.Web.Mvc namespace which I cannot add in my Biz projects that are having the poco model classes.
I cannot use [HiddenInput(DisplayValue = false)] also because this is also party of web.mvc
can somebody give a solution??
I think that the thing to do is create a custom Object.cshtml editor template, as described in
http://www.headcrash.us/blog/2011/09/custom-display-and-editor-templates-with-asp-net-mvc-3-razor/
(nb. I found How to add assembly in web.config file of mvc 4 to be helpful with the System.Data.EntityState reference.)
Within that template you can put appropriate code to hide the label. The following is a dumb example - I guess that I'd probably try to pick up a custom attribute, though apparently this would involve an overload of DataAnnotationsModelMetadataProvider.
if (prop.HideSurroundingHtml)
{
#Html.Editor(prop.PropertyName)
}
else if (prop.PropertyName == "PartyRoleId")
{
<div></div>
}
else if (!string.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString()))
{
<div class="editor-label">#Html.Label(prop.PropertyName)</div>
}

How can I make sure model binding will work as expected?

I'm programming in C# ASP.NET MVC4 (Razor engine). I have a need to create a partial view and reuse it in multiple places. The problem is that the view is a form, and in some cases I will need to use it with a ViewModel. My question is how the model binding will work because in a ViewModel it will be a property of a property. Example:
public class PersonModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class OfficeViewModel
{
public PersonModel Person { get; set; }
// other properties
}
The partial view for the PersonModel would be:
#model SomeNameSpace.PersonModel
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
When this view is rendered it would look like this:
<input type="textbox" id="FirstName" name="FirstName" />
<input type="textbox" id="LastName" name="LastName" />
Now I want to use this same view with my OfficeViewModel. In that case I would do this in my Office view:
#{ Html.RenderPartial("Person", Model.Person); }
When this partial view is rendered it will be rendered as shown above. If I were to NOT reuse that view, my Office view will be like this:
#model SomeNameSpace.OfficeViewModel
#Html.TextBoxFor(m => m.Person.FirstName)
#Html.TextBoxFor(m => m.Person.LastName)
That would be rendered as:
<input type="textbox" id="Person_FirstName" name="Person.FirstName" />
<input type="textbox" id="Person_LastName" name="Person.LastName" />
Notice how the name attribute has the Person prefix property. So if I use the RenderPartial option and pass in the Model.Person, will the model binder know where to bind the FirstName and LastName in the OfficeViewModel?
If the model binder is smart enough to check for properties of properties, what happens when I have a ManagerModel and EmployeeModel in the OfficeViewModel and they both have properties named FirstName and LastName?
I hope I have been clear, and thanks in advance.
Unfortunately, Html.RenderPartial does not carry over the information necessary to come up with the correct field names in this situation.
If you want to reuse a partial view in this way, look into using Editor Templates with Html.EditorFor instead. Like the other *For helpers, EditorFor takes a lambda expression which allows it to carry over the name of the property being passed in to the template.
#Gerald is right. Default RenderPartial won't figure it out. Though you can write a custom helper that will take care of that issue:
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = name
}
};
return helper.Partial(partialViewName, model, viewData);
}
Use it in the View like that:
#Html.PartialFor(x => x.Person, "Person")
Found the answer here: https://stackoverflow.com/a/4807655/78739
Basically I need to set the ViewData.TemplateInfo.HtmlFieldPrefix.
So I added a property to the PersonModel and called it HtmlFieldPrefix. Example:
public class PersonModel
{
public string HtmlFieldPrefix { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then in my partial view I do this:
#model SomeNameSpace.PersonModel
#{
ViewData.TemplateInfo.HtmlFieldPrefix = Model.HtmlFieldPrefix;
}
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
In my controller action I can do this:
model.HtmlFieldPrefix = "Person";
return View("_Person", model);
Now my rendered view will look like this:
<input type="textbox" id="Person_FirstName" name="Person.FirstName" />
<input type="textbox" id="Person_LastName" name="Person.LastName" />
If I leave the model.HtmlFieldPrefix as null, the view will render as:
<input type="textbox" id="FirstName" name="FirstName" />
<input type="textbox" id="LastName" name="LastName" />
So this fixes my issue, and allows me to use the partial view with ViewModels.

Entity Framework with strongly-typed MVC

I'm using the ASP.NET MVC and the ADO.NET Entity Framework together.
I want my Views and Controllers strongly-typed.
But how am I supposed to handle entity associations?
Here's a simple example:
A Person has one Department. A Department has zero or more People.
My controller passes an instance of a Person object and a collection of all the Department objects to the View.
public class PersonController : Controller
{
...
//
// GET: /Person/Create
public ActionResult Create()
{
Person Model = new Person();
Model.Id = Guid.NewGuid();
ViewData["Departments"] = db.Department;
return View(Model);
}
...
}
My View has a "Department" DropDownList with all departments as options.
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Fields</legend>
<p>
<label for="Id">Id:</label>
<%= Html.TextBox("Id") %>
<%= Html.ValidationMessage("Id", "*") %>
</p>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name") %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Department">Family:</label>
<%= Html.DropDownList("Department", new SelectList((IEnumerable)ViewData["Departments"], "Id", "Name"))%>
<%= Html.ValidationMessage("Department", "*")%>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
But, the Person object posted to the controller has no Department and fails!
public class PersonController : Controller
{
...
//
// POST: /Person/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person Model)
{
try
{
db.AddToPerson(Model);
db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
...
}
Why isn't the selected Department from DropDownList "Department" automatically added to the model Person?
How do I use the ADO.NET Entity Framework and ASP.NET MVC with strongly-typed Views and Controllers?
I will try to guide you from the MVC side, as this is where the problem is, I believe
In the Create() method you create the Person object and the list of Departments from the database, then both objects are passed to the View. The View takes the data from the Department list and uses it to render an HTML form - using only Id and Name.
In the next step the form is submitted to the server as a collection of value-key pairs (standard POST). The routing engine takes the requested url from the action attribute and resolves it to PersonController.Create(Person Model) action. The argument of this method is Person, so the data binder kicks in, creates the new instance of Person class and tries to match the incoming data with properties of the Person. In case of Department the incoming value is the Id of the department (because this is what you set as a value member for the DropDownList), while the property Department on the Person class is probably of Department type. This is a mismatch, so it cannot fill the property and it is left empty.
As you can see, this is not the limitation of DropDownList, the problem is that you cannot pass all Department data to the DropDownList and have it recreated during save (like with the Person), because of a nature of the POST request, and that is why the DropDownList takes only two values from each Department (value and name).
My usual solution: as normally my models are not the same classes as my business objects, I do this by having two properties on the model: get only IEnumerable property, and another DepartmentId property (get/set). Then I use it like this:
<%= Html.DropDownList("DepartmentId", Model.Departments) %>
Then in the save action, I grab the Department from the db using DepartmentId, assign it to Person and save.
In your case (models being business objects) I would probably not try to bind the Department to the Person model automatically, but just grab the Id and do it by myself.
This is just a guess (I'm not an EF specialist), but I think you may have another issue here: if the db is a field on Controller, and it is recreated at each request, this may be some unnecessary overhead. I hope it doesn't open a db connection every time, please check it.
Hope that helps
I use a ViewModel (check out the NerdDinner tutorial, references at the bottom).
First, you need to fake a foreign key constraint by extending your model in a partial:
public partial class Person
{
public int DepartmentId
{
get
{
if(this.DepartmentsReference.EntityKey == null) return 0;
else
return (int)this.DepartmentsReference.EntityKey.EntityKeyValues[0].Value;
}
set
{
this.DepartmentsReference.EntityKey =
new EntityKey("YourEntities.DepartmentSet", "Id", value);
}
}
}
Second, create the ViewModel:
public class PersonFormViewModel
{
public SelectList Departments { get; set: }
public Person Pers { get; set; }
public PersonFormViewModel(Person p, List<Department> departments)
{
this.Pers = p;
this.Departments = new SelectList(departments, "Id", "Name", p.DepartmentId);
}
}
Third, the controller action (an abbreviated create example):
public ActionResult Create()
{
YourEntities entities = new YourEntities();
List<Department> departments = entities.DepartmentSet.ToList();
PersonFormViewModel viewModel =
new PersonFormViewModel(new Person(), departments);
return View(modelView);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude="Id")]Person personToCreate
{
YourEntities entities = new YourEntities(); // Probably not instantiated here
entities.AddToPersonSet(personToCreate);
entities.SaveChanges();
redirectToAction("Index");
}
Fourth, the view snippet:
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name", Model.Pers.Name) %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="DepartmentId">Family:</label>
<%= Html.DropDownList("DepartmentId", Model.Departments)%>
<%= Html.ValidationMessage("DepartmentId", "*")%>
</p>
References:
NerdDinner Tutorial
How to Fake Foreign Key Properties
I haven't found that the out-of-the-box DropDownList helper works very well with model binding. I always have to grab the value of a DropDownList out of ViewData and map the value up manually in my controller prior to committing the changes to the repository or db.
Most of the time I'm using a DropDownList to show options for a custom type relationship (such as Departments for a Person). MVC can't know how to map up an object simply based on the value of a selected item in a list. Rather, you have to go grab that entity using the selected value and map it up yourself. I don't know if that's the problem here, and I haven't tried model binding a list to a model with just simple types as options (such as quantity of products to purchase or something like that), but I'd be curious to know more about this particular problem and how others are managing model binding with drop down lists.

Categories

Resources