I have a ViewModel which holds my InputModel as a property.
I use HTML helpers like so: #Html.TextBoxFor(m => m.InputModel.UserId) for example.
The problem is that this generates: <input name="InputModel.UserId" />
And the model-binder does not populate the properties in my HttpPost method:
public ActionResult Index(InputModel model)
I see there is a generic version of HTML helpers, I was thinking maybe I can leverage that somehow, but I havn`t yet figured out how. Or maybe there is a completley different way around this, that I'm not seeing since I am new to MVC.
You could either change your action to
public ActionResult Index(ViewModel model)
in which case the model.InputData would be populated or use Bind attribute
public ActionResult Index([Bind(Prefix="InputModel")] InputModel model)
Related
I have to create a textbox from a viewbag property in MVC. I could do the mapping like #Html.TextBox("Comments", (string)ViewBag.Comments) but how do I read it back when the page is posted to the server. It is not filling the viewbag property back. I am very new to MVC so maybe don't understand the concept totally .
Thanks
Your ViewBag wont get updated from your view and that is not the way to get data from your form. Rather, you should either use strongly typed model binding to read your data from your Action Method or you can simply check for the key in your Forms data. I am showing you example for both:
Example 1: Strongly typed model binding.
[HttpPost]
public ActionResult MyAction(string comments)
{
// the Comment from the text box.
return View();
}
Example 2: Reading from Posted Data:
[HttpPost]
public ActionResult MyAction()
{
// the Comment from the text box.
string comments = Request.Form["comments"];
return View();
}
I hope, you will like to use the Example 1.
Anyway, the best practice would be to bind your View with a Model class and use HtmlHelper for generating the text box like :
Html.EditorFor(model => model.Comments)
Where your Model class contains a property named Comments.
And your action method should accept the same Model type as argument. Here is an example:
[HttpPost]
public ActionResult MyAction(MyModel model)
{
string comments = model.Comments;
}
And you should bind your View with the model of type MyModel.
I can understand that, as you are new to MVC, this may not make clear sense now, so, I would suggest you to check out some basic MVC tutorial. You can start from here : http://www.asp.net/mvc/tutorials
In my views I'm using a generic type for the Model, ItemModel<T>.
This allows me to have a basetype on my model and it works fine. Within ItemModel<T> I attach the actual entity of T to a property called 'Item'.
Let's say I'm loading a User item: in my view I would like to do something like this:
<%: Html.TextBoxFor(Model => Model.Item.NickName,
new { id="NickName", name="NickName" })%>
Because the entity is added via the Item property, this generates the input tag's name attribute as 'Item.NickName'. When posting, MVC can no longer relate this to the User.NickName property of the user object in my controller
public ActionResult Login(User user, string redirectUrl)
and therefore nothing gets loaded into my User object. Is there a way to fix this? I've been looking into writing a custom bindingmodel or valueprovider, but that looks like alot of work for something this simple. Plus, I'm really not sure if that's that way to go.
All help is greatly appreciated!
change method of your actionresult from
public ActionResult Login(User user, string redirectUrl)
To
public ActionResult Login(User Item, string redirectUrl)
this way modelbiner will be able to locate properties of User object prefixed with Item
you can create your helper methods to create that textbox
public static MVCString MyTextBox<T>(this HtmlHelper html,T _item,String PropertyName)
{
TagBuilder myTag = new TagBuilder("input");
myTag.Attributes("type")="Text";
myTag.Attributes("id")=PropertyName;
myTag.Attributes("name")=PropertyName;
myTag.Attributes("value")=typeof(_item).GetProperty(PropertyName).GetValue(_item,null).toString();
Return MvcHtmlString.Create(selectTag.ToString())
}
Which section of ASP.NET MVC should be overrided to be able to change the name of fields (Model Properties) in output to a custom things? Something like below:
<input id="IsActive" name="IsActive" type="checkbox" />
to this:
<input id="MYCUSTOMFORMAT-IsActive" name="MYCUSTOMFORMAT-IsActive" type="checkbox" />
This custom formatting shouldn't break down anything such client-side and server-side validation.
Thanks in advance ;)
More Info
I know that we can do this in Display/Editor Templates but i think this will cause infringement.
You can override the name in the HtmlAttributes parameter of the Html.TextBoxFor (etc) helper methods - such as:
#Html.TextBoxFor(o=>o.FirstName, new {id = "customId_originalId"})
However since you are changing this on the client side, the server side will not be able to recognize these changed names and will not bind properly unless you write your own model binder. As such it probably isn't aware of the server side validations to link this to either so again you are stuck handling this in a custom rolled manner.
A simple workaround for this if you want a constant prefix, but not necessarily the right way to do things, would be to use a viewmodel that contains your properties. in this case:
public class CustomViewModel
{
public bool IsActive {get;set;}
}
[HttpGet]
public ActionResult MyView()
{
CustomViewModel MYCUSTOMFORMAT = new CustomViewModel();
return View(MYCUSTOMFORMAT);
}
[HttpPost]
public ActionResult MyView(CustomViewModel MYCUSTOMFORMAT){
return View(MYCUSTOMFORMAT);
}
This will give you an Id of MYCUSTOMFORMAT.IsActive.
The proper way to do this would likely be overriding the default model binder and how it handles translating names to properties but I don't know model binders well enough to give you much direction on this.
Given the following model which has a name, url, and an arbitrary list of keywords (I want the user to add a series of keywords) ...
public class Picture
{
public Picture()
{
keywords = new List<string>();
}
public string name {get;set:}
public string url {get;set;}
public List<string> keywords{get;set;}
}
... and the following action in my controller ...
[HttpPost]
public ActionResult Edit(FormCollection fc)
{
if (ModelState.IsValid)
{
// do stuff
}
return View(ModelManager.Picture);
}
In the FormCollection I have the following field
fc["keywords"] = "keyword1,keyword2,keyword3"
And I then create a Picture object based on the form collection.
However, I would prefer to use a strongly-typed action such as
[HttpPost]
public ActionResult Edit(Picture p)
But in this approach, my p.keywords property is always empty. Is there some way to help the framework recreate my p.keywords property before it hits my controller's action method?
I thought an Editor Template might work here, but I don't think there is a way to model bind a nested IEnumerable view model member. Your fastest bet may be handling it directly with FormCollection and some string parsing magic. Otherwise, if you have to strongly-type this, maybe a custom model binder like this could help if you can control your keyword element id's:
public class PictureKeywordBinder : IModelBinder
{
public object GetValue(ControllerContext controllerContext,
string modelName, Type modelType,
ModelStateDictionary modelState)
{
Picture picture = new Picture();
//set name, url, other paramaters here
foreach(var item in Request.Form.Keys)
{
if (item.StartsWith("keyword"))
{
picture.keywords.Add(Request.Form[item]);
}
}
//add any errors to model here
return picture;
}
}
Maybe the keyword id's could be setup in a partial view passed the sub model from your parent view:
<% Html.RenderPartial("PictureKeywords", Model.keywords);
Are your keywords seperate text boxes? If so, create an inputs like this and they will be populated by the model binder.
<input name="keywords[0]" type="text">
<input name="keywords[1]" type="text">
<input name="keywords[2]" type="text">
The way I got around this, is to use a hidden input to store the csv string of items, in your case, keywords.
I then hooked into the form submit event (using jQuery) and appended the inputs to form the csv string, which is then stored in the hidden input. This hidden input was strongly typed to a property on my model.
It's a little clunky, but if you have a dynamic number of possible keywords then this works quite well (except if JS is disabled of course)
In what way you are expecting the user to add more keywords? In the form comma separated values(CSV) or by dynamically adding textboxes?
Based on your requirement, i have two solutions with me.
If you're using the Html.TextBoxFor() type methods, you may well end up with Form controls that have dots in their names, like this:
<input type="text" name="Contact.FirstName" id="Contact_FirstName" />
If you want MVC to map those named fields to parameters in your controller (as opposed to an object parameter or whatever), you have to get the parameter names right. What to do about the dots?
Neither this:
[HttpPost]
public ActionResult FooAction(string firstName)
not this:
[HttpPost]
public ActionResult FooAction(string contact_FirstName)
seem to work.
Edit: Having a suitable object parameter would work (eg see clicktricity's answer), but I'm looking for a way to do it with named value parameters.
I have found another way, a kind of hack because I believe this is misuse of BindAttribute, to associate firstName parameter with Contact.FirstName input element:
[HttpPost]
public ActionResult FooAction([Bind(Prefix="Contact.FirstName")]string firstName)
This for sure works with ASP.NET MVC 1.
Depending on the other form controls, you should be able to have the MVC default model binder construct a Contact object for you. Then the signature of your action method would be:
[HttpPost]
public ActionResult FooAction(Contact contact)
Then the Contact.FirstName (and any other fileds) will be bound correctly
As Clicktricity suggests in comments you may use
[HttpPost]
public ActionResult FooAction(FormCollection form)
{
firstName = form["Contact.FirstName"];
}