Reputation: 4359
New to Asp.Net Core/Razor here.
I am building a Asp.Net Core Razor page. I have a main Razor page which serves as a parent page. I have a collection of sub pages (over 20 different children types).
I would like to be able to conditionally include a child page based on an identifier in my parent page's view model.
The basic simplified version of my parent Razor page would look something like this:
@model DocumentsViewModel
<div>
<!-- Some basic common form functionality--!>
</div>
<div class="WhereToEmbedChildForm">
@Html.Partial("SomeChildViewName")
@{ Html.RenderPartial("SomeChildViewName"); }
@{<partial name="SomeChildViewName" for="CurrentDocumentTypeViewModel"/>}
</div>
My parent page view model would look something like this:
public class SampleModel
{
public int ChildViewType { get; set; }
public string ChildViewModelName { get; set; }
}
To accomplish this, I have some questions:
Is it possible to dynamically create my ViewModel object (DynamicObject) to include the child's view model?
How would I dynamically include the child view (and associated view model) in my parent page?
Should I be using or @Html.Partial()/@{Html.RenderPartial} ?
Given Razor, is it possible to do what I'm attempting?
Upvotes: 1
Views: 1585
Reputation: 11124
Please note that using tag library is preferred above HTML helpers when creating new apps.
A proof-of-concept:
Updated version of SampleModel
:
public class SampleModel
{
public object ChildViewModel { get; set; }
public string ChildViewName { get; set; }
}
And a simple child model
public class ModelForPartialOne
{
public string SomeDemoProperty { get; set; }
}
In controller
private ModelForPartialOne GetModelForPartialOneWithDataFromDb()
{
// just a mock db call
return new ModelForPartialOne {SomeDemoProperty = "Hello from one"};
}
public IActionResult SomeControllerAction()
{
const int childViewType = 42; // you will get this from somewhere
switch (childViewType)
{
case 42:
return View(new SampleModel
{
ChildViewModel = GetModelForPartialOneWithDataFromDb(),
ChildViewName = "_partialOne",
});
}
}
In main Razor view
@model SampleModel
<div class="WhereToEmbedChildForm">
<partial name="@Model.ChildViewName" model="@Model.ChildViewModel"/>
</div>
_partialOne.cshtml
@model ModelForPartialOne
<h1>Hello from partial one</h1>
<p>SomeDemoProperty: @Model.SomeDemoProperty</p>
In order to avoid a bloated controller, logic for populating view models should be delegated to business logic services.
Alternative to the above solution, is to use view components.
Code is tested using a Core 5 MVC app.
Update
OP asked me this later on:
However, now I'm attempting to retrieve data from my partial view on a PostBack. The submit button lives on my parent page.
Let's rewrite the main view like this:
@model SampleModel
<div class="WhereToEmbedChildForm">
<form method="post" asp-action="@Model.PostbackAction">
<partial name="@Model.ChildViewName" model="@Model.ChildViewModel"/>
<button type="submit">Click Me</button>
</form>
</div>
We'll need a partial _partialTwo.cshtml with form data like this:
@model ModelForPartialOne
Some demo property:
<input asp-for="SomeDemoProperty">
Add this property to SampleModel
: public string PostbackAction { get; set; }
And modify controller GET method to return this:
return View(new SampleModel
{
ChildViewModel = GetModelForPartialOneWithDataFromDb(),
ChildViewName = "_partialTwo",
PostbackAction = "MyPostbackAction"
});
With the above changes, we're able to open a page with a form. Now we'll need a controller action to handle the postback. This is where the fun stops, because we cannot use object as type for model parameter in POST method; the code needs to know the type to use for model binding.
[HttpPost]
public IActionResult MyPostbackAction(ModelForPartialOne model)
{
// do something
return View("Index");
}
Upvotes: 2