Using .NET Core 3.1 with Razor Page Handler, I want to be able to populate a dropdown list from the layout page.
_layout.cshtml
@model IndexModel
<nav>
<ul>
<li >
<a href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@Localizer["Students"]</a>
<div aria-labelledby="navbarDropdown">
<a asp-area="@Areas.Admins" asp-page="ListStudents">@Localizer["ListStudents"]</a>
</div>
</li>
<li >
<select asp-items="@Model.ClassList" onchange="refreshPage(this)" >
@if (Model.ClassList.Count > 1){
<option value="All">@Localizer["SelectClass"]</option>
}
</select>
</li>
</ul>
</nav>
@RenderBody()
the dropdown works on page load, but when I navigate to another page like ListStudents
I encountering an error
InvalidOperationException: The model item passed into the ViewDataDictionary is of type '..Pages.ListStudentsModel', but this ViewDataDictionary instance requires a model item of type '..Pages.IndexModel'.
Index.cshtml.cs
public class IndexModel : PageModel
{
private readonly IClassService _classService;
public List<SelectListItem> ClassList { get; set; }
public IndexModel(IClassService classService)
{
_classService = classService;
ClassList = new List<SelectListItem>();
}
public async Task OnGet()
{
ClassList = await _classService.GetClassList();
}
}
ListStudents.cshtml.cs
public class ListStudentsModel : PageModel
{
public ListStudentsModel()
{
}
public async Task<IActionResult> OnGetAsync()
{
// some code here
return Page();
}
}
I'm not sure if this is the right idea or if should I use a partial view or component (I'm struggling with this part)
I tried to replace the dropdown with a another partial page
@await Html.PartialAsync("/Pages/ClassDropdown.cshtml", new ClassDropdownModel())
ClassDropdown.cshtml
@page
@model ClassDropdownModel
<span>Test</span>
ClassDropdown.cshtml.cs
public class ClassDropdownModel: PageModel
{
private readonly IClassService _classService;
public List<SelectListItem> ClassList { get; set; }
public IndexModel(IClassService classService)
{
_classService = classService;
ClassList = new List<SelectListItem>();
}
public async Task OnGet()
{
ClassList = await _classService.GetClassList();
}
}
but I'm getting error on "new ClassDropdownModel()" saying there's no argument given that corresponds to the required formal parameter 'classService'
CodePudding user response:
It seems that it's only possible with View Component and very easy to implement
Removed @model IndexModel from _layout
then added the ff:
ViewComponents/ClassesViewComponent.cs
[ViewComponent(Name = "Classes")]
public class ClassesViewComponent : ViewComponent
{
private readonly IClassService _classService;
public ClassesViewComponent(IClassService classService)
{
_classService = classService;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var classList = await _classService.GetClassList();
return View("Classes", classList );
}
}
Pages/Shared/Components/Classes/Classes.chtml
@inject IHtmlLocalizer<SharedResource> Localizer
@model List<KeyValuePair<string,string>>
<select onchange="refreshPage(this)" >
@if (Model.Count() > 1){
<option value="All">@Localizer["SelectClasses"]</option>
}
@foreach (var class in Model){
<option value="@class.Value">@class.Key</option>
}
</select>
_layout
@await Component.InvokeAsync("Classes")