Building ASP.Net MVC site Part 2

This is the posting about building ASP.Net MVC site. In this posting, I talk about differences and similarities of view, partial view, view component and templates. Why there are so many ways to do practically the same thing – generate HTML.

View is basic building block in ASP.Net MVC. It provides a template for your page. If you are building a simple site, this is the only thing you need. It doesn’t make sense to overengineer your site to accommodate possible future needs that most likely won’t even be required. If you are not building code for next deep space mission, you probably can update your codebase or at least you should be able to. If you haven’t thought about update – retest – deploy – verify procedure, please sit down for a while and think that before creating a solution that is impossible to test.

Others (partial view, view component and template) allow you to reuse parts of the view in another view or within the same view. Even all of them could be used for same purpose, I recommend using them for the purposes they suit best.

View

View is basic building block of your application. It works as a template for one specific functionality for example listing of users or edit single user. It can also provide base of your Single Page Application (SPA). If you are building SPA application, this post doesn’t offer that much help for you. Other reusable components are added to the view. They also can be added inside other component for example partial view can contain view components or templates.

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/overview

Partial view

Partial view is commonly used as providing reusable content between different views. It’s also the easiest to use and doesn’t require that much knowledge. It allows you to provide for example login form for users. It can contain visible parts or it can only contain references to scripts and styles, you want to use only in specific type of views like _ValidationScriptsPartial.cshtml file in Visual Studio MVC template.

It’s easy to add partial view to view. You use Html helper class that provides Partial or PartialAsync methods that allows you to include partial view to view.

Here is an example about using PartialAsync. Notice that it requires await before it.

@section Scripts {
@await Html.PartialAsync(“_ValidationScriptsPartial”)
}

Now I create shared component that I want to include to different pages. I create simple LinksPartial.chtml file that is my partial view. It contains following html snippet.

<ul class=” list-inline”>
<li><a href=”#”>The first link</a></li>
<li><a href=”#”>The second link</a></li>
<li><a href=”#”>The third link</a></li>
</ul>

As you can see it’s really simple. Now I add partial view to Index view.

<div class=”row”>
<div class=”col-lg-12″>
@Html.Partial(“Links”)
</div>
</div>

If we start web application, it shows us links at the end of the view.

mvc_part2_partial_view

If we need to pass different links to the partial view based on some logic, we can pass model to it. At first, we need to add some code. The first thing we need to have view model that contains links information. Link class contains information for single link and IndexViewModel is view model for Index view.

namespace Demo.Models
{
public class Link
{
public string Name { get; set; }
public string Url { get; set; }
}
}

 

using System.Collections.Generic;
namespace Demo.Models
{
public class IndexViewModel
{
public IEnumerable<Link> Links { get; set; }
}
}

Now we can add logic to controller that populates links and pass view model to view.

public IActionResult Index()
{
var viewModel = new IndexViewModel
{
Links = new[]
{
new Link { Name = “The first link”, Url = “#” },
new Link { Name = “The second link”, Url = “#” },
new Link { Name = “The third link”, Url = “#” },
}
};
return View(viewModel);
}

Then view needs to know about the model. This can be done by adding @model to the file as show here.

@model IndexViewModel
@{
ViewData[“Title”] = “Home Page”;
}

Next thing is that we modify our partial view call little bit to pass links information to partial view.

If you need to pass model to partial view, you can add second parameter to call and then partial view can access to model and act based in the information it provides.

<div class=”row”>
<div class=”col-lg-12″>
@Html.Partial(“Links”, Model.Links)
</div>
</div>

Partial view doesn’t need to know other possible properties that our IndexViewModel has, because I want to reuse is with other views also and they don’t most likely use same view model as Index does. Next thing is to modify our partial view to understand Links. This is done by changing our partial view from static html to Razor syntax.

@model IEnumerable<Link>
<ul class=” list-inline”>
@foreach (var link in Model)
{
<li><a href=”@link.Url”>@link.Name</a></li>
}
</ul>

Now you can see exactly same links as before, but now I can control number of links and their Url and Name properties from code instead of hard coded HTML. Partial views are great for sharing HTML, scripts and styles, but the problem comes if you want to pass some data to it. This works fine, if you have couple views sharing same partial view, but it’s not very practical if same partial view and data are shared but multiple views. Each view using Links Partial view must implement IEnumerable<Link> property and it’s not very practical to repeat same code all over again. This approach violates DRY (Don’t repeat yourself) principle. Next, I show how to modify code so that I can reuse same functionality without adding unnecessary dependency between view models and Links.

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial

View component

View component is like partial view, but it has own Invoke method that is like controller’s methods so it doesn’t require any data from view. Even if you don’t need to share any data between view and view component, you can control behavior of view component using parameters. Those allow view component to work differently in different situations.

First, I create a folder ViewComponents to root folder and then I create class called Links. As you may notice Links doesn’t have ViewComponent suffix. This is intentional, because I don’t want system to resolve it by some magic string as it would do with suffix defined. Code contains dummy code that will populate links. Normally this code would generate links with some logic from some data storage. Now because links are generated inside method, I use await Task.Delay(1); to make system to accept it as asynchronous method. This is simple method to create some mockup code to test UI level functionality before using real data storage.

using System.Threading.Tasks;
using Demo.Models;
using Microsoft.AspNetCore.Mvc;

namespace Demo.ViewComponents
{
public class Links : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
await Task.Delay(1);
var links = new[]
{
new Link {Name = “The first link”, Url = “#”},
new Link {Name = “The second link”, Url = “#”},
new Link {Name = “The third link”, Url = “#”},
};

return View(links);
}
}
}

Then I create Components folder to Shared and Links folder to Components folder to follow naming scheme of view components in ASP.Net MVC. After I have created folder I need to create file called Default.chtml. This file contains same code as LinksPartial.chtml file.

@model IEnumerable<Link>
<ul class=” list-inline”>
@foreach (var link in Model)
{
<li><a href=”@link.Url”>@link.Name</a></li>
}
</ul>

Next, I add one new using to _ViewImports.chtml file. This is required to get code in Index.chtml to work. Demo is my projects name, so you need to add namespace where your view component classes are located.

@using Demo.ViewComponents

Next, I add view component to Index view under partial view. This way we can see that they are working and both are generating identical HTML.

<div class=”row”>
<div class=”col-lg-12″>
@await Component.InvokeAsync(typeof(Links))
</div>
</div>

mvc_part2_view_component

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components

Template

If you end up having many small partial views, you probably are trying to implement template using partial view. There are two types of templates (display and editor templates). You can define template for any type including view models. Template gives you same features as partial without magic strings.

I show now how to create simple edit and display templates for Link. First, I need to create DisplayTemplates folder under Shared, because we want this template to be globally available for any possible page. Next, I will create new file called Link.chtml.

@model Link
<a href=”@Model.Url”>@Model.Name</a>

Now I have my template defined and I can start using it. Next, I edit both partial view and view component so they can start using my template file instead do trying to replicate same functionality. I need to do one small change. Replace the content of the foreach loop with following code.

<li>@Html.DisplayFor(l => link)</li>

Now I can refresh my site and everything looks similar. Nothing has changed. Next, I create editor template for Link. First, I create EditorTemplates folder under Shared folder and new file called Link.chtml.

@model Link
<div class=”row form-group”>
<div class=”col-md-6″>
<h2 class=”panel-title”>Link</h2>
</div>
</div>
<div class=”row form-group”>
<div class=”col-md-6″>
<label>Name</label>
<input asp-for=”Name”/>
</div>
</div>
<div class=”row form-group”>
<div class=”col-md-6″>
<label>Url</label>
<input asp-for=”Url” />
</div>
</div>

Now I can use it in view with following html snippet. I created new Edit view for showing editor for single Link. In my case my model for view is also Link, so I will pass Model directly to EditorFor. If I would use  view model containing property for Link, I would use m => Model.Link for example.

@model Link
@{
ViewData[“Title”] = “Link Editor”;
}
<div class=”row”>
<div class=”col-lg-12″>
@Html.EditorFor(m => Model)
</div>
</div>
<div class=”row form-group”>
<div class=”col-md-6″>
<button class=”btn btn-default”>Save</button>
</div>
</div>

Leave a Reply

Your email address will not be published. Required fields are marked *