The .NET Team's Favorite Razor Features

Jon Galloway - Sep 1 '21 - - Dev Community

Razor is a markup syntax for embedding .NET based code into webpages. It includes Razor markup, C#, and HTML. Files containing Razor generally have a .cshtml file extension. Razor is also found in Razor component files (.razor).

Razor is designed to be lightweight and unobtrusive, so you focus on your markup. However, there are a lot of hidden features that really come in handy! I asked some friends on the .NET team for some of their favorite Razor features, here's what they had to say.

Cache Tag Helper - Scott Hanselman

I really like taghelpers like cache... It's so easy!

@page 
@model ArchivesModel

<cache expires-after="@TimeSpan.FromHours(4)">
    @foreach (var show in Model.Shows)
    {
     <a href="@show.AsAwesomeUrl()" class="showCard">
      <img data-src="@("https://images.hanselminutes.com/podcast/shows/" 
        + show.Number + ".jpg")"
        loading="lazy" class="lazyload" 
        src="/images/grey.gif" width="212" height="212" 
        alt="@show.Title" />
      <span class="shownumber">@show.Number</span>
      <div class="overlay title">
       <!-- cant add padding to the wrapping div or the layout gets 
        messed up, title will be wrapped in a p tag
       which will have the padding added to it -->
       <p class="overlay-text">@show.Title</p>
      </div>
     </a>
    }
</cache>
Enter fullscreen mode Exit fullscreen mode

Docs: Cache taghelper

Implicit Razor expressions include async - James Montemagno

This is a core Razor feature that allows you to call C# asynchronous code directly from your cshtml!

<p>@await DoSomethingAsync("Hello", "from", "async")</p>
Enter fullscreen mode Exit fullscreen mode

Docs: Implicit Razor expressions

Custom Tag Helpers - Damian Edwards

Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files. There are a lot of built-in ones, but it's also very easy to write your own.

Here's an example of an asp-if Tag Helper that suppresses the output of the element if the supplied predicate equates to false.

[HtmlTargetElement("*", Attributes = "asp-if")]
public class IfTagHelper : TagHelper
{
    /// <summary>
    /// The predicate expression to test.
    /// </summary>
    [HtmlAttributeName("asp-if")]
    public bool Predicate { get; set; }

    public override void Process(TagHelperContext context, 
        TagHelperOutput output)
    {
        if (!Predicate)
        {
            output.SuppressOutput();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Here's how you'd use it:

<div asp-if="(DateTime.UtcNow.Minute % 2) == 0">
  This paragraph will only render during <strong>even</strong> minutes.
</div>
<div asp-if="(DateTime.UtcNow.Minute % 2) == 1">
  This paragraph will only render during <strong>odd</strong> minutes.
</div>
Enter fullscreen mode Exit fullscreen mode

I've written a lot of sample tag helpers in this TagHelper Pack Samples repository.

GitHub logo DamianEdwards / TagHelperPack

A set of useful, and possibly opinionated, Tag Helpers for ASP.NET Core

TagHelperPack CI NuGet

A set of useful, and possibly opinionated, Tag Helpers for ASP.NET Core (all versions).

Included Tag Helpers & Examples

See the examples page at https://taghelperpack.net

Supports ASP.NET Core 6.0.x and 7.0.x. Also supports ASP.NET Core 2.1.x when running on .NET Framework 4.7.1 or higher.

Installing

  1. Add a reference to the package from the cmd line:

    MyGreatProject> dotnet add package TagHelperPack
    Enter fullscreen mode Exit fullscreen mode
  2. Restore:

    MyGreatProject> dotnet restore
    Enter fullscreen mode Exit fullscreen mode
  3. Register the Tag Helpers in your application's _ViewImports.cshtml file:

    @addTagHelper *, TagHelperPack
    Enter fullscreen mode Exit fullscreen mode
  4. Optional: Register optimizations in ConfigureServices() or Program.cs (ASP.NET Core 6+)

    services.AddTagHelperPack();
    Enter fullscreen mode Exit fullscreen mode





Templated components - Daniel Roth

Anything in a .razor file is a work of art 😍

Templated components are pretty useful. They're components that accept one or more UI templates as parameters, which can then be used as part of the component's rendering logic. Templated components allow you to author higher-level components that are more reusable than what was possible before. For example, a list view component could allow the user to specify a template for rending items in the list, or a grid component could allow the user to specify templates for the grid header and for each row.

<h1>Pets</h1>

<TableTemplate Items="pets" Context="pet">
    <TableHeader>
        <th>ID</th>
        <th>Name</th>
    </TableHeader>
    <RowTemplate>
        <td>@pet.PetId</td>
        <td>@pet.Name</td>
    </RowTemplate>
</TableTemplate>

@code {
    private List<Pet> pets = new()
    {
        new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
        new Pet { PetId = 4, Name = "Salem Saberhagen" },
        new Pet { PetId = 7, Name = "K-9" }
    };

    private class Pet
    {
        public int PetId { get; set; }
        public string Name { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Docs: Templated components

ViewComponentTagHelpers - Taylor Mullen

You can create a ViewComponent and have it return Content, Views or do any other complex logic:

using Microsoft.AspNetCore.Mvc;

namespace WebApplication3000
{
   public class Foo: ViewComponent
   {
       public IViewComponentResult Invoke()
       {
           return Content("Hello, World!");
       }
   }   
}
Enter fullscreen mode Exit fullscreen mode

And then they instantly get transformed into TagHelpers that you can utilize in your views:

@addTagHelper *, WebApplication3000

<vc:foo />
Enter fullscreen mode Exit fullscreen mode

Docs: View Components

@inject - Brady Gaster

@inject is 🔥🔥🔥! I love that I can do that - it's crazy hawtsauce awesome.

Here's an example injecting configuration:

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration
@{
   string myValue = Configuration["root:parent:child"];
   ...
}
Enter fullscreen mode Exit fullscreen mode

You can also inject services:

@using System.Threading.Tasks
@using ViewInjectSample.Model
@using ViewInjectSample.Model.Services
@model IEnumerable<ToDoItem>
@inject StatisticsService StatsService
<!DOCTYPE html>
<html>
<head>
    <title>To Do Items</title>
</head>
<body>
    <div>
        <h1>To Do Items</h1>
        <ul>
            <li>Total Items: @StatsService.GetCount()</li>
            <li>Completed: @StatsService.GetCompletedCount()</li>
            <li>Avg. Priority: @StatsService.GetAveragePriority()</li>
        </ul>
        <table>
            <tr>
                <th>Name</th>
                <th>Priority</th>
                <th>Is Done?</th>
            </tr>
            @foreach (var item in Model)
            {
                <tr>
                    <td>@item.Name</td>
                    <td>@item.Priority</td>
                    <td>@item.IsDone</td>
                </tr>
            }
        </table>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Docs: Dependency injection into views in ASP.NET Core

Razor Class Libraries - Jeff Fritz

I like Razor Component Libraries. Just write a .razor file and it's a component that can be referenced. I used these a lot in my BlazorWebFormsComponents project, which helps with migrating ASP.NET Web Forms project to Blazor.

Here's an example Label component, implemented as Label.razor:

@inherits BaseWebFormsComponent

@if (Visible)
{
    <span id="@ID">@Text</span>
}
Enter fullscreen mode Exit fullscreen mode

The logic is in Label.razor.cs:

using BlazorWebFormsComponents.Interfaces;
using Microsoft.AspNetCore.Components;

namespace BlazorWebFormsComponents
{
    public partial class Label : BaseWebFormsComponent, ITextComponent
    {
        [Parameter]
        public string Text { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

GitHub logo FritzAndFriends / BlazorWebFormsComponents

A collection of Blazor components that emulate the ASP.NET Web Forms controls of the same name

BlazorWebFormsComponents

A collection of Blazor components that emulate the web forms components of the same name

Build status Join the chat at https://gitter.im/BlazorWebFormsComponents/community docs

Nuget Nuget (with prereleases) Live Sample

Live Samples running on Azure

Approach + Considerations

We believe that Web Forms applications that have been well maintained and provide value should have a path forward to the new user-interface frameworks with minimal changes. This is not an application converted nor is it a patch that can be applied to your project that magically makes it work with ASP.NET Core. This repository contains a library and series of strategies that will allow you to re-use much of your markup, much of your business code and help shorten your application re-write process.

This is not for everyone, not everyone needs to migrate their application. They can continue being supported as Web Forms for a very long time (2029 EOL at the time of this writing) and the applications that are considered for migration…




Use all the .NET Features - David Pine

Here's a really useful Razor snippet. This is one of the things I love about Razor syntax, not only do I get to use my existing C# skills but I can use all the language features that make me a productive developer.

@using static System.Runtime.InteropServices.RuntimeInformation

<div class="built-with">
    @FrameworkDescription
</div>
Enter fullscreen mode Exit fullscreen mode

Your eyes are not fooling you, that is in fact a using static declaration — meaning that all of the static members of the System.Runtime.InteropServices.RuntimeInformation type are available without their type qualifier. I can seamlessly mesh HTML and C# together, using a powerful templating engine to create rich-content. This is a simple example, but there are many more. You’re limited by your imagination and the constructs surrounding HTML and C# semantics. When this is rendered as HTML, you’d end up with a <div> element containing the .NET framework description. I love seeing the runtime version in my .NET apps 💜.

Resulting HTML:

<div class="built-with"> 
.NET 6.0.0-preview.7.21377.19
</div>
Enter fullscreen mode Exit fullscreen mode

Explicit transitions - Jon Galloway

In most cases, Razor implicitly transitions between C# and HTML without you needing to think about it. It's occasionally useful to explicitly call out a block as HTML text inline in C# code. You can do that with an explicit transition. For example, this can be useful if you're writing out JavaScript or CSS from within a C# code block.

You can use the <text></text> tag structure to delimit a block, and you can use @: for a single line.

<script>
@for(int i = 0; i < 10; i++) 
{
    @:console.log(@i);
}
</script>
Enter fullscreen mode Exit fullscreen mode

which will render:

<script>
    console.log(0);
    console.log(1);
    console.log(2);
    console.log(3);
    console.log(4);
    console.log(5);
    console.log(6);
    console.log(7);
    console.log(8);
    console.log(9);
</script>
Enter fullscreen mode Exit fullscreen mode

This is handy for things like rendering a chart with data populated on the server, like this:

@section Scripts {
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<script>
    window.onload = function () {
        var dataPoints = [];
        var chart = new CanvasJS.Chart("chartContainer", {
            animationEnabled: true,
            data: [
                {

                    dataPoints: dataPoints
                }
            ]
        });
        @{Random rand = new Random();}
        @for(int i = 0; i < 10; i++)
        {
            @:dataPoints.push({ x: @i, y: @rand.Next(0, 100) });
        }

        chart.render();
    };
</script>
}
Enter fullscreen mode Exit fullscreen mode

Docs: Dependency injection into views in ASP.NET Core

Learn more about Razor and Razor Pages

Want to learn more about Razor and Razor Pages? Check out this 2 hour intro: Let's Learn .NET: Razor Pages.

What about you?

Got a favorite Razor feature you'd like to share? Drop it in the comments!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .