Skip to main content

09 - Razor Pages - Views

Passing data

Loosely typed (ViewData)

  • ViewData - collection of data
  • Accessible both in code and in views
    • ViewData – string keys based dictionary (Dict<string,object>)
    • ViewData["key"] = someObject;
  • 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 transition from HTML into C#
  • Razor expressions – any output is rendered to html output
    • Implicit – generally spaces are not allowed
      <div>@DateTime.Now</div>
      <div>@DoSomething("Hello", "World")</div>
    • Explicit - @(…C#...) - spaces are ok
      <div>@(DateTime.Now - TimeSpan.FromDays(7))</div

Expression encoding

  • C# expressions in Razor are converted to string (.ToString()) and html encoded
  • If C# expressions evaluates to IHtmlContent, then it is not encoded and rendered directly
  • Use html Raw helper to write out unencoded content
    @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

  • Explicit transition to html

    • Use razor <text></text> tags
    @{ var inCSharp = true;
    <p>Now in HTML, was in C# @inCSharp</p>
    }
    • Single line transition - @:
    @for (var i = 0; i < people.Length; i++) { var person = people[i]; @:Name: @person.Name }

Control structures

Conditionals @if, else if, else @switch

@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

Comments

Razor supports C# and HTML comments.

@{ /* C# comment. */ // Another C# comment. }
<!-- HTML comment -->

Razor comments @* …. *@

@* @{ /* C# comment. */ // Another C# comment. }
<!-- HTML comment -->
*@

Razor view directives

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

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

@using, @model, @inject, @section

  • @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

@function

@functions { // C# Code }

@functions { public string GetHello() { return "Hello"; } }

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

Layout

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

Placeholder

Razor pages layout

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

  • Specifying an layout

    • Can use partial name or full path
    @{ Layout = "_Layout"; }
  • Every layout must call RenderBody()
    Wherever RenderBody() is called, contents of the view will be rendered.

Section

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

Then define the section in view:

@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
@using WebApp
@using WebApp.Models.AccountViewModels
@using WebApp.Models.ManageViewModels
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Pages\_ViewImports.cshtml is shared location for all views.

Initial code

  • Place initial code in _ViewStart.cshtml
  • Included in front of every page
  • Hierarchical, like _ViewImports
  • These are included in order, starting from the /Pages
@{
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

Calling an partial view (returns an IHtmlString):

  • @Html.Partial(“_SomePartialView")
  • @await Html.PartialAsync("_SomePartialViewWithAsyncCode")
  • <partial name="_PartialName" />

Referencing

// Uses a view in current folder with this name
// If none is found, searches the Shared folder
@Html.Partial("ViewName")

// A view with this name must be in the same folder
@Html.Partial("ViewName.cshtml")

// Locate the view based on the application root
// Paths that start with "/" or "~/" refer to the application root
@Html.Partial("~/Pages/Folder/ViewName.cshtml")
@Html.Partial("/Pages/Folder/ViewName.cshtml")

// Locate the view using relative paths
@Html.Partial("../Account/LoginPartial.cshtml")

Accessing data

  • Receives a copy of parents ViewData (not persisted)
  • You can pass an instance of ViewDataDictionary
    @Html.Partial("PartialName", customViewData)
  • You can pass an ViewModel (declared with @model in partial)
    @Html.Partial("PartialName", Model.ContactType)
  • You can pass both an ViewModel and an instance of ViewDataDictionary
    @Html.Partial("ArticleSection", sectionModel, new ViewDataDictionary(this.ViewData) { { "index", index } })
  • You can create new ViewDataDictionary and include existing viewData into it
    new ViewDataDictionary(this.ViewData) { { "index", index } }

Tag helpers

  • Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files.
  • There are many built-in Tag Helpers for common tasks - such as creating forms, links, loading assets and more - and even more available in public GitHub repositories and as NuGet packages.
  • Tag Helpers are authored in C#, and they target HTML elements based on element name, attribute name, or parent tag.
  • Tag helpers do not replace Html helpers – there is not a Tag helper for every Html helper.

<form> helpers #1

  • asp-all-route-data
  • asp-antiforgery
  • asp-fragment
  • asp-page
  • asp-route-<parameter name>
 <form
asp-page="SomeOtherPage"
asp-route-returnurl="@ViewData["ReturnUrl"]"
method="post"
class="form-horizontal">
<form method="post" class="form-horizontal" action="/Account/Login"></form>

<form> helpers #2

  • Asp-page="<Page Name>"
    Forms target is constructed out of page
  • Asp-route-<parameter name>
    Parameter name is added to forms target (as query string)
  • Asp-antiforgery="true/false"
    Generate anti-forgery token as hidden value to form.Usually controlled by [ValidateAntiForgeryToken] attribute in Controller
<input
name="__RequestVerificationToken"
type="hidden"
value="CfDJ8O3e77kPeG5Fju-exRwNp8_5FUhPiQ-vxSyhpobWy0ORwL1QWwrZfyGSKxe-hClHCByTfSImSXgBIJ-cxSgHrvQsOLluGSIwgAFHclMpAIWaBBB8csOoiW0gS1o_bp_sQlhxrypM37B47MU8I_cfN1A"
/>

<form> helpers #3

  • Asp-all-route-data
    Give a dictionary for all route/target parameters
  • Asp-fragment
    add #<value> to route/target

<div>

  • Asp-validation-summary – display validation summary in this div
    • ValidationSummary.All – property and model
    • ValidationSummary.ModelOnly – only model
    • ValidationSummary.None - none

<input> helpers #1

  • Asp-for="<ModelExpression>"
    • Generates id and name attributes – used later in model binding
    • Sets the type attribute – based on model type and data annotations
    • Sets up client side validation asp-for="property1" becomes in generated code m=>m.property1
  • Asp-format="<format>"
    • Use to format value <input asp-for="SomeNumber" asp-format="{0:N4}"/>
  • Input tag helper does not handle collections and templates – use Html.XxxxxFor

<input> helpers #2

Input type is set based on .NET type.

.NET typeInput type
Booltype="checkbox"
Stringtype="text"
DateTimetype="datetime"
Bytetype="number"
Inttype="number"
Single, Doubletype="number"

<input> helpers #3

Or use data annotations.

AttributeInput type
[EmailAddress]type="email"
[Url]type="url"
[HiddenInput]type="hidden"
[Phone]type="tel"
[DataType(DataType.Password)]type="password"
[DataType(DataType.Date)]type="date"
[DataType(DataType.Time)]type="time"

<span>

  • Asp-validation-for
    Display validation error (if there is one for this model property) in this span
<span asp-validation-for="LastName" class="text-danger"></span>

<label>

  • Asp-for
    Generate label for this model property
<label asp-for="LastName" class="col-md-2 control-label"></label>
<label class="col-md-2 control-label" for="FirstName">FirstName</label>

<textarea>

  • Asp-for
    Generate textarea input for this model property. Id, name, validation

<select>

  • Asp-for
    specifies the model property

  • Asp-items
    sepcifies option elements (List<SelectListItem>)

    <select asp-for="Country" asp-items="Model.Countries"></select>
  • You can generate option list from enums

    <select asp-for="EnumCountry" asp-items="Html.GetEnumSelectList<CountryEnum>()"></select>

option group <optgroup>

The HTML <optgroup> element is generated when the view model contains one or more SelectListGroup objects.

public CountryViewModelGroup()
{
var NorthAmericaGroup = new SelectListGroup { Name = "NA" };
var EuropeGroup = new SelectListGroup { Name = "EU" };

Countries = new List<SelectListItem>{
new SelectListItem{
Value = "MEX",
Text = "Mexico",
Group = NorthAmericaGroup
},
new SelectListItem{
Value = "FR",
Text = "France",
Group = EuropeGroup
},
...

<select> multi-select

The Select Tag Helper will automatically generate the multiple = "multiple" attribute if the property specified in the asp-for attribute is an IEnumerable

public class CountryViewModelIEnumerable
{
public IEnumerable<string> CountryCodes { get; set; }

public List<SelectListItem> Countries { get; } = new List<SelectListItem>
{
new SelectListItem { Value = "MX", Text = "Mexico" },
new SelectListItem { Value = "CA", Text = "Canada" },
...

Collections

public class ToDoItem
{
public string Name { get; set; }
public bool IsDone { get; set; }
}
@model List<ToDoItem>
@for (int i = 0; i < Model.Count; i++) {
<tr>
<td>
<label asp-for="@Model[i].Name"></label>
</td>
<td>
<input asp-for="@Model[i].IsDone" />
</td>
</tr>
}</ToDoItem
>

<a>

  • Asp-page, Asp-route-<parameter name>
  • Asp-host
    Specify host to use in generated link (default is relative to current host)
  • Asp-protocol
    Specify protocol to use (default is current protocol)

<img>

  • Asp-append-version="<true/false>"
    Enable cache busting. Generates file version hash and appends it to source.

<script> helpers #1

<script
src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk"
></script>
  • Asp-append-version
  • Asp-fallback-src
  • Asp-fallback-src-exclude
  • Asp-fallback-src-include
  • Asp-fallback-test
  • Asp-src-exclude
  • Asp-src-include

<script> helpers #2

  • Asp-append-version
  • Asp-fallback-src
    If asp-fallback-test is negative (no network) then fall back to this location
  • Asp-fallback-test
    Javascript functionality to test
  • Asp-fallback-src-exclude, Asp-fallback-src-include, Asp-src-exclude, Asp-src-include
    Comma separated list of sources to include or exclude
  • Asp-append-version
  • Asp-fallback-href
  • Asp-fallback-href-exclude
  • Asp-fallback-href-include
  • Asp-fallback-test-class
  • Asp-fallback-test-property
  • Asp-fallback-test-value
  • Asp-href-exclude
  • Asp-href-include
<link
rel="stylesheet"
href="https://…/3.3.7/css/bootstrap.min.css"
asp-fallback-href="~/lib/css/bootstrap.min.css"
asp-fallback-test-class="sr-only"
asp-fallback-test-property="position"
asp-fallback-test-value="absolute"
/>

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />

<environment>

  • Names=“comma_separated_list"
    asp.net core pre-defines following enviroments – Development, Staging, Production. Useful for branching in cshtml files.
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment names="Staging,Production">
<link
rel="stylesheet"
href="https://ajax.aspnetcdn.com/…/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only"
asp-fallback-test-property="position"
asp-fallback-test-value="absolute"
/>
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>

HTML helpers

  • Tag helpers are there in addition to Html helpers.
  • Some functionality is only possible with Html helpers.
  • Html helpers generate full html tags – harder to read cshtml files, designers cant modify easily
  • Four main categories of helpers
    • Output
    • Input
    • Form
    • Link

Output

  • DisplayFor
  • DisplayForModel
  • DisplayNameFor
  • DisplayNameForModel
  • DisplayTextFor
  • LabelFor
  • LabelForModel

Input

  • EditorFor
  • TextAreaFor
  • TextBoxFor
  • DropDownFor
  • EnumDropDownFor
  • ListBoxFor
  • RadioButtonFor
  • HiddenFor
  • CheckBoxFor
  • PasswordFor

Form

  • BeginForm
  • BeginRouteForm
  • EndForm
  • AntiForgeryToken
  • HiddenFor
  • ActionLink
  • RouteLink

EditorFor

  • EditorFor(expression)

  • EditorFor(expression, additionalViewData)

  • EditorFor(expression, templateName)

  • EditorFor(expression, templateName, additionalViewData)

  • EditorFor(expression, templateName, htmlFieldName)

  • EditorFor(expression, templateName, htmlFieldName, additionalViewData)

  • Expression - lambda

<dt>@Html.DisplayNameFor(model => model.FirstName)</dt>
<dd>@Html.DisplayFor(model => model.FirstName)</dd>
@Html.EditorFor( model => model.Participant.FirstName, new { htmlAttributes = new { @class = "form-control" } } )
@Html.ValidationMessageFor( model => model.Participant.FirstName, "", new { @class = "text-danger" } ) @Html.ActionLink("Show items",
"Show", new { id = 1}, htmlAttributes: new { @class = "btn btn-primary", role = "button" })

Named Handler Methods

asp-page-handler

Enables you to specify multiple methods that can be executed for a single verb. You might want to do this if your page features multiple forms, each one responsible for a different outcome, for example

<div class="row">
<div class="col-lg-1">
<form asp-page-handler="edit" method="post">
<button class="btn btn-default">Edit</button>
</form>
</div>
<div class="col-lg-1">
<form asp-page-handler="delete" method="post">
<button class="btn btn-default">Delete</button>
</form>
</div>
<div class="col-lg-1">
<form asp-page-handler="view" method="post">
<button class="btn btn-default">View</button>
</form>
</div>
</div>

OR

Form with multiple different actions.

<form method="post">
<button asp-page-handler="Register">Register Now</button>
<button asp-page-handler="RequestInfo">Request Info</button>
</form>
public async Task<IActionResult> OnPostRegisterAsync()
{
//…
return RedirectToPage();
}
public async Task<IActionResult> OnPostRequestInfo()
{
//…
return RedirectToPage();
}

All this will add ?handler=somehandler to url submitted to server. Asp.net core chooses correct method based on HTTP verb and handler value.

If you prefer not to have query string values representing the handler's name in the URL, you can use routing to add an optional route value for "handler" as part of the route template in the @page directive:

@page "{handler?}"