I have an ASP.NET MVC view and related model.
How can I fill its related model from within the view?
You don't. MVC does, and it does this automatically when the form is posted back to the controller (assuming you're using model binding, and not a FormsCollection)
You POST to a controller action from your view and the model binder will populate it:
View:
#using(Html.BeginForm())
{
#Html.EditorFor(m => m.SomeProperty)
<input type="submit" value="Submit" />
}
Controller:
[HttpPost]
public ActionResult SomeAction(SomeModel model)
{
// your model has been populated by what was in the form at this point
}
Related
I have a single View, which has tabs with various different sections. Each tab I have implemented as a partial view.
There is one ViewModel which has various sub classes to populate the view and partial views within the tabs.
FormCaptureViewModel
- FormDetailViewModel
- FormBrandingViewModel
- etc
[HttpGet]
public IActionResult FormCapture()
{
return View(new FormCaptureViewModel());
}
<div class="tab-pane" id="tab2">
#Html.Partial("_FormBrandingPartial", Model.FormBranding)
</div>
<div class="tab-pane" id="tab3">
#Html.Partial("_FormDesignerPartial", Model.FormDesigner)
</div>
<div class="tab-pane" id="tab4">
#Html.Partial("_FormAnalyticsPartial", Model.FormAnalytics)
</div>
Then I want each form to do a post back to FormCapture like so:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult FormCapture(FormBrandingViewModel brandingModel)
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult FormCapture(FormDetailViewModel detailModel, SaveAction action)
{
return View();
}
Each partial view will have a form tag that looks like this:
<form role="form" method="post" asp-action="FormCapture" enctype="multipart/form-data">
However I get an AmbiguousActionException: Multiple actions matched. This makes sense because asp will not know which method to use. How do I go about fixing this?
I am trying to keep away from using ajax because using scripts in partial views is not great.
Any ideas how to implement something like this? Surely this is a normal use case for complicated views (with tabs, etc.)?
I cannot just submit the entire FormCaptureViewModel on each partial view as other tabs may have issues or be non-existent depending on the view's state.
My aim:
Utilize ModelState validations specific to the sub viewmodel.
The URL must not change on post back.
I have a parent view model (Let's call it ParentViewModel) which has a list of children view models (Let's call them ChildViewModel). Each child view model can be edited independently and I have a separate form which I display in a loop. This works brilliantly but I cannot work out how to post just the child model and ignore the parent.
This is my form:
#model ParentViewModel
...
#foreach (var child in Model.Children)
{
#using (Html.BeginForm("_EditChild", "Admin", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.EditorFor(model => child.Content, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => child.Content, "", new {#class = "text-danger"})
</div>
<div class="form-group">
<div class="col-md-12">
<input type="submit" value="Create" class="btn btn-default new-post" />
</div>
</div>
}
}
And this is the signature of my controller. It is expecting a type ChildViewModel which exists in ParentViewModel as a list.
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult _EditPost([Bind(Include = "")] ChildViewModel childViewModel)
{
}
The form works and submits BUT ChildViewModel is null when it reaches the submit controller. This is certainly because binding between the Form Post and the Action is not happening.
I am afraid it is not possible to post the child model only , since the page can define one and only one model that is the parent model you have defined .
But you can solve your problem simply by posting the parent model and extracting the child model in the controller .
It is possible, just not intended by ASP.NET MVC. All you would have to do is remove the parent prefix from the name of your submitted inputs on the client side. Your input would probably look something like:
<input name="Children[0].SomeProperty" ../>
If your AdminController._EditChild action expects a ChildViewModel, then you'd just have to use javascript to rename the input to:
<input name="SomeProperty" ../>
and the model binder should build the ChildViewModel. Alternatively, you might also be able to solve it by creating a custom ValueProvider or ModelBinder that maps the inputs to a ChildViewModel even though it has the wrong prefix...although that would seem like an uglier hack to me than changing the input names. On that note, I would probably also update the IDs with javascript when updating the names just to keep them in sync, even though only the name is used for binding.
Note also, if you're not looping but simply want to submit a single child ViewModel of your model, you can just assign it to a variable:
#var childVM = Model.ChildProp;
#Html.HiddenFor(m => childVM.ID)
Notice m is disregarded in the property expression of HiddenFor. I think last time I did this the variable name had to match the action's parameter name so you would submit this to:
public ActionResult SomeAction(ChildViewModel childVM){ ... }
I'm currently trying to understand why this technique can't be combined with looping.
The goal is to have one page with a wizard. Each step of the wizard is a partial view containing a form. I have only one controller (Insurance) with an action for each view. The actions receive the posted data and return the viewmodel for the next step, or the viewmodel of the current step containing the error details.
The page (Index.cshtml) has partial views, rendered as
#Html.Partial("~/Views/Shared/_RegistrationCode.cshtml")
and the partial view itself contains a form, rendered as
#using (Html.BeginForm("RegistrationCodeDetails", "Insurance", FormMethod.Post)) {
and a
<input type="submit" name="nextButton" value="Verder" class="btn btn-success" />
within the form to submit it.
The code works as intended up to the point where the first action returns the viewmodel for the next step (partial view _Product) using
return PartialView("_Product", productViewModel);. The ActionResult is not sent to the partial view, but rendered as a full view, so the result is a partial being rendered as the only thing on the screen.
I've fiddled with #using (Ajax.BeginForm("RegistrationCodeDetails", "Insurance", new AjaxOptions { UpdateTargetId = "articleProductOutput", HttpMethod = "Post" })) { but the data is not rendered in the second wizard step partial.
Edit:
We've decided to take a different approach: One page, one controller and basically one viewmodel. The initial data is rendered right away, data depending on other steps in the wizard is retrieved using JSON and partial views.
Unless you mark your partial view as [ChildActionOnly], it won't load in the same page!
you Partial view should look like
[ChildActionOnly]
public ActionResult _ParialView1()
{
//TODO: Add the required code here
}
//and your PartialView should be included in the main view as :
#{Html.Action("_PartialView1","Controller1");}
Thanks and hope this helps!
Currently I have a partial view where I manually display all my blog categories with links. I would like to make it dynamic by pulling from the database. I am not sure how to accomplish this within a partial view. I would even be willing to do it within the actual _layout page if it's easier that way.
Here is what I have right now.
_Categories.cshtml
<h2>Categories</h2>
<hr/>
<p>
ASP.Net MVC<br/>
Ruby on Rails<br/>
</p>
I would like to create these links dynamically as opposed to hard coding.
_Layout.cshtml
#Html.Partial("_Categories")
The main problem is there is no controller for the layout of a partial which is why I can't figure out how to go about it.
Thanks in advance for any help.
Create a controller action named ListCategories in BlogController (or in a new CategoryController). Add all the categories to the ViewBag in the action by querying them from your back-end database
public ActionResult ListCategories()
{
ViewBag.Categories = db.Categories;
}
And use a #foreach loop in the view for the action ListCategories.cshtml:
<h2>Categories</h2>
<hr/>
<p>
#foreach(Category c in ViewBag.Categories)
{
#c.Name<br/>
}
</p>
Finally, change your _Layout.cshtml to point to this action:
#Html.Action("ListCategories")
// or #Html.Action("ListCategories", "CategoryController")
In my _Layout.cshtml file, I'd like to invoke something like this:
<div id="content">
<div id="left-wrapper" class="box">
#Html.Action("FreeThisWeek", "Products")
#RenderBody()
</div>
</div>
And this is my ProductsController file:
[ChildActionOnly]
public ActionResult FreeThisWeek()
{
//Some code that fetches the data and builds the model.
var model = BuildFreeProducts();
return View(model);
}
If I try to run this code, I get a StackOverflowException because the Action returns the View() which asks for the Layout, which runs the Action which returns the View(), and so on.
Understandable, but how do I accomplish this which correct code?
Where do I write the View that compounds this data model with the HTML I write?
try returning PartialView("yourview",model) . Make sure the view you are returning does not use this page as a layout. you can specify that by using #{Layout=null} at the top of the view you are returning.
You are returning the View inside your FreeThisWeek Action and inside the View you are using the _Layout again. So it become recursive.
Go to your FreeThisWeek View and set Layout as null
#{
Layout=null;
}