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;
}
Related
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:
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.
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 );
}
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.
In an ASP.NET MVC application I am trying to submit multiple objects with a single form. I am able to get simple types to post back but am having issues with complex types. I feel like I have mimicked the example provided by Phil Haack in his blog post Model Binding To A List but with no luck. Even going so far as copying his code exactly to no avail.
I am trying to populate the ProjectNum and TaskNum properties of a set of MachineLicenseBillback objects. Unfortunately the IList<MachineLicenseBillback> machinePts always ends up as a null when posted.
What am I missing?
Class
public class MachineLicenseBillback
{
public MachineLicenseBillback() { }
public virtual int MachineId { get; set; }
public virtual string ProjectNum { get; set; }
public virtual string TaskNum { get; set; }
public virtual string VerifiedFlag { get; set; }
public virtual DateTime? RcdChgDateTime { get; set; }
public virtual string RcdChgAgent { get; set; }
}
Action
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult TrueUp(int id, IList<MachineLicenseBillback> machinePts)
{
// ...
}
Form
<% using (Html.BeginForm("TrueUp", "Home", new { id = Model.Customer.Id },
FormMethod.Post))
{ %>
<input type="hidden" name="machinePts.Index" value="<%= machine.MachineId %>" />
<input type="text" name="machinePts[<%= machine.MachineId%>].ProjectNum"
value="<%= machine.MachineLicenseBillback.ProjectNum %>" />
<input type="text" name="machinePts[<%= machine.MachineId %>].TaskNum"
value="<%= machine.MachineLicenseBillback.TaskNum %>" />
<input type="submit" value="Submit" />
<% } %>
The .Index syntax was removed for MVC 1 RTM and reintroduced in MVC 2. For MVC 1, list elements must be numbered sequentially: machinePts[0], machinePts[1], etc.
Scott Hanselman has a complete walkthrough here for binding lists of items. In short, your controller method needs an array of MachineLicenseBillback, not an IList.
public ActionResult TrueUp(int id, MachineLicenseBillback[] machinePts)
{
// ...
}
Looking at your code, if you want to bind to an IDictionary (not an IList), you can use key/value pairs in the view instead. Or can keep the code you currently have in the view, and use an Array as the parameter in the controller method.
Pay special attention to the naming conventions. If there is a mismatch in naming, the model binder won't pick up the data.