"
ASP.NET (snapshot 2017) Microsoft documentation and samples

View components

By Rick Anderson

View or download sample code ((xref:)how to download)

Introducing view components

New to ASP.NET Core MVC, view components are similar to partial views, but they are much more powerful. View components don’t use model binding, and only depend on the data you provide when calling into it. A view component:

View components are intended anywhere you have reusable rendering logic that is too complex for a partial view, such as:

A view component consists of two parts: the class (typically derived from ViewComponent) and the result it returns (typically a view). Like controllers, a view component can be a POCO, but most developers will want to take advantage of the methods and properties available by deriving from ViewComponent.

Creating a view component

This section contains the high-level requirements to create a view component. Later in the article, we’ll examine each step in detail and create a view component.

The view component class

A view component class can be created by any of the following:

Like controllers, view components must be public, non-nested, and non-abstract classes. The view component name is the class name with the “ViewComponent” suffix removed. It can also be explicitly specified using the ViewComponentAttribute.Name property.

A view component class:

View component methods

A view component defines its logic in an InvokeAsync method that returns an IViewComponentResult. Parameters come directly from invocation of the view component, not from model binding. A view component never directly handles a request. Typically, a view component initializes a model and passes it to a view by calling the View method. In summary, view component methods:

View search path

The runtime searches for the view in the following paths:

The default view name for a view component is Default, which means your view file will typically be named Default.cshtml. You can specify a different view name when creating the view component result or when calling the View method.

We recommend you name the view file Default.cshtml and use the Views/Shared/Components/<view_component_name>/<view_name> path. The PriorityList view component used in this sample uses Views/Shared/Components/PriorityList/Default.cshtml for the view component view.

Invoking a view component

To use the view component, call the following inside a view:

@Component.InvokeAsync("Name of view component", <anonymous type containing parameters>)

The parameters will be passed to the InvokeAsync method. The PriorityList view component developed in the article is invoked from the Views/Todo/Index.cshtml view file. In the following, the InvokeAsync method is called with two parameters:

[!code-cshtmlMain]

   1:  @using ViewComponentSample.Models
   2:  @model IEnumerable<TodoItem>
   3:   
   4:  <h2>ToDo</h2>
   5:  <table>
   6:      <tr>
   7:          <th>
   8:              @Html.DisplayNameFor(model => model.IsDone)
   9:          </th>
  10:          <th>
  11:              @Html.DisplayNameFor(model => model.Priority)
  12:          </th>
  13:          <th>
  14:              @Html.DisplayNameFor(model => model.Name)
  15:          </th>
  16:          <th></th>
  17:      </tr>
  18:      @foreach (var item in Model)
  19:      {
  20:          <tr>
  21:              <td>
  22:                  @Html.DisplayFor(modelItem => item.IsDone)
  23:              </td>
  24:              <td>
  25:                  @Html.DisplayFor(modelItem => item.Priority)
  26:              </td>
  27:              <td>
  28:                  @Html.DisplayFor(modelItem => item.Name)
  29:              </td>
  30:          </tr>
  31:      }
  32:  </table>
  33:   
  34:  <div>
  35:      @await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })
  36:  </div>

Invoking a view component as a Tag Helper

For ASP.NET Core 1.1 and higher, you can invoke a view component as a (xref:)Tag Helper:

[!code-cshtmlMain]

   1:  @using ViewComponentSample.Models
   2:  @addTagHelper *, ViewCompFinal
   3:  @model IEnumerable<TodoItem>
   4:   
   5:  <h2>ToDo</h2>
   6:   
   7:  <table>
   8:      <tr>
   9:          <th>
  10:              @Html.DisplayNameFor(model => model.IsDone)
  11:          </th>
  12:          <th>
  13:              @Html.DisplayNameFor(model => model.Priority)
  14:          </th>
  15:          <th>
  16:              @Html.DisplayNameFor(model => model.Name)
  17:          </th>
  18:          <th></th>
  19:      </tr>
  20:   
  21:      @foreach (var item in Model)
  22:      {
  23:          <tr>
  24:              <td>
  25:                  @Html.DisplayFor(modelItem => item.IsDone)
  26:              </td>
  27:              <td>
  28:                  @Html.DisplayFor(modelItem => item.Priority)
  29:              </td>
  30:              <td>
  31:                  @Html.DisplayFor(modelItem => item.Name)
  32:              </td>
  33:          </tr>
  34:      }
  35:  </table>
  36:  <div>
  37:      <vc:priority-list max-priority="2" is-done="false">
  38:      </vc:priority-list>
  39:  </div>

Pascal-cased class and method parameters for Tag Helpers are translated into their lower kebab case. The Tag Helper to invoke a view component uses the <vc></vc> element. The view component is specified as follows:

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Note: In order to use a View Component as a Tag Helper, you must register the assembly containing the View Component using the @addTagHelper directive. For example, if your View Component is in an assembly called “MyWebApp”, add the following directive to the _ViewImports.cshtml file:

@addTagHelper *, MyWebApp

You can register a View Component as a Tag Helper to any file that references the View Component. See (xref:)Managing Tag Helper Scope for more information on how to register Tag Helpers.

The InvokeAsync method used in this tutorial:

[!code-cshtmlMain]

   1:  @using ViewComponentSample.Models
   2:  @model IEnumerable<TodoItem>
   3:   
   4:  <h2>ToDo</h2>
   5:  <table>
   6:      <tr>
   7:          <th>
   8:              @Html.DisplayNameFor(model => model.IsDone)
   9:          </th>
  10:          <th>
  11:              @Html.DisplayNameFor(model => model.Priority)
  12:          </th>
  13:          <th>
  14:              @Html.DisplayNameFor(model => model.Name)
  15:          </th>
  16:          <th></th>
  17:      </tr>
  18:      @foreach (var item in Model)
  19:      {
  20:          <tr>
  21:              <td>
  22:                  @Html.DisplayFor(modelItem => item.IsDone)
  23:              </td>
  24:              <td>
  25:                  @Html.DisplayFor(modelItem => item.Priority)
  26:              </td>
  27:              <td>
  28:                  @Html.DisplayFor(modelItem => item.Name)
  29:              </td>
  30:          </tr>
  31:      }
  32:  </table>
  33:   
  34:  <div>
  35:      @await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })
  36:  </div>

In Tag Helper markup:

[!code-cshtmlMain]

   1:  @using ViewComponentSample.Models
   2:  @addTagHelper *, ViewCompFinal
   3:  @model IEnumerable<TodoItem>
   4:   
   5:  <h2>ToDo</h2>
   6:   
   7:  <table>
   8:      <tr>
   9:          <th>
  10:              @Html.DisplayNameFor(model => model.IsDone)
  11:          </th>
  12:          <th>
  13:              @Html.DisplayNameFor(model => model.Priority)
  14:          </th>
  15:          <th>
  16:              @Html.DisplayNameFor(model => model.Name)
  17:          </th>
  18:          <th></th>
  19:      </tr>
  20:   
  21:      @foreach (var item in Model)
  22:      {
  23:          <tr>
  24:              <td>
  25:                  @Html.DisplayFor(modelItem => item.IsDone)
  26:              </td>
  27:              <td>
  28:                  @Html.DisplayFor(modelItem => item.Priority)
  29:              </td>
  30:              <td>
  31:                  @Html.DisplayFor(modelItem => item.Name)
  32:              </td>
  33:          </tr>
  34:      }
  35:  </table>
  36:  <div>
  37:      <vc:priority-list max-priority="2" is-done="false">
  38:      </vc:priority-list>
  39:  </div>

In the sample above, the PriorityList view component becomes priority-list. The parameters to the view component are passed as attributes in lower kebab case.

Invoking a view component directly from a controller

View components are typically invoked from a view, but you can invoke them directly from a controller method. While view components do not define endpoints like controllers, you can easily implement a controller action that returns the content of a ViewComponentResult.

In this example, the view component is called directly from the controller:

[!code-csharpMain]

   1:  using System.Linq;
   2:  using Microsoft.AspNetCore.Mvc;
   3:  using ViewComponentSample.Models;
   4:  using System.Threading.Tasks;
   5:  using Microsoft.EntityFrameworkCore;
   6:   
   7:  namespace ViewComponentSample.Controllers
   8:  {
   9:      public class ToDoController : Controller
  10:      {
  11:          private readonly ToDoContext _ToDoContext;
  12:   
  13:          public ToDoController(ToDoContext context)
  14:          {
  15:              _ToDoContext = context;
  16:          }
  17:   
  18:          public IActionResult Index()
  19:          {
  20:              var model = _ToDoContext.ToDo.ToList();
  21:              return View(model);
  22:          }
  23:          #region snippet_IndexVC
  24:          public IActionResult IndexVC()
  25:          {
  26:              return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
  27:          }
  28:          #endregion
  29:   
  30:          public async Task<IActionResult> IndexFinal()
  31:          {
  32:              return View(await _ToDoContext.ToDo.ToListAsync());
  33:          }
  34:   
  35:          public IActionResult IndexNameof()
  36:          {
  37:              return View(_ToDoContext.ToDo.ToList());
  38:          }
  39:   
  40:          public IActionResult IndexFirst()
  41:          {
  42:              return View(_ToDoContext.ToDo.ToList());
  43:          }
  44:      }
  45:  }

Walkthrough: Creating a simple view component

Download, build and test the starter code. It’s a simple project with a Todo controller that displays a list of Todo items.

List of ToDos
List of ToDos

Add a ViewComponent class

Create a ViewComponents folder and add the following PriorityListViewComponent class:

[!code-csharpMain]

   1:  //#define V1
   2:  #if V1
   3:  #region snippet1
   4:  using Microsoft.AspNetCore.Mvc;
   5:  using Microsoft.EntityFrameworkCore;
   6:  using System.Collections.Generic;
   7:  using System.Linq;
   8:  using System.Threading.Tasks;
   9:  using ViewComponentSample.Models;
  10:   
  11:  namespace ViewComponentSample.ViewComponents
  12:  {
  13:      public class PriorityListViewComponent : ViewComponent
  14:      {
  15:          private readonly ToDoContext db;
  16:   
  17:          public PriorityListViewComponent(ToDoContext context)
  18:          {
  19:              db = context;
  20:          }
  21:   
  22:          public async Task<IViewComponentResult> InvokeAsync(
  23:          int maxPriority, bool isDone)
  24:          {
  25:              var items = await GetItemsAsync(maxPriority, isDone);
  26:              return View(items);
  27:          }
  28:          #region snippet2
  29:          private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
  30:          {
  31:              return db.ToDo.Where(x => x.IsDone == isDone &&
  32:                                   x.Priority <= maxPriority).ToListAsync();
  33:          }
  34:          #endregion
  35:      }
  36:  }
  37:  #endregion
  38:   
  39:  #endif

Notes on the code:

Create the view component Razor view

The markup @await Component.InvokeAsync shows the syntax for calling view components. The first argument is the name of the component we want to invoke or call. Subsequent parameters are passed to the component. InvokeAsync can take an arbitrary number of arguments.

Test the app. The following image shows the ToDo list and the priority items:

todo list and priority items
todo list and priority items

You can also call the view component directly from the controller:

[!code-csharpMain]

   1:  using System.Linq;
   2:  using Microsoft.AspNetCore.Mvc;
   3:  using ViewComponentSample.Models;
   4:  using System.Threading.Tasks;
   5:  using Microsoft.EntityFrameworkCore;
   6:   
   7:  namespace ViewComponentSample.Controllers
   8:  {
   9:      public class ToDoController : Controller
  10:      {
  11:          private readonly ToDoContext _ToDoContext;
  12:   
  13:          public ToDoController(ToDoContext context)
  14:          {
  15:              _ToDoContext = context;
  16:          }
  17:   
  18:          public IActionResult Index()
  19:          {
  20:              var model = _ToDoContext.ToDo.ToList();
  21:              return View(model);
  22:          }
  23:          #region snippet_IndexVC
  24:          public IActionResult IndexVC()
  25:          {
  26:              return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
  27:          }
  28:          #endregion
  29:   
  30:          public async Task<IActionResult> IndexFinal()
  31:          {
  32:              return View(await _ToDoContext.ToDo.ToListAsync());
  33:          }
  34:   
  35:          public IActionResult IndexNameof()
  36:          {
  37:              return View(_ToDoContext.ToDo.ToList());
  38:          }
  39:   
  40:          public IActionResult IndexFirst()
  41:          {
  42:              return View(_ToDoContext.ToDo.ToList());
  43:          }
  44:      }
  45:  }

priority items from IndexVC action
priority items from IndexVC action

Specifying a view name

A complex view component might need to specify a non-default view under some conditions. The following code shows how to specify the “PVC” view from the InvokeAsync method. Update the InvokeAsync method in the PriorityListViewComponent class.

[!code-csharpMain]

   1:  //#define Final
   2:  #if Final
   3:   
   4:  using Microsoft.AspNetCore.Mvc;
   5:  using Microsoft.EntityFrameworkCore;
   6:  using System.Collections.Generic;
   7:  using System.Linq;
   8:  using System.Threading.Tasks;
   9:  using ViewComponentSample.Models;
  10:   
  11:  namespace ViewComponentSample.ViewComponents
  12:  {
  13:      public class PriorityListViewComponent : ViewComponent
  14:      {
  15:          private readonly ToDoContext db;
  16:   
  17:          public PriorityListViewComponent(ToDoContext context)
  18:          {
  19:              db = context;
  20:          }
  21:         
  22:          private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
  23:          {
  24:              return db.ToDo.Where(x => x.IsDone == isDone &&
  25:                                   x.Priority <= maxPriority).ToListAsync();
  26:          }
  27:          #region snippet1
  28:          public async Task<IViewComponentResult> InvokeAsync(
  29:              int maxPriority, bool isDone)
  30:          {
  31:              string MyView = "Default";
  32:              // If asking for all completed tasks, render with the "PVC" view.
  33:              if (maxPriority > 3 && isDone == true)
  34:              {
  35:                  MyView = "PVC";
  36:              }
  37:              var items = await GetItemsAsync(maxPriority, isDone);
  38:              return View(MyView, items);
  39:          }
  40:          #endregion
  41:      }
  42:  }
  43:   
  44:  #endif

Copy the Views/Shared/Components/PriorityList/Default.cshtml file to a view named Views/Shared/Components/PriorityList/PVC.cshtml. Add a heading to indicate the PVC view is being used.

[!code-cshtmlMain]

   1:  @model IEnumerable<ViewComponentSample.Models.TodoItem>
   2:   
   3:  <h2> PVC Named Priority Component View</h2>
   4:  <h4>@ViewBag.PriorityMessage</h4>
   5:  <ul>
   6:      @foreach (var todo in Model)
   7:      {
   8:          <li>@todo.Name</li>
   9:      }
  10:  </ul>

Update Views/TodoList/Index.cshtml:

[!code-cshtmlMain]

   1:  @using ViewComponentSample.Models
   2:  @model IEnumerable<TodoItem>
   3:   
   4:  <h2>ToDo</h2>
   5:  <table>
   6:      <tr>
   7:          <th>
   8:              @Html.DisplayNameFor(model => model.IsDone)
   9:          </th>
  10:          <th>
  11:              @Html.DisplayNameFor(model => model.Priority)
  12:          </th>
  13:          <th>
  14:              @Html.DisplayNameFor(model => model.Name)
  15:          </th>
  16:          <th></th>
  17:      </tr>
  18:      @foreach (var item in Model)
  19:      {
  20:          <tr>
  21:              <td>
  22:                  @Html.DisplayFor(modelItem => item.IsDone)
  23:              </td>
  24:              <td>
  25:                  @Html.DisplayFor(modelItem => item.Priority)
  26:              </td>
  27:              <td>
  28:                  @Html.DisplayFor(modelItem => item.Name)
  29:              </td>
  30:          </tr>
  31:      }
  32:  </table>
  33:   
  34:  <div>
  35:      @await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })
  36:  </div>

Run the app and verify PVC view.

Priority View Component
Priority View Component

If the PVC view is not rendered, verify you are calling the view component with a priority of 4 or higher.

Examine the view path

ToDo output with Shared component view
ToDo output with Shared component view

Avoiding magic strings

If you want compile time safety, you can replace the hard-coded view component name with the class name. Create the view component without the “ViewComponent” suffix:

[!code-csharpMain]

   1:  // Used at the very end with Avoiding magic strings
   2:  //#define no_suffix
   3:  #if no_suffix
   4:  #region snippet1
   5:  using Microsoft.AspNetCore.Mvc;
   6:  using Microsoft.EntityFrameworkCore;
   7:  using System.Collections.Generic;
   8:  using System.Linq;
   9:  using System.Threading.Tasks;
  10:  using ViewComponentSample.Models;
  11:   
  12:  namespace ViewComponentSample.ViewComponents
  13:  {
  14:      public class PriorityList : ViewComponent
  15:      {
  16:          private readonly ToDoContext db;
  17:   
  18:          public PriorityList(ToDoContext context)
  19:          {
  20:              db = context;
  21:          }
  22:   
  23:          public async Task<IViewComponentResult> InvokeAsync(
  24:          int maxPriority, bool isDone)
  25:          {
  26:              var items = await GetItemsAsync(maxPriority, isDone);
  27:              return View(items);
  28:          }
  29:          private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
  30:          {
  31:              return db.ToDo.Where(x => x.IsDone == isDone &&
  32:                                   x.Priority <= maxPriority).ToListAsync();
  33:          }
  34:      }
  35:  }
  36:  #endregion
  37:  #endif

Add a using statement to your Razor view file, and use the nameof operator:

[!code-cshtmlMain]

   1:  @using ViewComponentSample.Models
   2:  @using ViewComponentSample.ViewComponents
   3:  @model IEnumerable<TodoItem>
   4:   
   5:  <h2>ToDo nameof</h2>
   6:  <!-- Markup removed for brevity.  -->
   7:  <table>
   8:      <tr>
   9:          <th>
  10:              @Html.DisplayNameFor(model => model.IsDone)
  11:          </th>
  12:          <th>
  13:              @Html.DisplayNameFor(model => model.Priority)
  14:          </th>
  15:          <th>
  16:              @Html.DisplayNameFor(model => model.Name)
  17:          </th>
  18:          <th></th>
  19:      </tr>
  20:      @foreach (var item in Model)
  21:      {
  22:          <tr>
  23:              <td>
  24:                  @Html.DisplayFor(modelItem => item.IsDone)
  25:              </td>
  26:              <td>
  27:                  @Html.DisplayFor(modelItem => item.Priority)
  28:              </td>
  29:              <td>
  30:                  @Html.DisplayFor(modelItem => item.Name)
  31:              </td>
  32:          </tr>
  33:      }
  34:  </table>
  35:   
  36:  <div>
  37:   
  38:      @await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })
  39:  </div>

Additional Resources



Comments ( )
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnetcore/mvc/views/view-components.htm
< THANKS ME>