Suggested Videos
Part 21 - ASP.NET Core razor pages client side validation | Text | Slides
Part 22 - Delete operation in asp.net core razor pages | Text | Slides
Part 23 - Partial views in asp.net core | Text | Slides
In this video we will discuss View Components in ASP.NET core with example.
View component example
In addition to page specific content, we want to display the following Employee Head Count Summary on several pages in our application.
Since, we want to display the Employee Head Count Summary on several pages, it makes sense to create a reusable component.
What we do not want to do is include the code on each page. This approach duplicates code.
We can't use a partial view either, because a partial view cannot have it's own data access logic. It depends on the data passed from the parent view or razor page. We do not want to include the code that retrieves data on each page. This is again code duplication. We want something that can retrieve data independently and render. View Component is a perfect choice for this.
View Components folder
Create View Components folder, in the root project directory. We will place all our view components in this folder. Add a new class file with name - HeadCountViewComponent.cs. Copy and paste the following code.
View component class
ASP.NET Core looks for the view in the following locations
Razor pages project
Copy and paste the following code in default.cshtml
Rendering view component
Use Component.InvokeAsync to invoke the view component. We want to render it from Details razor page. So include the following code on Details.cshtml page.
Supporting classes
DeptHeadCount.cs in Models project
IEmployeeRepository.cs in Models project
MockEmployeeRepository.cs in Models project
Part 21 - ASP.NET Core razor pages client side validation | Text | Slides
Part 22 - Delete operation in asp.net core razor pages | Text | Slides
Part 23 - Partial views in asp.net core | Text | Slides
In this video we will discuss View Components in ASP.NET core with example.
View component example
In addition to page specific content, we want to display the following Employee Head Count Summary on several pages in our application.
Since, we want to display the Employee Head Count Summary on several pages, it makes sense to create a reusable component.
What we do not want to do is include the code on each page. This approach duplicates code.
We can't use a partial view either, because a partial view cannot have it's own data access logic. It depends on the data passed from the parent view or razor page. We do not want to include the code that retrieves data on each page. This is again code duplication. We want something that can retrieve data independently and render. View Component is a perfect choice for this.
View Components folder
Create View Components folder, in the root project directory. We will place all our view components in this folder. Add a new class file with name - HeadCountViewComponent.cs. Copy and paste the following code.
using Microsoft.AspNetCore.Mvc;
using RazorPagesTutorial.Services;
namespace RazorPagesTutorial.ViewComponents
{
public class HeadCountViewComponent : ViewComponent
{
private readonly IEmployeeRepository employeeRepository;
public HeadCountViewComponent(IEmployeeRepository employeeRepository)
{
this.employeeRepository = employeeRepository;
}
public IViewComponentResult Invoke()
{
var result = employeeRepository.EmployeeCountByDept();
return View(result);
}
}
}
using RazorPagesTutorial.Services;
namespace RazorPagesTutorial.ViewComponents
{
public class HeadCountViewComponent : ViewComponent
{
private readonly IEmployeeRepository employeeRepository;
public HeadCountViewComponent(IEmployeeRepository employeeRepository)
{
this.employeeRepository = employeeRepository;
}
public IViewComponentResult Invoke()
{
var result = employeeRepository.EmployeeCountByDept();
return View(result);
}
}
}
View component class
- View component class name ends with the suffix ViewComponent
- It inherits from the ViewComponent base class
- It supports dependency injection, just like a razor page or an MVC controller
- A view component does not directly respond to an HTTP request. It is usually invoked and consumed by a razor page, layout view or an MVC view.
- When a view component is invoked, it calls the Invoke method.
- Inovke() method returns IViewComponentResult.
- Use InvokeAsync() method, if you want to call the View Component asynchronously.
- View Component follows the MVC design pattern. It initialises a model and passes it to a view by calling the View method.
- Though it follows the MVC approach, it can be used both in an MVC project and a razor pages project.
ASP.NET Core looks for the view in the following locations
Razor pages project
- /Pages/Shared/Components/{View Component Name}/{View Name}
- /Views/{Controller Name}/Components/{View Component Name}/{View Name}
- /Views/Shared/Components/{View Component Name}/{View Name}
- In the Shared folder, create Components folder.
- In the Components folder, create a folder with the same name as the View Component. Our view component name is HeadCountViewComponent. So create a folder with name HeadCount. The suffix ViewComponent is not required.
- In this folder create a file with name default.cshtml
Copy and paste the following code in default.cshtml
@model
IEnumerable<DeptHeadCount>
<h3>Employee Head Count Summary</h3>
<table class="table table-bordered">
<thead class="thead-light">
<tr>
<th>Department</th>
<th>Head Count</th>
</tr>
</thead>
<tbody>
@foreach (var deptHeadCount in Model)
{
<tr>
<td>@deptHeadCount.Department</td>
<td>@deptHeadCount.Count</td>
</tr>
}
</tbody>
</table>
Rendering view component
Use Component.InvokeAsync to invoke the view component. We want to render it from Details razor page. So include the following code on Details.cshtml page.
@await Component.InvokeAsync("HeadCount")
Supporting classes
DeptHeadCount.cs in Models project
namespace RazorPagesTutorial.Models
{
public class DeptHeadCount
{
public Dept Department { get; set; }
public int Count { get; set; }
}
}
{
public class DeptHeadCount
{
public Dept Department { get; set; }
public int Count { get; set; }
}
}
IEmployeeRepository.cs in Models project
namespace RazorPagesTutorial.Services
{
public interface IEmployeeRepository
{
// Other methods
IEnumerable<DeptHeadCount> EmployeeCountByDept();
}
}
{
public interface IEmployeeRepository
{
// Other methods
IEnumerable<DeptHeadCount> EmployeeCountByDept();
}
}
MockEmployeeRepository.cs in Models project
namespace RazorPagesTutorial.Services
{
public class MockEmployeeRepository : IEmployeeRepository
{
private List<Employee> _employeeList;
public MockEmployeeRepository()
{
_employeeList = new List<Employee>()
{
new Employee() { Id = 1, Name = "Mary", Department = Dept.HR,
Email = "mary@pragimtech.com", PhotoPath="mary.png" },
new Employee() { Id = 2, Name = "John", Department = Dept.IT,
Email = "john@pragimtech.com", PhotoPath="john.png" },
new Employee() { Id = 3, Name = "Sara", Department = Dept.IT,
Email = "sara@pragimtech.com", PhotoPath="sara.png" },
new Employee() { Id = 4, Name = "David", Department = Dept.Payroll,
Email = "david@pragimtech.com" },
};
}
public IEnumerable<DeptHeadCount> EmployeeCountByDept()
{
return _employeeList.GroupBy(e => e.Department)
.Select(g => new DeptHeadCount()
{
Department = g.Key.Value,
Count = g.Count()
}).ToList();
}
}
}
{
public class MockEmployeeRepository : IEmployeeRepository
{
private List<Employee> _employeeList;
public MockEmployeeRepository()
{
_employeeList = new List<Employee>()
{
new Employee() { Id = 1, Name = "Mary", Department = Dept.HR,
Email = "mary@pragimtech.com", PhotoPath="mary.png" },
new Employee() { Id = 2, Name = "John", Department = Dept.IT,
Email = "john@pragimtech.com", PhotoPath="john.png" },
new Employee() { Id = 3, Name = "Sara", Department = Dept.IT,
Email = "sara@pragimtech.com", PhotoPath="sara.png" },
new Employee() { Id = 4, Name = "David", Department = Dept.Payroll,
Email = "david@pragimtech.com" },
};
}
public IEnumerable<DeptHeadCount> EmployeeCountByDept()
{
return _employeeList.GroupBy(e => e.Department)
.Select(g => new DeptHeadCount()
{
Department = g.Key.Value,
Count = g.Count()
}).ToList();
}
}
}
We greatly appreciate your effort, I have implemented ViewComponent and the problem is how to handle postbacks like grid event (sort, pagination). Can you please suggest me best approach?
ReplyDeleteGetting error in default.cshtml
ReplyDelete@model IEnumerable
The type or namespace name 'DeptHeadCount' could not be found (are you missing a using directive or an assembly reference?)