Monthly Archives: December 2017

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>

Building ASP.Net MVC site Part 1

MVC (Model View Controller) allows you to create different screen components to help development of your site. Here is a short introduction that helps you to understand some practices of building good application.

Shared MVC views

MVC contains three shared views:

  • _Layout.cshtml (in /Views/Shared)
    File defines common layout for “all” views. You can create custom layout file if required, but I don’t recommend doing it. It’s seldom needed. You should keep it as the last resort. Layout file defines body section (not same as HTML body tag) and one or more sections. Default layout file contains one scripts section that allows developer to inject script tags from view to rendered page.
  • _ViewImports.cshtml (in /Views)
    File defines import statements (same as using statements in C# code). This is hierarchical. Each folder can contain one. You should carefully think what you want to put here. I recommend that you add to common _ViewImports.cshtml file in /Views folder only those namespaces you really require in “every” page and if you will create controller level files, you will put only common definitions shared in all views to that file.
  • _ViewStart.cshtml (in /Views)
    File defines code executed before view. This file is also hierarchical. Each folder can contain one. If folder contains _ViewStart.chtml file, it will be executed after shared one. Default file defines Layout file that view uses. I personally wouldn’t use this for any custom code. You can Controller or Service to provide additional logic you need in your page or use some other technique to add logic in place you can better manage (create unit tests etc.).

You should always remember that if something is technically possible, it’s not always the recommended way of doing things like over using those three files.

_Layout.chtml

As described above layout is used for creating common layout for all views. There are couple things you should know about placement of different html elements in the file.

Your view is rendered before jQuery and other scripts are loaded. This is for performance reasons so that HTML will be rendered before larger javascript files will get loaded. This can however cause some problems, if you want to add script tags to partial views or view components.

_ViewImports.cshtml

View imports is used mainly for declaring namespaces for view. It also contains several other purposes. As I have already said, you shouldn’t use all possible features even they are technically possible. This applies to imports file well. If multiple _ViewImports.cshtml files are run for a view, combined behavior of the directives included in the ViewImports.cshtml files will be as follows:

  • @addTagHelper, @removeTagHelper: all run, in order
    I recommend that you define all used tag helpers to main imports file in Views folder. Tag helpers should be common for your application. If you will end up having view specific tag helpers, you should consider if you are developing maintenance nightmare or are your tag helpers doing too much inside one tag helper.
  • @tagHelperPrefix: the closest one to the view overrides any others
    I don’t recommend using this at all. Your tag helpers should follow HTML syntax and there are no namespaces or prefixes.
  • @model: the closest one to the view overrides any others
    If you want to define model to here, you really should be sure that you can reuse same model in all or almost all views this imports file is affecting. In any other case, you shouldn’t use this setting either. Preferred way is to define model in view.
  • @inherits: the closest one to the view overrides any others
    This will allow you to create a custom page class instead of default RazorPage<T> where T is the model you defined for the page. This can be useful in some special cases where you want to all some custom functionality to page. In most cases however this can be done with several other means like tag helpers or dependency injection. I personally find this a method echo from the old days.
  • @using: all are included; duplicates are ignored
    This is the most common usage for the imports file. It helps you to import common namespaces to your views. In most cases you will have own namespaces for view models that are specific to one domain. You can easily add those namespaces to all your domain specific views by creating own imports file to same folder where your views are.
  • @inject: for each property, the closest one to the view overrides any others with the same property name
    Inject allows you to add additional services to the views. I think the most common use case is to add Localizer to your views if you want to use multilingual site. You should use inject in same way as tag helpers. Add inject lines to common imports file and do not use inject directive in any other imports file unless you have really good reason for it.

_ViewStart.cshtml

View start allows you to execute common code in views. This can be used similar cases as inherits directive could be used. Code is executed in all views that are in same folder or any sub folders beneath view start file. This means that you need to be sure that all views can execute this code. I recommend that you will use some other method to achieve similar functionality.

Building ASP.Net MVC site series: