ASP.NET MVC 2 - Bind a Model's Property to a Different Named Value

Asp.Net MVC 2 - Bind a model's property to a different named value

You can use the BindAttribute to accomplish this.

public ActionResult Submit([Bind(Prefix = "L")] string[] longPropertyName) {

}

Update

Since the 'longPropertyName' parameter is part of the model object, and not an independent parameter of the controller action, you have a couple of other choices.

You could keep the model and the property as independent parameters to your action and then manually merge the data together in the action method.

public ActionResult Submit(MyModel myModel, [Bind(Prefix = "L")] string[] longPropertyName) {
if(myModel != null) {
myModel.LongPropertyName = longPropertyName;
}
}

Another option would be implementing a custom Model Binder that performs the parameter value assignment (as above) manually, but that is most likely overkill. Here's an example of one, if you're interested: Flags Enumeration Model Binder.

Model Binding to Multiple Types with Same Property Names

Use:

public JsonResult Submit([Bind(Prefix = "audience")]Audience aud,[Bind(Prefix = "locale")]Locale loc)
{
// do stuff
}

Every Audience field should have "audience" prefix in name in html field: audience.id,audience.name,...

<input id="audience_name" name="audience.Name" type="text" value="" />

Bind to a POCO model using different property names

You could add data annotations over your POCO Model.

Here is better explanation: https://stackoverflow.com/a/15916121/9233618

public class TeamScore
{
[JsonProperty("first_name_text ")]
public string FirstName { get; set; }
[JsonProperty("last_name_text ")]
public string LastName { get; set; }
}

MVC Model that includes property of a different Model

I was until now unaware of this thing called a "Navigation Property". So Site, being a property of a Device which is it's own Model, links the two tables together.

So what I was missing, as Steve Greene had clued me in on, was Including Site along with Device in the models for the views in which I wanted to use the Site's Name (in the list for Index, and using a Predicate for Details and Delete)...called "Eager Loading".

I also moved the code for generating the list of items for the DropDownFor to the Controller, as suggested, using a ViewData. Now as stated in the question, because that is passing back only the Site ID I have to manually set the Device's Site based on the ID of the item they selected.

So here is what my Devices Controller looks like now:

        Async Function Index() As Task(Of ActionResult)
'Need to have this include Site so that it will pull in that data in order to reference site name in the View
'(doing this is called Eager Loading)
Dim devices = db.Devices.Include("Site").ToListAsync()
Return View(Await devices)
End Function

' GET: Devices/Details/5
Async Function Details(ByVal id As Integer?) As Task(Of ActionResult)
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim device As Device = Await db.Devices.FindAsync(id)
If IsNothing(device) Then
Return HttpNotFound()
Else
'Need to have this include Site using Predicate so that it will pull in that data in order to reference site name in the View
device = db.Devices.Include("Site").Where(Function(x) x.DeviceID = id).FirstOrDefault()
End If
Return View(device)
End Function

' GET: Devices/Create
Function Create()
'Create the list of items that the DropDownFor in the View will reference
Dim selectSite As New List(Of SelectListItem)
Dim db = New TestWebApplication2.Models.TestWebApplication2Context
Dim listOfSites As List(Of Site)
listOfSites = (From site In db.Sites Select site).ToList()

For Each item In listOfSites
selectSite.Add(New SelectListItem() With {.Value = item.SiteID, .Text = item.Name})
Next
ViewData("selectSite") = selectSite

Return View()
End Function

' POST: Devices/Create
'To protect from overposting attacks, please enable the specific properties you want to bind to, for
'more details see http://go.microsoft.com/fwlink/?LinkId=317598.
<HttpPost()>
<ValidateAntiForgeryToken()>
Async Function Create(<Bind(Include:="DeviceID,Make,Model,Serial")> ByVal device As Device) As Task(Of ActionResult)
If ModelState.IsValid Then
'Because Site is selected in a DropDownFor which only passes the ID integer, need to set the device's site property accordingly
'Note that with Site being a Navigation property, it looks like it doesn't get saved the way you would think...but it all works
Dim thisSite As Integer = CInt(Request.Form.Get("Site"))
device.Site = (From site In db.Sites Where site.SiteID = thisSite Select site).FirstOrDefault()
db.Devices.Add(device)
Await db.SaveChangesAsync()
Return RedirectToAction("Index")
End If
Return View(device)
End Function

' GET: Devices/Edit/5
Async Function Edit(ByVal id As Integer?) As Task(Of ActionResult)
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim device As Device = Await db.Devices.FindAsync(id)
If IsNothing(device) Then
Return HttpNotFound()
Else
'Create the list of items that the DropDownFor in the View will reference
Dim selectSite As New List(Of SelectListItem)
Dim db = New TestWebApplication2.Models.TestWebApplication2Context
Dim listOfSites As List(Of Site)
listOfSites = (From site In db.Sites Select site).ToList()

For Each item In listOfSites
selectSite.Add(New SelectListItem() With {.Value = item.SiteID, .Text = item.Name})
Next
ViewData("selectSite") = selectSite
End If

Return View(device)
End Function

' POST: Devices/Edit/5
'To protect from overposting attacks, please enable the specific properties you want to bind to, for
'more details see http://go.microsoft.com/fwlink/?LinkId=317598.
<HttpPost()>
<ValidateAntiForgeryToken()>
Async Function Edit(<Bind(Include:="DeviceID,Make,Model,Serial")> ByVal device As Device) As Task(Of ActionResult)
If ModelState.IsValid Then
'Because Site is selected in a DropDownFor which only passes the ID integer, need to set the device's site property accordingly
'Note that with Site being a Navigation property, it looks like it doesn't get saved the way you would think...but it all works
Dim thisSite As Integer = CInt(Request.Form.Get("Site"))
device.Site = (From site In db.Sites Where site.SiteID = thisSite Select site).FirstOrDefault()
db.Entry(device).State = EntityState.Modified
Await db.SaveChangesAsync()
Return RedirectToAction("Index")
End If
Return View(device)
End Function

' GET: Devices/Delete/5
Async Function Delete(ByVal id As Integer?) As Task(Of ActionResult)
If IsNothing(id) Then
Return New HttpStatusCodeResult(HttpStatusCode.BadRequest)
End If
Dim device As Device = Await db.Devices.FindAsync(id)
If IsNothing(device) Then
Return HttpNotFound()
Else
'Need to have this include Site using Predicate so that it will pull in that data in order to reference site name in the View
device = db.Devices.Include("Site").Where(Function(x) x.DeviceID = id).FirstOrDefault()
End If
Return View(device)
End Function

' POST: Devices/Delete/5
<HttpPost()>
<ActionName("Delete")>
<ValidateAntiForgeryToken()>
Async Function DeleteConfirmed(ByVal id As Integer) As Task(Of ActionResult)
Dim device As Device = Await db.Devices.FindAsync(id)
db.Devices.Remove(device)
Await db.SaveChangesAsync()
Return RedirectToAction("Index")
End Function

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If (disposing) Then
db.Dispose()
End If
MyBase.Dispose(disposing)
End Sub

How to bind URL parameters to model properties with different names

The model binder matches the parameters it gets from the view to the model you have in the action by the Names, so if they won't match the binding will not work.

options you got:

  1. Match the inputs names to the model properties names... but You said you can't do this, (for some unknown reason).
  2. Write custom Model Binder. *
  3. Use The Bind attribute with prefix - though it'll still force you to have input names close to the model properties names.

so basically, You can't do exactly what you want.


Update:

You wrote in a comment that the properties CAN match the parameters names, so instead of write custom attributes that maybe will succeeded to do the binding, just write a ViewModel (The VM fromMVC...) to adjust the url parameters names.

Writing custom model binder is not recommended by the MVC team:

In general, we recommend folks don’t write custom model binders because they’re difficult to get right and they’re rarely needed

from here

MVC 3 Bind Model property to 2 fields (e.g. Other Title)

a better solution is not to bind to two fields, instead, copy selected item from a drop-down into bound textbox with some clever javascript:

@Html.DropDownList("ddlTitle", new SelectList(Model.TitleList), "Please select")
@Html.TextBoxFor(m => m.Title, new { maxLength = 10 })

Javascript:

ToggleTitleFields = function () {
var title, txtTitle;
title = $('select#ddlTitle').val();
txtTitle = $('input#Title');
if (title === "Other") {
txtTitle.val("");
txtTitle.show();
return txtTitle.focus();
} else {
txtTitle.hide();
txtTitle.val(title);
return $('span[data-valmsg-for="Title"]').empty();
}
};

$(document).on("change", "select#ddlTitle", function(e) {
return ToggleTitleFields();
});

hope this helps somebody



Related Topics



Leave a reply



Submit