I'm newbie to MVC and my FORM Post is not working. Could anyone tell me if I pass Dinner object to the VIEW. And do HTTP POST, should MVC give Person object back?
e.g.
public ActionResult Edit(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsHostedBy(User.Identity.Name))
return View("InvalidOwner");
return View(dinner);
}
public ActionResult Edit(Dinner dinner) {
//should this dinner class be populated?
}
The default model binder automatically populates action arguments if values are present in the request. So for example if your form contains the following fields:
<input type="text" name="Foo" />
<input type="text" name="Bar" />
and your Dinner object contains those properties:
public class Dinner
{
public string Foo { get; set; }
public string Bar { get; set; }
}
then when you submit the form to the following action:
[HttpPost]
public ActionResult Edit(Dinner dinner)
{
// the dinner.Foo and dinner.Bar properties will be
// automatically bound from the request
...
}
For more advanced binding scenarios such as lists and dictionaries you may checkout the following blog post for the correct wire format.
Related
I'm using a ViewModel to get the data from the HTML form to the MVC controller.
In the ActionResult parameter, there is the ViewModel that is filled, it's working fine.
Now, I want to get one field of the viewmodel separated in addition to the viewmodel. Is it possible? In another word, I want a copy of MyViewModel.MyModel.Id into another actionresult parameter.
Here is the working Controller Method:
public ActionResult Edit(AuditViewModelCritereViewModel model){}
Now I want to have this kind of method
public ActionResult Edit(AuditViewModelCritereViewModel model, int auditId){}
Here are the Models:
public class AuditViewModelCritereViewModel
{
public AuditViewModel audit { get; set; }
...
}
public class AuditViewModel
{
public int auditId { get; set; }
...
}
I could achieve this by adding HiddenField but clean is the best, I would like to make this prettier than copiing HTML id value.
In the HTML form, here is the field:
<input data-val="true" id="audit_auditId" name="audit.auditId" type="hidden" value="10">
This is HTTP request content that interest us:
This is what I've tried so far as int paramater:
int auditId
int audit_auditId
Thank you for your help.
Yes it is possible
<input data-val="true" id="audit_auditId" name="auditId" type="hidden" value="10">
Take same name of input field where you want to get the data in action parameter.
Action Parameter name and Field name should be same. It will works fine
OR
You can get this things with using Bind Attribute in Controller
public ActionResult Edit(AuditViewModelCritereViewModel model, [Bind(Prefix = "audit.auditId")]int auditId) { }
Passing ViewModel to #Html.Partial
Have two ViewModels
public class RegisterVM
{
... some properties
public AddressVM AddressInformation { get; set; } //viewmodel
}
public class AddressVM {
public string Street1 { get; set; }
public string Street2 { get; set; }
public string PostalCode { get; set; }
}
When loading main view using VM:
#model ViewModels.RegisterVM
All field load. But When I add Partial View and pass viewmodel
#Html.Partial("_AddressDetails", Model.AddressInformation)
It fails
Error: Exception Details: System.NullReferenceException: Object reference not set to an instance of an object. Why does it fail?
The partial View _AddressDetails is expecting a
#model ViewModels.AddressVM
Update
Based on changes from Prashant,
When submitting the information The Address information is NULL.
In The controller:
[HttpPost]
public ActionResult Register(RegisterVM vm){
...
//when viewing vm.AddressInformation.Street1 is null. and there is a value
//Is there a different way of retrieving the values from partial view?
}
Thanks for reading.
The error is generated because property AddressInformation is null, and you need to initailize it in a parameterless constructor or in the controller before you pass it to the view, for example
public class RegisterVM
{
public RegisterVM() // default constructor
{
AddressInformation = new AddressVM();
}
public AddressVM AddressInformation { get; set; }
....
}
However you usage means that the controls generated will be
<input name="Street1" .../>
whereas they need to be
<input name="AddressInformation.Street1" .../>
in order to bind to your model. You can either make your partial an EditorTemplate (/Views/Shared/EditorTemplates/AddressVM.cshtml) and use in the main view as
#Html.EditorFor(m => m.AddressInformation)
or pass the prefix to the partial as additional ViewData
#Html.Partial("_AddressDetails", Model.AddressInformation, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "AddressInformation" }})
This is working for me. You just need to instantiate your VM, attach it and send it to the view.
Page Action
public ActionResult Page(){
RegisterVM vm = new RegisterVM();
vm.AddressInformation = new AddressVM();
return View(vm);
}
Page.cshtml
#model Project.Web.Models.RegisterVM
<!-- loading partial view -->
#Html.Partial("_AddressDetails",Model.AddressInformation)
Partial View File
<input type="text" name="name" value=" " />
I am not having more info about code but as per mention details, can you try this
public ActionResult Register(){ return View(register); }
i know you may tried this but try to assigned explict value. as this is basic MVC implementation. if it not work out then you need to provide more code details.
hope this help.
in Register get Method must instatiate your viewModel because in view, call other partial with viewModel members(proprties);
public ActionResult Register(){
RegisterVM vm = new RegisterVM();
return View(vm);
}
I've got an action on my controller that looks like this:
[HttpPost]
public ActionResult Edit(EditMyObjectViewModel editMyObjectViewModel)
{
}
EditMyActionViewModel contains a MyObject
This is passed in to the Edit view (the GET version of the above controller action)
When it is posted back in, the ID isn't set....
If I change the controller to be:
[HttpPost]
public ActionResult Edit(Guid id, EditMyObjectViewModel editMyObjectViewModel)
{
editMyObjectViewModel.ID = id;
}
That works, but it seems a little wrong?
I guess I could also bind a hidden field on the view to Model.ID?
What's the convention here?
EDIT
Model / ViewModels are as follows:
public class EditMyObjectViewModel
{
public IEnumerable<SomeItem> SomeUnrelatedStuff { get; set; }
public MyObject MyObject { get; set; }
}
public class MyObject
{
public guid ID { get; set; }
public string Name { get; set; }
}
View is as follows:
#model MyApp.Models.EditMyObjectViewModel
#using (Html.BeginForm("Edit", "License", FormMethod.Post, new { #class = "form form-horizontal" }))
{
#Html.TextboxFor(x=>x.MyObject.Name);
<input type="submit" class="btn btn-success" value="Create Modification" />
}
You are right, you either need to create a hidden field to hold the id or you need to add the ID as a parameter to the action method.
The issue is simply where the id comes from and how it gets populated. The default model binder populates the model from form fields; it wont' use a query parameter. And the query parameter won't go into the model but it will get populated in an argument on the action method.
The one other thing you could do is to create a custom model binder to populate the query parameter into the model. But that feels like overkill in this situation. Plus, you'd have to do this for each page/model.
It it was me, I'd add the id to the action method.
I have the two buttons in MVC3 application.
<input type="submit" name="command" value="Transactions" />
<input type="submit" name="command" value="All Transactions" />
When I click on a button, it posts back correctly but the FormCollection has no "command" keys. I also added a property "command" in the model and its value is null when the form is posted.
public ActionResult Index(FormCollection formCollection, SearchReportsModel searchReportsModel). {
if (searchReportsModel.command == "All Transactions")
...
else
....
}
I am using IE8. How can I use multiple buttons in MVC3? Is there a workaround for this issue? I did lot of research and could not find a solution.
Update:
Dave: I tried your solution and it is throwing Http 404 error "The resource cannot be found".
Here is my code:
[HttpPost]
[AcceptSubmitType(Name = "Command", Type = "Transactions")]
public ActionResult Index(SearchReportsModel searchReportsModel)
{
return RedirectToAction("Transactions", "Reports", new { ...});
}
[HttpPost]
[ActionName("Index")]
[AcceptSubmitType(Name = "Command", Type = "All Transactions")]
public ActionResult Index_All(SearchReportsModel searchReportsModel)
{
return RedirectToAction("AllTransactions", "Reports", new { ... });
}
public class AcceptSubmitTypeAttribute : ActionMethodSelectorAttribute
{
public string Name { get; set; }
public string Type { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
return controllerContext.RequestContext.HttpContext
.Request.Form[this.Name] == this.Type;
}
}
The issue was resolved after commenting the following Remote validation attribute in the ViewModel (SearchReportsModel). It looks like it is a bug in MVC3:
//[Remote("CheckStudentNumber", "SearchReports", ErrorMessage = "No records exist for this Student Number")]
public int? StudentNumber { get; set; }
You might be able to get away with an ActionMethodSelectorAttribute attribute and override the IsValidForRequest method. You can see below this method just determines whether a particular parameter (Name) matches one of it's properties (Type). It should bind with a view model that looks like this:
public class TestViewModel
{
public string command { get; set; }
public string moreProperties { get; set; }
}
The attribute could look like this:
public class AcceptSubmitTypeAttribute : ActionMethodSelectorAttribute
{
public string Name { get; set; }
public string Type { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
return controllerContext.RequestContext.HttpContext
.Request.Form[this.Name] == this.Type;
}
}
Then, you could tag your actions with the AcceptSubmitType attribute like this:
[AcceptSubmitType(Name="command", Type="Transactions")]
public ActionResult Index(TestViewModel vm)
{
// use view model to do whatever
}
// to pseudo-override the "Index" action
[ActionName("Index")]
[AcceptSubmitType(Name="command", Type="All Transactions")]
public ActionResult Index_All(TestViewModel vm)
{
// use view model to do whatever
}
This also eliminates the need for logic in a single controller action since it seems you genuinely need two separate courses of action.
Correct me If I'm wrong, but according to W3C standard you should have only 1 submit button per form. Also having two controls with identical names is a bad idea.
when you submit (on any button) your whole page is posted back to the controller action, I have had the same problem but have not found a decent solution yet.. maybe you could work with a javascript 'onclick' method and set a hidden value to 1 for the first button and 0 for the second button or something like that?
This is a nice Blog about this found here
I like the look of adding in AcceptParameterAttribute
#CodeRush: The W3C standard does allow more than 1 submit per form. http://www.w3.org/TR/html4/interact/forms.html. "A form may contain more than one submit button".
its me... yet again!
Ive got these class,
public class PrankTargetArgumentViewModel
{
public PrankTarget Target { get; set; }
public PrankDefinition Prank { get; set; }
public List<PrankArgument> Arguments { get; set; }
}
public class PrankArgument
{
public string Name { get; set; }
public string Value { get; set; }
}
and what I'm doing is - if this current ParkDefinition needs arguments them im doing an ActionRedirect on the save to another Action which should handle the gathering of the Arguments
My Action result is like this..
public ActionResult PrankArguments()
{
PrankInstance currentInstance = SessionContext.CurrentPrankInstance;
if (currentInstance == null)
throw new ArgumentNullException("currentInstance");
PrankTargetArgumentViewModel model = new PrankTargetArgumentViewModel();
model.Prank = currentInstance.Prank;
model.Target = currentInstance.Target;
string[] args = model.Prank.Arguments.Split('|');
model.Arguments = new List<PrankArgument>();
foreach (string s in args)
{
model.Arguments.Add(new PrankArgument { Name = s, Value = s });
}
return View(model);
}
my http post method is just an empty method with the parameter of PrankTargetArgumentViewModel
[HttpPost]
public ActionResult PrankArguments(PrankTargetArgumentViewModel model)
{
return View();
}
My HTML is like this..
#using (Html.BeginForm())
{
#Html.EditorFor(x => Model)
<p>
<input type="submit" value="Create" />
</p>
}
So my problem is this, on the PrankArguments(PrankTargetArgumentViewModel model) post back action, the model param is always null.. I've filled the object with values on the load so I guessed they would be there on the post back with the new arguments that I added.
so the flow goes like this.
Create Prank
If prank needs arguments then load ActionResult PrankArguments()
Add extra arguments to an already poplulated object.
save, Call ActionResult PrankArguments(PrankTargetArgumentViewModel model)
-- this is where the problem is, the model parameter is passed back as null.
Ive had this problem quite a few times and always just given up but im not going to let that happen this time!
any help would be great! cheers, Ste!
Ps. If you need anymore of my code just let me know.
EDIT - Removed view bag debug properties!
I think if I understand you correctly if your view is strongly typed to PrankTargetArgumentViewModel then all you have to do to retrieve the values is:
[HttpPost]
public ActionResult PrankArguments()
{
var pta = new PrankTargetArgumentViewModel();
TryUpdateModel(pta);
}
After reviewing my own code - I noticed that I didn't need the entire PrankTargetArgumentViewModel and a simple List of Arguments would have been fine.
I alterd my PrankArguments view to take an IEnumerable and used;
#using (Html.BeginForm())
{
#Html.EditorForModel()
<p>
<input type="submit" value="Finish" />
</p>
}
then had my post back method signature like this
[HttpPost]
public ActionResult PrankArguments(IEnumerable<PrankArgument> arguments)
which worked exactly how I wanted.
Thanks for all the suggestions guys.