ASP.NET Core 2.2 For Beginners (Part 9): Razor Views

In this chapter, you will learn about different views that can be used for layout, to include namespaces, and to render partial content in a view.

Layout Views

The _Layout.cshtml Razor view gives the application more structure and makes it easier to display data that should be visible on every page, such as a navigation bar and a footer. You avoid duplication using this view. The underscore at the beginning of the name is not required, but it is a convention that is commonly used among developers. It signifies that the view shouldn’t be rendered as a view result with the View method from a controller action.

The normal views, like the Index view, are rendered inside the _Layout view. This means that they don’t have any knowledge about the navigation and the footer; they only need to render what the action tells them to render.

If you look inside the views you have created, they have some code in common, such as the <html>, <head>, and <body> elements. Because the markup is the same for all the views, it could be moved to the _Layout view.

Shared views, like _Layout, are placed in a folder called Shared inside the Views folder. These views are available anywhere in the application. The layout view doesn’t have to be named _Layout; you can even have multiple layout views in the application if you like.

The _Layout view is a Razor view, which means that you can use C# inside the view, like you can in any other view. It should also have a method called @RenderBody, which is responsible for rendering the different content views the user navigates to, such as the Index and the Details views.

There is an object called @ViewBag in the _Layout view. It is a dynamic object that you can use to send data from the server to the view.

Another method that can be used in the _Layout view is the @RenderSection. This method can be used to render specific sections of HTML from the content view in the _Layout view. There is an asynchronous version of this method that you can use if you want that type of behavior.

Adding the _Layout View

  1. Create a new folder named Shared inside the Views
  2. Add Razor Layout partial view to the Shared You will learn more about view in an upcoming chapter.
  3. Open the Index and Details view and remove the CDN links you added earlier.
  4. Open the Create view and cut out the CDN links that you added earlier.
  5. Paste in the <script> and <link> elements to the <head> element of the _Layout view for the CDN (Content Delivery Network) URLs to JQuery and Bootstrap, as well as for validation libraries. Using CDNs, you don’t have to add the files locally in your project.

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/
    jquery-validate/1.19.0/jquery.validate.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/
    jquery-validation-unobtrusive/3.2.11/
    jquery.validate.unobtrusive.js"></script>

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/
    bootstrap.min.js"></script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/
    twitter-bootstrap/4.1.3/css/bootstrap.min.css" />

  1. Add a <footer> element at the bottom of the <body> element.
  2. Add a call to the @RenderSection method to the <footer> element and pass in the name of the section that could be in any of the views. If you want the section to be optional, then pass in false for the second parameter. Name the section footer and pass in false.

<footer>@RenderSection("footer", false)</footer>

 

The complete markup for the _Layout view:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />

    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.js"></script>

    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" />

    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        @RenderBody()
    </div>

    <footer>
        @RenderSection("footer", false)
    </footer>
</body>
</html>

Altering the Content Views

Now that the _Layout view has been added, you need to remove the markup shared among the content views.

Open the Index view and remove the <head> and <body> elements, and do the same for the other views in the Home folder. You can use the Ctrl+E, D keyboard command to format the HTML.

Since you removed the <title> element from the view, you can add it to the ViewBag object as a property called Title. Assign the name of the view to the property. Since the ViewBag is placed inside a C# block, it doesn’t need the @-sign.

You can also use the Layout property in the C# block to tell the MVC framework which layout view to use with the view. The layout view must be specified with an explicit path, beginning with the tilde (~) sign.

The usual C# rules apply inside C# blocks, such as ending code lines with a semicolon.

  1. Open the Index view and remove all the <html>, <head>, and <body> elements, but leave the table and the @model

@model IEnumerable<AspNetCore22Intro.ViewModels.VideoViewModel>

<table>
    @foreach (var video in Model)
    {
        <tr>
            <td>@Html.ActionLink(video.Id.ToString(), "Details",
                new { id = video.Id })</td>
            <td>@video.Title</td>
            <td>@video.Genre</td>
        </tr>
    }
</table>

  1. Add a C# block below the @model directive and add a Title property to the ViewBag inside the C# block and assign a title to it (Home, in this case).Also, add the Layout property inside the C# block and assign the explicit path to the cshtml file.

@{
    ViewBag.Title = "Home";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

  1. Add a @section block named footer at the end of the Index view and place a <div> element with the text This is the Index footer inside it.

@section footer{ <div>This is the Index footer</div> }

  1. Repeat steps 1 and 2 for all the views in the Views/Home folder but change the Title property from Home to Details and Create
  2. Save all the files and switch to the browser. Navigate to the Index view (/). You should be able to see the footer text below the video list. This verifies that the layout view is used to render the Index.

The complete code in the Index view, after removing the elements:

@model IEnumerable<AspNetCore22Intro.ViewModels.VideoViewModel>

@{
    ViewBag.Title = "Home";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<table>
    @foreach (var video in Model)
    {
        <tr>
            <td>@Html.ActionLink(video.Id.ToString(), "Details",
                new { id = video.Id })</td>
            <td>@video.Title</td>
            <td>@video.Genre</td>
        </tr>
    }
</table>

@section{
    <div>This is the Index footer</div>
}

 

The complete code in the Details view, after removing the elements:

@model AspNetCore22Intro.ViewModels.VideoViewModel

@{
    ViewBag.Title = "Details";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div>Id: @Model.Id</div>
<div>Title: @Model.Title</div>
<div>Genre: @Model.Genre</div>

@Html.ActionLink("Home", "Index")

 

The complete code in the Create view, after removing the elements:

@using AspNetCore22Intro.Models
@model AspNetCore22Intro.Entities.Video
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    ViewBag.Title = "Create";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>Create Video</h1>

<form asp-action="Create" method="post">
    <div asp-validation-summary="All"></div>

    <table>
        <tr>
            <td><label asp-for="Title"></label></td>
            <td><input asp-for="Title" /></td>
            <td><span asp-validation-for="Title"></span></td>
        </tr>
        <tr>
            <td><label asp-for="Genre"></label></td>
            <td><select asp-for="Genre"
                 asp-items="Html.GetEnumSelectList<Genres>()"></select>
            </td>
            <td><span asp-validation-for="Genre"></span></td>
        </tr>
    </table>

    <input type="submit" value="Create" />
</form>

<div>
    <a asp-action="Index">Back to List</a>
</div>

The _ViewStart file

The Razor view engine has a convention that looks for a file called _ViewStart.cshtml. This file is executed before any other views, but it has no HTML output. One purpose it has is to remove duplicate code from code blocks in the views, like the Layout declaration. Instead of declaring the location of the _Layout view in each view, it can be placed inside the _ViewStart view. It is possible to override the settings in the _ViewStart view by adding the Layout declaration in individual views.

If you place this view directly in the Views folder, it will be available to all views. Placing it in another folder inside the Views folder makes it available to the views in that folder.

You can assign null to the Layout property in a specific view to stop any layout view from being used with the view.

Let’s create the _ViewStart view in the Views folder, and add the Layout declaration in it.

  1. Add a Razor View Start view to the Views folder (use the New Item dialog). This file will load the _Layout view that renderes the pages uniformally. It is important that you name it _ViewStart, to adhere to MVC conventions.
  2. Delete the _Layout view path from the Index , Details and Create views in the Views/Home
  3. Save all the files and navigate to the root (/). You should still see the text This is the Index footer rendered by the _Layout

The _ViewImports file

The Razor view engine has a convention that looks for a file called _ViewImports.cshtml. This file is executed before any other views, but it has no HTML output. You can use this file to add using statements that will be used by all the views; this removes code duplication and cleans up the views.

So, if you know that many views will use the same namespaces, then add them to the _ViewImports.cshtml file. Add the file to the Views folder.

  1. Add a Razor View Imports file named cshtml to the Views folder.
  2. Open the Create view and cut out the @using and @addTagHelper
  3. Open the _ViewImports view and paste in the code.
  4. Add a using statement to the Entities namespace.
  5. Save the _ViewImports
  6. Open the Create view and remove the namespace path in the @model The view should be able to find the Video model from the using statement in the _ViewImports view.

@model Video

  1. Open the Index view and cut out the ViewModels namespace path from the @model directive and add it as a using statement to the _ViewImports view and save it. Leave only the class name in the @model directive.

@model IEnumerable<VideoViewModel>

  1. Open the Details view and delete the ViewModels namespace path. Leave only the class name in the @model directive.

@model VideoViewModel

  1. Save all the files and navigate to the different views in the browser, to verify that the application still works as before.

The complete code in the _ViewImports file:

@using AspNetCore22Intro.Models
@using AspNetCore22Intro.Entities
@using AspNetCore22Intro.ViewModels
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Tag Helpers

Tag Helpers are new to ASP.NET Core, and can in many instances replace the old HTML helpers. The Tag Helpers blend in with the HTML as they appear to be HTML attributes or HTML elements.

You have already used Tag Helpers in the Create form. There you added the asp-for and asp-validation-for among others. They blend in much better than the alternatives: Label­For, TextBoxFor, EditorFor, and other HTML helpers that are used in previous versions of ASP.NET. You can still use Razor HTML Helpers in ASP.NET Core, and they have one benefit over Tag Helpers; they are tightly coupled to the model. This means that you get Intelli­Sense and can rename properties more easily. In a Tag Helper, the property is added as a string value.

To use the Tag Helpers, you need to add a @addTagHelper directive to the _ViewImports view, or in specific views where you want to use them. The first parameter, the asterisk, specifies that all Tag Helpers in that namespace should be available. You can change this to a specific Tag Helper if you don’t want to import all helpers.

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Let’s add a link calling the Create action from the Index view using Tag Helpers, so that you don’t have to type in the URL to the Create view in the browser. Let’s also replace the ActionLink HTML helper for the Id property, with a Tag Helper that opens the Details view and has the description Details.

Altering the Index View

  1. Open the Index
  2. Add an anchor tag (<a>) between the </table> tag and the @section Add the text Create to the anchor tag.
  3. Use the asp-action Tag Helper attribute to specify which action in the Home controller you want the link to call. You can add the asp-controller Tag Helper attribute if you want to navigate to a controller that the view doesn’t belong to.

<a asp-action="Create">Create</a>

  1. Save the file and navigate to the Index view in the browser. You should see a link with the text Create. When you click the link, the Create view should appear.
  2. Click the Back to List link to get back to the Index
  3. Place a breakpoint inside the HTTP GET Create action in the HomeController class, and start the application with debugging (F5).
  4. Click the Create link again. The execution should halt at the breakpoint. This demonstrates that the Action was called by the Tag Helper attribute.
  5. Remove the breakpoint and stop the application in Visual Studio.
  6. Remove the ActionLink for the Id
  7. Add an anchor tag that opens the Details view using the asp-action Tag Helper attribute, and the asp-route-id Tag Helper attribute to pass in the video id.

<td>
    <a asp-action="Details" asp-route-id="@video.Id">Details</a>
</td>

  1. Start the application without debugging (Ctrl+F5). You should now see Details Click one to verify that the Details view for that video is displayed.

The complete markup for the Index view:

@model IEnumerable<VideoViewModel>
@{
    ViewBag.Title = "Home";
}

<table>
    @foreach (var video in Model)
    {
        <tr>
            <td><a asp-action="Details"
                 asp-route-id="@video.Id">Details</a></td>
            <td>@video.Title</td>
            <td>@video.Genre</td>
        </tr>
    }
</table>

<a asp-action="Create">Create</a>

@section footer{
    <div>This is the Index footer</div>
}

Ading an Edit View and Its Actions

There are two more views needed to complete the CRUD operations, the Edit and Delete views. Let’s add the Edit view by copying the Create view and modify the form. Then let’s refactor the IVideoData interface, and the classes implementing it. Instead of saving data directly when a video is added or edited, this refactoring will make it possible to add or edit multiple videos before saving the changes to the database.

  1. Copy the Create view and paste it into the Home Rename the view Edit.
  2. Visual Studio sometimes gets confused when a view is copied, pasted, and renamed. To avoid confusion, close the Edit view and open it again.
  3. Change the title to Edit followed by the video title.

ViewBag.Title = $"Edit {Model.Title}";

  1. Do the same for the view’s heading; use the value from the ViewBag.

<h1>@ViewBag.Title</h1>

  1. Change the asp-action Tag Helper attribute to call an action named Edit; you will add the action to the HomeController class later. Make sure that the form is using the post method; it is safer than using the default get method when posting a form.

<form asp-action="Edit" method="post">

  1. Change the submit button’s text to Edit.

<input type="submit" value="Edit" />

  1. Open the Index view and add a link to the Edit view, like you did in the Details You can copy and change the Details anchor tag you added earlier. Move the links after the Genre table cell to make the form a little more pleasing to the eye.

<tr>
    <td>@video.Title</td>
    <td>@video.Genre</td>
    <td><a asp-action="Details"
         asp-route-id="@video.Id">Details</a></td>
    <td><a asp-action="Edit"
         asp-route-id="@video.Id">Edit</a></td>
</tr>

  1. To make the Edit link and view work, you have to add HTTP GET and HTTP POST Edit actions to the HomeController Let’s start with the HTTP GET action. Copy the HTTP GET Details action and paste it into the class. Rename it Edit and add the HttpGet attribute to it. This will make it possible to open the Edit view with the link you added in the Index view.

[HttpGet]
public IActionResult Edit(int id)
{
    ...
}

  1. Rename the model variable video.
  2. Replace the return statement with one that returns the video object to the view.

return View(video);

  1. Add an HTTP POST Edit action that has an id parameter of type int and a VideoCreateEditViewModel parameter called model. Add the HttpPost attribute to the action.

[HttpPost]
public IActionResult Edit(int id, VideoCreateEditViewModel model)
{
    ...
}

  1. Fetch the video matching the passed-in id and store it in a variable called video.

var video = _videos.Get(id);

  1. Add an if-statement that checks if the model state is invalid, or the video object is null. If any of them are true, then return the view with the model.

if (video == null || !ModelState.IsValid)
    return View(model);

  1. Assign the Title and Genre values from the model to the video object you fetched. Entity Framework will keep track of changes to the video objects.

video.Title = model.Title;
video.Genre = model.Genre;

  1. Call the Commit method on the _Video This method does not exist yet, but you will add it to the IVideoData service classes shortly. After you have refactored the IVideoData service, the method will work, and save any changes to the database. Since Entity Framework keeps track of any changes to the DbContext, you don’t have to send in the video object to the Commit method.

_videos.Commit();

  1. Add a redirect to the Details

return RedirectToAction("Details", new { id = video.Id });

The complete code for the HTTP GET Edit action:

[HttpGet]
public IActionResult Edit(int id)
{
    var video = _videos.Get(id);

    if (video == null) return RedirectToAction("Index");

    return View(video);
}

 

The complete code for the HTTP POST Edit action:

[HttpPost]
public IActionResult Edit(int id, VideoCreateEditViewModel model)
{
    var video = _videos.Get(id);

    if (video == null || !ModelState.IsValid) return View(model);

    video.Title = model.Title;
    video.Genre = model.Genre;

    _videos.Commit();

    return RedirectToAction("Details", new { id = video.Id });
}

Refactoring the IVideoData Service

The idea is that you should be able to do multiple changes and add new videos before committing the changes to the database. To achieve this, you must move the SaveChanges method call to a separate method called Commit. Whenever changes should be persisted to the database, the Commit method must be called.

  1. Open the IVideoData
  2. Add a definition for a method called Commit that returns an int. The int value will in some instances reflect the number of records that were affected by the commit.

int Commit();

  1. Open the MockVideoData class and add a Commit method that returns 0. You must add the method even though it isn’t necessary for the mock data. The mock data is instantly saved when in memory. The interface demands that the Commit method is implemented.

public int Commit()
{
    return 0;
}

  1. Open the SqlVideoData class and add a Commit method that return the results from the call to the SaveChanges

public int Commit()
{
    return _db.SaveChanges();
}

  1. Remove the call to the SaveChanges method from the Add

public void Add(Video video)
{
    _db.Add(video);
}

  1. Open the HomeController and verify that the Commit method doesn’t have a red squiggly line and therefore is working properly.
  2. Call the Commit method in the Create action, below the call to the Add This is necessary since you refactored out the call to the SaveChanges method from the Add method in the SqlVideoData service.
  3. Save all files and navigate to the root URL. The new Edit links should appear to the right of the videos in the listing, beside the Details.
  4. Click the Edit link for one of the videos to open the new Edit
  5. Make some changes to the video and click the Edit

  6. The Details view for the video is displayed. Click the Home link to get back to the video list in the Index

  7. The Index view should reflect the changes you made to the video.

The complete code in the IVideoData interface:

public interface IVideoData
{
    IEnumerable<Video> GetAll();
    Video Get(int id);
    void Add(Video newVideo);
    int Commit();
}

 

The complete code in the SqlVideoData class:

public class SqlVideoData : IVideoData
{
    private readonly VideoDbContext _db;

    public SqlVideoData(VideoDbContext db)
    {
        _db = db;
    }

    public void Add(Video newVideo)
    {
        _db.Add(newVideo);
    }

    public int Commit()
    {
        return _db.SaveChanges();
    }

    public Video Get(int id)
    {
        return _db.Find<Video>(id);
    }

    public IEnumerable<Video> GetAll()
    {
        return _db.Videos;
    }
}

Partial Views

A partial view has two main purposes. The first is to render a portion of a view; the other is to enable the reuse of markup to clean up the code in a view.

To render a partial view, you can use either the synchronous @Html.Partial method or the asynchronous @Html.PartialAsync method, or you can use the <partial> Tag Helper. Both methods take two parameters, where the first is the name of the partial view and the second is an optional model object.

Note that partial views always use data from the parent view model.

The following example would render a partial view called _VideoPartial that receives a video object from the parent view’s model. The first code line is synchronous while the second is asynchronous; you choose which one you want to use.

@Html.Partial("_VideoPartial", video);

@await  Html.PartialAsync("_VideoPartial", video);

<partial name="_VideoPartial" model="video"/>

Let’s create a partial view called _VideoPartial to clean up the Index view. It will display the videos as sections, and get rid of that ugly table in the process.

  1. Add a new Razor View called _VideoPartial to the Home
  2. Delete all code inside the view.
  3. Add the VideoViewModel class as its model.

@model VideoViewModel

  1. Add a <section> element in the partial view.
  2. Add an <h3> element inside the <section> element and add the video title to it using the @Model

<h3>@Model.Title</h3>

  1. Add a <div> element below the <h3> element and add the video genre to it using the @Model

<div>@Model.Genre</div>

  1. Add another <div> element below the previous <div> element.
  2. Copy the Details and Edit links from the Index view and paste them into the newest <div> element. Change the asp-route-id Tag Helper to fetch its value from the @Model

<div>
    <a asp-action="Details" asp-route-id="@Model.Id">Details</a>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a>
</div>

  1. Open the Index view and replace the <table> element and all its content with a foreach loop that renders the partial view. The foreach loop is the same as the one in the <table> element, so you can copy it before removing the <table> element.

@foreach (var video in Model)
{
    <partial name="_VideoPartial" model="video"/>
}

  1. Place the remaining anchor tag inside a <div> element to make it easier to style.

<div>
    <a asp-action="Create">Create</a>
</div>

  1. Remove the @section footer You will display other information at the bottom of the page using a View Component in the next section.
  2. Save all the files and navigate to the root URL in the browser. The videos should now be stacked vertically as cards. They might not look pretty, but you can make them look great with CSS styling.

The complete code for the partial view:

@model VideoViewModel

<section>
    <h3>@Model.Title</h3>
    <div>@Model.Genre</div>
    <div>
        <a asp-action="Details" asp-route-id="@Model.Id">Details</a>
        <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a>
    </div>
</section>

 

The complete code for the Index view:

@model IEnumerable<VideoViewModel>
@{ ViewBag.Title = "Home"; }

@foreach (var video in Model)
{
    <partial name="_VideoPartial" model="video"/>
}

<div>
    <a asp-action="Create">Create</a>
</div>

View Components

A View Component is almost a complete MVC abstraction. It is a partial view that has its own model, which it gets from a method called Invoke in a controller-like class. A View Component’s model is independent from the current view’s model. You should not use a regular partial view, with a model, from the _Layout view, since it has no model and it is difficult to get one into it. Use a View Component to render partial content in the _Layout view.

In previous versions of MVC, you use @Html.ActionHelper to execute a child action. In this version of MVC it has been replaced with the View Component.

You can look at a View Component as having a controller that you never route to.

View Component views are always placed in a folder called Components inside the Views folder. If you place the folder in the Views/Shared folder, the view can be used from any view. Each View Component has a subfolder in the Components folder with the same name as the View Component.

Adding a View Component for the IMessageService Service

Let’s implement a View Component that uses the IMessageService service to display the configuration message in every view.

  1. Create a new folder called ViewComponents under the project node. This folder will hold the necessary files for View Components to work.
  2. Add a class called Message to the folder and inherit the ViewComponent Add a using statement to the Microsoft.AspNetCore.Mvc namespace to get access to the ViewComponent class.

public class Message : ViewComponent { }

  1. Add a constructor and inject the IMessageService interface to it, name the parameter message, and store it in a private class-level variable called _message. Add a using statement to the Services namespace to get access to the IMessageService

private readonly IMessageService _message;

public Message(IMessageService message)
{
    _message = message;
}

  1. Add a public method called Invoke that returns an IViewComponentResult.

public IViewComponentResult Invoke()
{
}

  1. Add a variable called model to the Invoke method, which stores the result from the GetMessage method call.

var model = _message.GetMessage();

  1. Return the model with the View Because the model is a string, the View method gets confused and thinks it is the name of the view to render. To fix this you pass in the name of the view as the first parameter and the model object as its second parameter.

return View("Default", model);

  1. Create a new folder called Components inside the Views/Shared
  2. Add a folder called Message inside the Components
  3. Add a Razor View called Default in the Message
  4. Delete all code in the view.
  5. Add an @model directive of type string.

@model string

  1. Add a <section> element with a <small> element inside it. Add the @Model value to the <small> element.

<section>
    <small>@Model</small>
</section>

  1. Open the _Layout view and call the InvokeAsync method on the Component property inside the <footer> element. Pass in the name of the View Component as a parameter. Remember to use @await when calling an asynchronous method.

<footer>
    @RenderSection("footer", false)
    @await Component.InvokeAsync("Message")
</footer>

  1. Save all the files.
  2. Navigate to all the views, one at a time, to verify that the message from the configuration file (Hello from configuration) is displayed in each of their footers.

 

The complete code for the Message View Component:

public class Message : ViewComponent
{
    private IMessageService _message;

    public Message(IMessageService message)
    {
        _message = message;
    }

    public IViewComponentResult Invoke()
    {
        var model = _message.GetMessage();
        return View("Default", model);
    }
}

 

The complete markup for the Default view:

@model string

<section>
    <small>@Model</small>
</section>

 

The complete code for the _Layout view:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        @RenderBody()
    </div>

    <footer>
        @RenderSection("footer", false)
        @await Component.InvokeAsync("Message")
    </footer>
</body>
</html>

Summary

In this chapter, you worked with layout views and partial views. You also used new features, such as Tag Helpers, View Components, and the _ViewStart and _ViewImport views.

Using these features allows you to reuse code and decompose a large view into smaller, more maintainable, pieces. They give you the ability to write maintainable and reusable code.

Stay connected with news and updates!

Join our mailing list to receive the latest news and updates from our team.
Don't worry, your information will not be shared.

Subscribe
Close

50% Complete

Two Step

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.