Skip to content

05 - Razor Views

Views in ASP.NET Core MVC

  • In the Model-View-Controller (MVC) pattern, the view encapsulates the presentation details of the user's interaction with the app.
  • Views are HTML templates with embedded code that generate content to send to the client.
  • Views use Razor syntax, which allows code to interact with HTML with minimal code or ceremony.

View files

  • Views use .cshtml extension
  • Are located in web project – Views folder
  • Typically, each controller has its own folder within Views folder
  • Typically, every action has its own view
    \Views\<ControllerName>\<ActionName>.cshtml
  • Shared views are in folder \Views\Shared
  • In addition to action specific views there are
    • partial views
    • layouts,
    • special view files
    • ...

Specify what view file to use

  • Usually just return View() or return View(viewModel) is used
  • View discovery process (unless concrete view is requested)
    \Views\<ControllerName>\<ActionName>.cshtml
    \Views\Shared\<ActionName>.cshtml
  • Returning some specific view
    1
    2
    return View(ViewName,viewModel); // cshtml extension not required
    return View(/Views/CustomViews/ViewName.cshtml,viewModel);
    

Passing data

Two widely used methods

  • Using ViewModels (DTOs) (can be cpecific objects or domain objects)
    • strongly typed – intellisense, compiler errors
    • strongly recommended (required in this course!)
  • Using ViewBag or ViewData
    • loosely typed data, no compiler support
    • erros can only be dicovered during runtime

Passing data - ViewModel

  • return view(viewModel);
  • In view file specify model class
    @model ModelType and access it with @Model
  • ViewModels are strongly typed, with full IntelliSense support. Views are precompiled by default on distribution build.
1
2
3
4
5
6
@model Domain.Person
<h2>Info</h2>
<div>
    @Model.FirstName<br /> 
    @Model.LastName<br />
</div>

Passing data - loosely typed

  • ViewData or ViewBag - collection of data (objects)
  • Accessible both in action methods and in views
  • ViewData – string based dictionary
    1
    ViewData["key"] = someObject;
    
  • ViewBag – dynamic object collection
    1
    ViewBag.otherKey = fooObject;
    
  • ViewBag uses ViewData for its actual object storage.
    So you can mix and match them.
    1
    2
    ViewData["foo"] = "bar";
    ViewBag.foo = ViewBag.foo + "barbar";
    
  • No intellisense support, needs casting (except strings)

Razor syntax

  • Razor is markup syntax for embedding server based code into web pages.
  • Razor syntax – Razor markup, C# code and HTML
  • Default language in razor is HTML
  • @ symbol is used to transiton from HTML into C#
  • Razor expressions – any output is rendered to html output

  • Implicit – generally spaces are not allowed
1
2
<div>@DateTime.Now</div>
<div>@DoSomething("Hello", "World")</div>
  • Explicit - @(...C#...) - spaces are ok
1
<div>@(DateTime.Now - TimeSpan.FromDays(7))</div

Expression encoding

  • C# expressions in Razor are converted to string foobar.ToString() and encoded.
  • If C# expressions evaluates to IHtmlContent, then it is not converted and rendered directly
  • Use html Raw helper to write out unencoded content
1
@Html.Raw("<span>Hello World</span>")

Razor code blocks

  • @{ ... c# statement; ... }
  • Default language in code block is C#
  • Using html inside code block transitions language back to html
1
2
3
4
@{
    var inCSharp = true;
    <p>Now in HTML, was in C# @inCSharp</p>
}
  • Explicit transition to html
  • Use razor <text></text> tags
  • Single line transition - @:
1
2
3
4
5
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    @:Name: @person.Name
}

Control structures

  • Conditionals
    • @if, else if, else
    • @switch
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@if (value % 2 == 0)
{
    <p>The value was even</p>
}
else if (value >= 1337)
{
    <p>The value is large.</p>
}
else
{
    <p>The value was not large and is odd.</p>
}
  • Looping
    • @for
    • @foreach
    • @while
    • @do while
  • Compound
    • @using
1
2
3
4
5
6
7
8
@using (Html.BeginForm())
{
    <div>
        email:
        <input type="email" id="Email" name="Email" value="" />
        <button type="submit"> Register </button>
    </div>
}

Comments in razor

  • Razor supports C# and HTML comments.
1
2
3
4
5
@{
    /* C# comment. */
    // Another C# comment.
}
<!-- HTML comment -->
  • Razor comments @* ... *@
1
2
3
4
5
6
7
 @*
    @{
        /* C# comment. */
        // Another C# comment.
    }
    <!-- HTML comment -->
*@

Razor directives

A directive will typically change the way a page is parsed or enable different functionality within your Razor page.

  • @using
  • @model
  • @inherits
  • @inject
  • @functions
  • @section
  • @page

Directives

Vies are transformed into C# code/classes and compiled

1
2
3
4
@{
    var output = "Hello World";
}
<div>Output: @output</div>

Directives add some modifications into generated C# code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class _Views_Something_cshtml : RazorPage<dynamic>
{
    public override async Task ExecuteAsync()
    {
        var output = "Hello World";

        WriteLiteral("/r/n<div>Output: ");
        Write(output);
        WriteLiteral("</div>");
    }
}
  • @using System.IO
    adds using to the generated code

  • @model SomeModelType
    public class_Views_Test_cshtml : RazorPage<SomeModelType>

  • @inject
    Enables you to inject a service from your service container into your Razor page for use.

  • @section
    used in conjunction with the layout page to enable views to render content in different parts of the rendered HTML page.

  • @functions { // C# Code }

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@functions {
    public string GetHello()
    {
        return "Hello";
    }
}

<div>From method: @GetHello()</div> 
~~~~

~~~csharp
public class _Views_Home_Test_cshtml : RazorPage<dynamic>
{
    public string GetHello()
    {
        return "Hello";
    }
    public override async Task ExecuteAsync()
    {
       x
        Write(GetHello());
        WriteLiteral("</div>\r\n");
    }
...

Layout

Most web apps have a common layout that provides the user with a consistent experience as they navigate from page to page.

MVVM

Default layout

  • Default layout is located at
    Views\Shared\_Layout.cshtml

  • Specifying an layout

    • Can use partial name or full path
  • Every layout must call RenderBody()
    Wherever RenderBody() is called, contents of the view will be rendered.

1
2
3
@{
    Layout = "_Layout";
}

Section

  • Defining placeholder in layout file
    @RenderSection("Scripts", required: false)

  • Then define the section in view

1
2
3
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Shared directives

Directives can be placed in file
_ViewImports.cshtml

Supports only these:

  • @addTagHelper
  • @removeTagHelper
  • @tagHelperPrefix
  • @using
  • @model
  • @inherits

Views\_ViewImports.cshtml is shared location for all views

1
2
3
4
5
@using WebApp
@using WebApp.Models.AccountViewModels
@using WebApp.Models.ManageViewModels
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Initial code

  • Place initial code in
    _ViewStart.cshtml
  • Included in front of every view
  • Hierarchical, like _ViewImports
  • These are included in order, starting from the /View, then from /Controller
1
2
3
@{
    Layout = "_Layout";
}

Partial views

A partial view is a view that is rendered within another view. The HTML output generated by executing the partial view is rendered into the calling (or parent) view.

Partial views do not include _ViewStart.cshtml

Partial tag helper.

1
2
3
<partial name="_PartialName" />
<partial name="_PartialName"  for="Model" view-data="ViewData"/>
<partial name="_PartialName"  model="Model" view-data="ViewData"/>
  • for
    • assigns model expression
  • model
    • assigns model instance
  • view-data
    • assigns ViewDataDictionary

Older, html helper partial.

1
2
3
4
5
6
7
@Html.Partial("SomePartialView")
@await Html.PartialAsync("SomePartialViewWithAsyncCode")

@{
    Html.RenderPartial("SomePartialView ");
    Html.RenderPartialAsync("SomePartialViewWithAsyncCode ");
}

Partial view discovery

1
2
3
4
/Areas/<Area-Name>/Views/<Controller-Name>
/Areas/<Area-Name>/Views/Shared
/Views/Shared
/Pages/Shared

Partial views can be chained—a partial view can call another partial view if a circular reference isn't formed by the calls. Relative paths are always relative to the current file, not to the root or parent of the file.

Area

  • Areas are used to organize related functionality into a group as a separate namespace (for routing) and folder structure (for views).
  • Using areas creates a hierarchy for the purpose of routing by adding another route parameter, area, to controller and action.
  • Effectively a separate MVC structure inside an web application

Folder structure

Typical folder structure

WebApp

  • Areas
    • Admin
      • Controllers
      • Views
      • Models
    • Client
      • Controllers
      • Views
      • Models
1
2
3
/Areas/<Area-Name>/Views/<Controller-Name>/<Action-Name>.cshtml
/Areas/<Area-Name>/Views/Shared/<Action-Name>.cshtml
/Views/Shared/<Action-Name>.cshtml

Area controllers

  • Structure of views folder is important – compiled singly. Controllers and models can be anywhere – one dll.
  • Assign controller to specific area using area attribute
1
2
3
4
5
6
namespace MyStore.Areas.Admin.Controllers;

[Area("Admin")]
public class HomeController : Controller {
    ...
}

Area routing

Set up convention-based routing

1
2
3
app.MapControllerRoute(
    name: "areas",
    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

Inside the same are - no action needed, ambient value is used

To different area

1
<a asp-area="Admin" asp-controller="Home" asp-action="Index">Admin</a>

To no area

1
<a asp-area="" asp-controller="Home" asp-action="Index">Home</a>