Post form receiving a model in MVC3 - asp.net-mvc

I have the following model:
public class Person
{
public int ID{get;set;}
public string Name {get;set;}
public string Address{get;set;}
}
For other hand i have the following view called Index:
#model List<Person>
#{
foreach(Person person in Model)
{
#person.Name
}
}
Finally i have the following action:
public ActionResult Index()
{
List<Person> persons=new List<Person>();
persons.Add(new Person(){ID=1,Name="John"});
persons.Add(new Person(){ID=2,Name="Tom"});
persons.Add(new Person(){ID=2,Name="Derek"});
}
Im thinking to create a form (since i cannot use ajax for this app due to some requirements), to post an instance of the person chosen by the user (when clicks an anchor of my view). I would like to know how i could post a Person instance to another action described below (since my view is typed to a generic list of persons).
[HttpPost]
public ActionResult Index(Person person)
{
... Do whatever
}

Solution with POST:
in order to work it with POST you will have to palce a form and plant hidden fields like
#model List<Person>
#{
foreach(Person person in Model)
{
<form action="/controller/actionresult">
<input type="submit" value=person.Name />
<input type="hidden" name="ID" value="#person.ID"
<input type="hidden" name="Name" value="#person.Name"
</form>
}
}
and on the server side
[HttpPost]
public ActionResult Index(Person person)
{
... Do whatever
}
Solution with GET:
a tags normally dont work with POST there default behaviour is to request the server for a resource via GET although you can override this behaviour using javascript but in your case that is not an option therefore you can try
#model List<Person>
#{
foreach(Person person in Model)
{
#Html.ActionLink(persno.Name,"Index","home",new{ID=person.ID,Name=person.Name,null);
}
}
the only drawback is you will have to use a submit button instead of a tags, you can use css styling to style the button like an a tag. also if you have not set custom routes for this kind of request the uri will have query string params like
http://yourdomain/home/index?ID=1&Name=john

You're approaching it from the wrong angle. As you're not presenting any way to edit a Person you should just pass it by reference (i.e. the PersonId).
So just use a straight-forward link:
// View:
Html.ActionLink( person.Name, "ViewPerson", new { personId = person.ID } );
// Controller action:
public ActionResult ViewPerson(String personId) {
Person person = GetPersonFromDatabase( personId );
}

Related

Form returns null viewmodel to the Action [duplicate]

I have a model object structure with a Foo class that contains a Bar with a string value.
public class Foo
{
public Bar Bar;
}
public class Bar
{
public string Value { get; set; }
}
And a view model that uses that structure like this
public class HomeModel
{
public Foo Foo;
}
I then have a form in view that in Razor looks something like this.
<body>
<div>
#using (Html.BeginForm("Save", "Home", FormMethod.Post))
{
<fieldset>
#Html.TextBoxFor(m => m.Foo.Bar.Value)
<input type="submit" value="Send"/>
</fieldset>
}
</div>
</body>
In html that becomes.
<form action="/Home/Save" method="post">
<fieldset>
<input id="Foo_Bar_Value" name="Foo.Bar.Value" type="text" value="Test">
<input type="submit" value="Send">
</fieldset>
</form>
Finally the controller to handle the post loos like this
[HttpPost]
public ActionResult Save(Foo foo)
{
// Magic happends here
return RedirectToAction("Index");
}
My problem is that Bar in Foo is null once it hits the Save controller action (Foo is created but with an null Bar field).
I thought the model binder in MVC would be able to create the Foo and the Bar object and set the Value property as long as it looks like the above. What am I missing?
I also know my view model is a bit over complicated and could be simpler but I for what I'm trying to do I'd really help me if I could use the deeper object structure. The examples above uses ASP.NET 5.
Firstly, the DefaultModelBinder will not bind to fields so you need to use properties
public class HomeModel
{
public Foo Foo { get; set; }
}
Secondly, the helpers are generating controls based on HomeModel but you posting back to Foo. Either change the POST method to
[HttpPost]
public ActionResult Save(HomeModel model)
or use the BindAttribute to specify the Prefix (which essentially strips the value of prefix from the posted values - so Foo.Bar.Value becomes Bar.Value for the purposes of binding)
[HttpPost]
public ActionResult Save([Bind(Prefix="Foo")]Foo model)
Note also that you should not name the method parameter with the same name as one of your properties otherwise binding will fail and your model will be null.
I just discovered another reason this can happen, which is if your property is named Settings! Consider the following View model:
public class SomeVM
{
public SomeSettings DSettings { get; set; } // named this way it will work
public SomeSettings Settings { get; set; } // property named 'Settings' won't bind!
public bool ResetToDefault { get; set; }
}
In code, if you bind to the Settings property, it fails to bind (not just on post but even on generating the form). If you rename Settings to DSettings (etc) it suddenly works again.
I had the same problem and after I followed #Stephen Muecke steps I realized that the problem was caused because my inputs were disabled (I was disabling them with JQuery on document ready) as you can see it here: How do I submit disabled input in ASP.NET MVC?. At the end I used read-only instead of disabled attribute and all the values were sent successfully to the controller.
I had the same problem, but once I created a HIDDEN FIELD for the foreign-key...it all worked just fine...
FORM EXAMPLE:
#using (Html.BeginForm("save", "meter", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => Model.Entity.Id)
#Html.HiddenFor(model => Model.Entity.DifferentialMeter.MeterId)
#Html.HiddenFor(model => Model.Entity.LinearMeter.MeterId)
#Html.HiddenFor(model => Model.Entity.GatheringMeter.MeterId)
... all your awesome controls go here ...
}
ACTION EXAMPLE:
// POST: /Meter/Save
[HttpPost]
public ActionResult Save(Meter entity)
{
... world-saving & amazing logic goes here ...
}
PRETTY PICTURES:

Model change in post action not visible in Html.TextBoxFor?

This must be something very obvious but for me it looks very strange. I have simple controller, model with one property, and view which displays value of property and renders editor for that property. When I click the button, form is posted and exclamation mark is appened to property. This exclamation mark is visible in my view but only in p tag, not in input tag rendered by Html.TextBoxFor().
Why Html.TextBoxFor() ignores that I updated my model in post action?
Is there any way to change this behavior of Html.TextBoxFor()?
View
#model ModelChangeInPostActionNotVisible.Models.IndexModel
#using (Html.BeginForm())
{
<p>#Model.MyProperty</p>
#Html.TextBoxFor(m => m.MyProperty)
<input type="submit" />
}
Model
namespace ModelChangeInPostActionNotVisible.Models
{
public class IndexModel
{
public string MyProperty { get; set; }
}
}
Controller
namespace ModelChangeInPostActionNotVisible.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new IndexModel { MyProperty = "hi" });
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
model.MyProperty += "!";
return View(model);
}
}
}
HTML after clicking on submit button
<form action="/" method="post"> <p>hi!</p>
<input id="MyProperty" name="MyProperty" type="text" value="hi" /> <input type="submit" />
</form>
This is by design.
The helper methods are using the ModelState, thus if the response of your request is using the same Model, it will display the value that was posted.
This is to allow you to render the same view in the situation where the validation would have failed.
To make sure you display the new information add : ModelState.Clear(); before you return.
Read more here : http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx
namespace ModelChangeInPostActionNotVisible.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new IndexModel { MyProperty = "hi" });
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
model.MyProperty += "!";
ModelState.Clear();
return View(model);
}
}
}
Yan Brunet is absolutely correct that the variable needs to be removed from the ModelState in order to be modified in the controller. You don't have to clear the entire ModelState, though. You could do the following to remove just the variable to want to modify:
ModelState.Remove("MyProperty");
This would be useful in case you wanted to retain other values which the user had entered.

ASP.NET MVC, passing Model from View to Controller

I'm having trouble with ASP.NET MVC and passing data from View to Controller. I have a model like this:
public class InputModel {
public List<Process> axProc { get; set; }
public string ToJson() {
return new JavaScriptSerializer().Serialize(this);
}
}
public class Process {
public string name { get; set; }
public string value { get; set; }
}
I create this InputModel in my Controller and pass it to the View:
public ActionResult Input() {
if (Session["InputModel"] == null)
Session["InputModel"] = loadInputModel();
return View(Session["InputModel"]);
}
In my Input.cshtml file I then have some code to generate the input form:
#model PROJ.Models.InputModel
#using(Html.BeginForm()) {
foreach(PROJ.Models.Process p in Model.axProc){
<input type="text" />
#* #Html.TextBoxFor(?? => p.value) *#
}
<input type="submit" value="SEND" />
}
Now when I click on the submit button, I want to work with the data that was put into the textfields.
QUESTION 1: I have seen this #Html.TextBoxFor(), but I don't really get this "stuff => otherstuff". I concluded that the "otherstuff" should be the field where I want to have my data written to, in this case it would probably be "p.value". But what is the "stuff" thing in front of the arrow?
Back in the Controller I then have a function for the POST with some debug:
[HttpPost]
public ActionResult Input(InputModel m) {
DEBUG(m.ToJson());
DEBUG("COUNT: " + m.axProc.Count);
return View(m);
}
Here the Debug only shows something like:
{"axProc":[]}
COUNT: 0
So the returned Model I get is empty.
QUESTION 2: Am I doing something fundamentally wrong with this #using(Html.BeginForm())? Is this not the correct choice here? If so, how do I get my model filled with data back to the controller?
(I cannot use "#model List< Process >" here (because the example above is abbreviated, in the actual code there would be more stuff).)
I hope someone can fill me in with some of the details I'm overlooking.
Change your view to some thing like this to properly bind the list on form submission.
#using(Html.BeginForm()) {
for(int i=0;i<Model.axProc.Count;i++){
<span>
#Html.TextBoxFor(model => model.axProc[i].value)
</span>
}
<input type="submit" value="SEND" />
}
In #Html.TextBoxFor(stuff => otherstuff) stuff is your View's model, otherstuff is your model's public member.
Since in the View you want to render input elements for the model member of a collection type (List), you should first create a separate partial view for rendering a single item of that collection (Process). It would look something like this (name it Process.cshtml, for example, and place into the /Views/Shared folder):
#model List<PROJ.Models.Process>
#Html.TextBoxFor(model => p.value)
Then, your main View would look like this:
#model PROJ.Models.InputModel
#using(Html.BeginForm()) {
foreach(PROJ.Models.Process p in Model.axProc){
#Html.Partial("Process", p)
}
<input type="submit" value="SEND" />
}
Also, check that the loadInputModel() method actually returns something, e.g. not an empty list.

MVC4 - One form 2 submit buttons

I followed the instructions on this post:
Asp.net mvc3 razor with multiple submit buttons
and here is my model:
public class AdminModel
{
public string Command { get; set; }
}
My Controller
[HttpPost]
public ActionResult Admin(List<AdminModel> model)
{
string s = model.Command;
}
My View
#using (Html.BeginForm("Admin", "Account"))
{
<input type="submit" name="Command" value="Deactivate"/>
<input type="submit" name="Command" value="Delete"/>
}
When I post back, string "s" is always null.
I also tried the second answer (the one with 146 votes) in this forum post : How do you handle multiple submit buttons in ASP.NET MVC Framework? and thats also null. What am I doing wrong?
you need to take the value from their server side by the name of the button,
public ActionResult Admin(List<AdminModel> model,string Command)
{
string s = Command;
}
From what I can see in the posted code, you aren't sending a list of models to your controller, just a single model instance. Try modifying the controller to this:
[HttpPost]
public ActionResult Admin(AdminModel model)
{
string s = model.Command;
}

MVC3 using CheckBox with a complex viewmodel

Right guys. I need your brains as I can't find a way to do this properly.
I have a view model:
public class EditUserViewModel
{
public User User;
public IQueryable<ServiceLicense> ServiceLicenses;
}
User is unimportant as I know how to deal with it.
ServiceLicenses has the following implementation:
public class ServiceLicense
{
public Guid ServiceId { get; set; }
public string ServiceName { get; set; }
public bool GotLic { get; set; }
}
Getting a checked list of users is cool. It works like a charm.
<fieldset>
<legend>Licenses</legend>
#foreach (var service in Model.ServiceLicenses)
{
<p>
#Html.CheckBoxFor(x => service.GotLic)
#service.ServiceName
</p>
}
</fieldset>
The problem I'm having is getting the updated ServiceLicenses object with new checked services back to the HttpPost in my controller. For simplicity lets say it looks like this:
[HttpPost]
public ActionResult EditUser(Guid id, FormCollection collection)
{
var userModel = new EditUserViewModel(id);
if (TryUpdateModel(userModel))
{
//This is fine and I know what to do with this
var editUser = userModel.User;
//This does not update
var serviceLicenses = userModel.ServiceLicenses;
return RedirectToAction("Details", new { id = editUser.ClientId });
}
else
{
return View(userModel);
}
}
I know I am using CheckBox the wrong way. What do I need to change to get serviceLicenses to update with the boxes checked in the form?
i understand that ServiceLicenses property is a collection and you want MVC binder to bind it to you action parameters property. for that you should have indices attached with inputs in your view e.g
<input type="checkbox" name = "ServiceLicenses[0].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[1].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[2].GotLic" value="true"/>
Prefix may not be mandatory but it is very handy when binding collection property of action method parameter. for that purpose i would suggest using for loop instead of foreach and using Html.CheckBox helper instead of Html.CheckBoxFor
<fieldset>
<legend>Licenses</legend>
#for (int i=0;i<Model.ServiceLicenses.Count;i++)
{
<p>
#Html.CheckBox("ServiceLicenses["+i+"].GotLic",ServiceLicenses[i].GotLic)
#Html.CheckBox("ServiceLicenses["+i+"].ServiceName",ServiceLicenses[i].ServiceName)//you would want to bind name of service in case model is invalid you can pass on same model to view
#service.ServiceName
</p>
}
</fieldset>
Not using strongly typed helper is just a personal preference here. if you do not want to index your inputs like this you can also have a look at this great post by steve senderson
Edit: i have blogged about creating master detail form on asp.net mvc3 which is relevant in case of list binding as well.

Resources