-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Tag Helper Components doc (#7922)
Addresses #4033 Addresses #5475 [Internal Review Page](https://review.docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/th-components?view=aspnetcore-2.1&branch=pr-en-us-7922)
- Loading branch information
1 parent
298133d
commit 65eaf9d
Showing
48 changed files
with
20,650 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
--- | ||
title: Tag Helper Components in ASP.NET Core | ||
author: scottaddie | ||
description: Learn what Tag Helper Components are and how to use them in ASP.NET Core. | ||
monikerRange: '>= aspnetcore-2.0' | ||
ms.author: scaddie | ||
ms.date: 09/18/2018 | ||
uid: mvc/views/tag-helpers/th-components | ||
--- | ||
# Tag Helper Components in ASP.NET Core | ||
|
||
By [Scott Addie](https://twitter.com/Scott_Addie) and [Fiyaz Bin Hasan](https://github.com/fiyazbinhasan) | ||
|
||
A Tag Helper Component is a Tag Helper that allows you to conditionally modify or add HTML elements from server-side code. This feature is available in ASP.NET Core 2.0 or later. | ||
|
||
ASP.NET Core includes two built-in Tag Helper Components: `head` and `body`. They're located in the `Microsoft.AspNetCore.Mvc.Razor.TagHelpers` namespace and can be used in both MVC and Razor Pages. Tag Helper Components don't require registration with the app in *_ViewImports.cshtml*. | ||
|
||
[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/mvc/views/tag-helpers/th-components/samples) ([how to download](xref:tutorials/index#how-to-download-a-sample)) | ||
|
||
## Use cases | ||
|
||
Two common use cases of Tag Helper Components include: | ||
|
||
1. [Injecting a `<link>` into the `<head>`.](#inject-into-html-head-element) | ||
1. [Injecting a `<script>` into the `<body>`.](#inject-into-html-body-element) | ||
|
||
The following sections describe these use cases. | ||
|
||
### Inject into HTML head element | ||
|
||
Inside the HTML `<head>` element, CSS files are commonly imported with the HTML `<link>` element. The following code injects a `<link>` element into the `<head>` element using the `head` Tag Helper Component: | ||
|
||
[!code-csharp[](th-components/samples/RazorPagesSample/TagHelpers/AddressStyleTagHelperComponent.cs)] | ||
|
||
In the preceding code: | ||
|
||
* `AddressStyleTagHelperComponent` implements `TagHelperComponent`. The abstraction: | ||
* Allows initialization of the class with a `TagHelperContext`. | ||
* Enables the use of Tag Helper Components to add or modify HTML elements. | ||
* The `Order` property defines the order in which the Components are rendered. `Order` is necessary when there are multiple usages of Tag Helper Components in an app. | ||
* `ProcessAsync` compares the execution context's `TagName` property value to `head`. If the comparison evaluates to true, the content of the `_style` field is injected into the HTML `<head>` element. | ||
|
||
### Inject into HTML body element | ||
|
||
The `body` Tag Helper Component can inject a `<script>` element into the `<body>` element. The following code demonstrates this technique: | ||
|
||
[!code-csharp[](th-components/samples/RazorPagesSample/TagHelpers/AddressScriptTagHelperComponent.cs)] | ||
|
||
A separate HTML file is used to store the `<script>` element. The HTML file makes the code cleaner and more maintainable. The preceding code reads the contents of *TagHelpers/Templates/AddressToolTipScript.html* and appends it with the Tag Helper output. The *AddressToolTipScript.html* file includes the following markup: | ||
|
||
[!code-html[](th-components/samples/RazorPagesSample/TagHelpers/Templates/AddressToolTipScript.html)] | ||
|
||
The preceding code binds a [Bootstrap tooltip widget](https://getbootstrap.com/docs/3.3/javascript/#tooltips) to any `<address>` element that includes a `printable` attribute. The effect is visible when a mouse pointer hovers over the element. | ||
|
||
## Register a Component | ||
|
||
A Tag Helper Component must be added to the app's Tag Helper Components collection. There are three ways to add to the collection: | ||
|
||
1. [Registration via services container](#registration-via-services-container) | ||
1. [Registration via Razor file](#registration-via-razor-file) | ||
1. [Registration via Page Model or controller](#registration-via-page-model-or-controller) | ||
|
||
### Registration via services container | ||
|
||
If the Tag Helper Component class isn't managed with `ITagHelperComponentManager`, it must be registered with the [dependency injection (DI)](xref:fundamentals/dependency-injection) system. The following `Startup.ConfigureServices` code registers the `AddressStyleTagHelperComponent` and `AddressScriptTagHelperComponent` classes with a [transient lifetime](xref:fundamentals/dependency-injection#lifetime-and-registration-options): | ||
|
||
[!code-csharp[](th-components/samples/RazorPagesSample/Startup.cs?name=snippet_ConfigureServices&highlight=12-15)] | ||
|
||
### Registration via Razor file | ||
|
||
If the Tag Helper Component isn't registered with DI, it can be registered from a Razor Pages page or an MVC view. This technique is used for controlling the injected markup and the component execution order from a Razor file. | ||
|
||
`ITagHelperComponentManager` is used to add Tag Helper Components or remove them from the app. The following code demonstrates this technique with `AddressTagHelperComponent`: | ||
|
||
[!code-cshtml[](th-components/samples/RazorPagesSample/Pages/Contact.cshtml?name=snippet_ITagHelperComponentManager)] | ||
|
||
In the preceding code: | ||
|
||
* The `@inject` directive provides an instance of `ITagHelperComponentManager`. The instance is assigned to a variable named `manager` for access downstream in the Razor file. | ||
* An instance of `AddressTagHelperComponent` is added to the app's Tag Helper Components collection. | ||
|
||
`AddressTagHelperComponent` is modified to accommodate a constructor that accepts the `markup` and `order` parameters: | ||
|
||
[!code-csharp[](th-components/samples/RazorPagesSample/TagHelpers/AddressTagHelperComponent.cs?name=snippet_Constructor)] | ||
|
||
The provided `markup` parameter is used in `ProcessAsync` as follows: | ||
|
||
[!code-csharp[](th-components/samples/RazorPagesSample/TagHelpers/AddressTagHelperComponent.cs?name=snippet_ProcessAsync&highlight=10-11)] | ||
|
||
### Registration via Page Model or controller | ||
|
||
If the Tag Helper Component isn't registered with DI, it can be registered from a Razor Pages page model or an MVC controller. This technique is useful for separating C# logic from Razor files. | ||
|
||
Constructor injection is used to access an instance of `ITagHelperComponentManager`. The Tag Helper Component is added to the instance's Tag Helper Components collection. The following Razor Pages page model demonstrates this technique with `AddressTagHelperComponent`: | ||
|
||
[!code-csharp[](th-components/samples/RazorPagesSample/Pages/Index.cshtml.cs?name=snippet_IndexModelClass)] | ||
|
||
In the preceding code: | ||
|
||
* Constructor injection is used to access an instance of `ITagHelperComponentManager`. | ||
* An instance of `AddressTagHelperComponent` is added to the app's Tag Helper Components collection. | ||
|
||
## Create a Component | ||
|
||
To create a custom Tag Helper Component: | ||
|
||
* Create a public class deriving from `TagHelperComponentTagHelper`. | ||
* Apply an `[HtmlTargetElement]` attribute to the class. Specify the name of the target HTML element. | ||
* *Optional*: Apply an `[EditorBrowsable(EditorBrowsableState.Never)]` attribute to the class to suppress the type's display in IntelliSense. | ||
|
||
The following code creates a custom Tag Helper Component that targets the `<address>` HTML element: | ||
|
||
[!code-csharp[](th-components/samples/RazorPagesSample/TagHelpers/AddressTagHelperComponentTagHelper.cs)] | ||
|
||
Use the custom `address` Tag Helper Component to inject HTML markup as follows: | ||
|
||
```csharp | ||
public class AddressTagHelperComponent : TagHelperComponent | ||
{ | ||
private readonly string _printableButton = | ||
"<button type='button' class='btn btn-info' onclick=\"window.open(" | ||
"'https://binged.it/2AXRRYw')\">" + | ||
"<span class='glyphicon glyphicon-road' aria-hidden='true'></span>" + | ||
"</button>"; | ||
|
||
public override int Order => 3; | ||
|
||
public override async Task ProcessAsync(TagHelperContext context, | ||
TagHelperOutput output) | ||
{ | ||
if (string.Equals(context.TagName, "address", | ||
StringComparison.OrdinalIgnoreCase) && | ||
output.Attributes.ContainsName("printable")) | ||
{ | ||
var content = await output.GetChildContentAsync(); | ||
output.Content.SetHtmlContent( | ||
$"<div>{content.GetContent()}</div>{_printableButton}"); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
The preceding `ProcessAsync` method injects the HTML provided to `SetHtmlContent` into the matching `<address>` element. The injection occurs when: | ||
|
||
* The execution context's `TagName` property value equals `address`. | ||
* The corresponding `<address>` element has a `printable` attribute. | ||
|
||
For example, the `if` statement evaluates to true when processing the following `<address>` element: | ||
|
||
[!code-cshtml[](th-components/samples/RazorPagesSample/Pages/Contact.cshtml?name=snippet_AddressPrintable)] | ||
|
||
## Additional resources | ||
|
||
* <xref:fundamentals/dependency-injection> | ||
* <xref:mvc/views/dependency-injection> | ||
* <xref:mvc/views/tag-helpers/builtin-th/Index> |
38 changes: 38 additions & 0 deletions
38
aspnetcore/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Contact.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
@page | ||
@model ContactModel | ||
<!-- <snippet_ITagHelperComponentManager> --> | ||
@using RazorPagesSample.TagHelpers; | ||
@using Microsoft.AspNetCore.Mvc.Razor.TagHelpers; | ||
@inject ITagHelperComponentManager manager; | ||
|
||
@{ | ||
string markup; | ||
|
||
if (Model.IsWeekend) | ||
{ | ||
markup = "<em class='text-warning'>Office closed today!</em>"; | ||
} | ||
else | ||
{ | ||
markup = "<em class='text-info'>Office open today!</em>"; | ||
} | ||
|
||
manager.Components.Add(new AddressTagHelperComponent(markup, 1)); | ||
} | ||
<!-- </snippet_ITagHelperComponentManager> --> | ||
|
||
<h2>Tag Helper Component registration via Razor</h2> | ||
|
||
<!-- <snippet_AddressPrintable> --> | ||
<address printable> | ||
One Microsoft Way<br /> | ||
Redmond, WA 98052-6399<br /> | ||
<abbr title="Phone">P:</abbr> | ||
425.555.0100 | ||
</address> | ||
<!-- </snippet_AddressPrintable> --> | ||
|
||
<address> | ||
<strong>Support:</strong> <a href="mailto:[email protected]">Support@example.com</a><br /> | ||
<strong>Marketing:</strong> <a href="mailto:[email protected]">Marketing@example.com</a> | ||
</address> |
26 changes: 26 additions & 0 deletions
26
...core/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Contact.cshtml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
using System; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
|
||
namespace RazorPagesSample.Pages | ||
{ | ||
public class ContactModel : PageModel | ||
{ | ||
public string Message { get; set; } | ||
|
||
public bool IsWeekend | ||
{ | ||
get | ||
{ | ||
var dayOfWeek = DateTime.Now.DayOfWeek; | ||
|
||
return dayOfWeek == DayOfWeek.Saturday || | ||
dayOfWeek == DayOfWeek.Sunday; | ||
} | ||
} | ||
|
||
public void OnGet() | ||
{ | ||
Message = "Your contact page."; | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
aspnetcore/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Error.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
@page | ||
@model ErrorModel | ||
@{ | ||
ViewData["Title"] = "Error"; | ||
} | ||
|
||
<h1 class="text-danger">Error.</h1> | ||
<h2 class="text-danger">An error occurred while processing your request.</h2> | ||
|
||
@if (Model.ShowRequestId) | ||
{ | ||
<p> | ||
<strong>Request ID:</strong> <code>@Model.RequestId</code> | ||
</p> | ||
} | ||
|
||
<h3>Development Mode</h3> | ||
<p> | ||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred. | ||
</p> | ||
<p> | ||
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application. | ||
</p> |
23 changes: 23 additions & 0 deletions
23
...etcore/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Error.cshtml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
|
||
namespace RazorPagesSample.Pages | ||
{ | ||
public class ErrorModel : PageModel | ||
{ | ||
public string RequestId { get; set; } | ||
|
||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); | ||
|
||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] | ||
public void OnGet() | ||
{ | ||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; | ||
} | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
aspnetcore/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Index.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
@page | ||
@model IndexModel | ||
|
||
<h2>Tag Helper Component registration via Razor Pages Page Model</h2> | ||
|
||
<address printable> | ||
One Microsoft Way<br /> | ||
Redmond, WA 98052-6399<br /> | ||
<abbr title="Phone">P:</abbr> | ||
425.555.0100 | ||
</address> | ||
|
||
<address> | ||
<strong>Support:</strong> <a href="mailto:[email protected]">Support@example.com</a><br /> | ||
<strong>Marketing:</strong> <a href="mailto:[email protected]">Marketing@example.com</a> | ||
</address> |
47 changes: 47 additions & 0 deletions
47
...etcore/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Index.cshtml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
namespace RazorPagesSample.Pages | ||
{ | ||
#region snippet_IndexModelClass | ||
using System; | ||
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers; | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
using RazorPagesSample.TagHelpers; | ||
|
||
public class IndexModel : PageModel | ||
{ | ||
private readonly ITagHelperComponentManager _tagHelperComponentManager; | ||
|
||
public bool IsWeekend | ||
{ | ||
get | ||
{ | ||
var dayOfWeek = DateTime.Now.DayOfWeek; | ||
|
||
return dayOfWeek == DayOfWeek.Saturday || | ||
dayOfWeek == DayOfWeek.Sunday; | ||
} | ||
} | ||
|
||
public IndexModel(ITagHelperComponentManager tagHelperComponentManager) | ||
{ | ||
_tagHelperComponentManager = tagHelperComponentManager; | ||
} | ||
|
||
public void OnGet() | ||
{ | ||
string markup; | ||
|
||
if (IsWeekend) | ||
{ | ||
markup = "<em class='text-warning'>Office closed today!</em>"; | ||
} | ||
else | ||
{ | ||
markup = "<em class='text-info'>Office open today!</em>"; | ||
} | ||
|
||
_tagHelperComponentManager.Components.Add( | ||
new AddressTagHelperComponent(markup, 1)); | ||
} | ||
} | ||
#endregion | ||
} |
8 changes: 8 additions & 0 deletions
8
aspnetcore/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Privacy.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
@page | ||
@model PrivacyModel | ||
@{ | ||
ViewData["Title"] = "Privacy Policy"; | ||
} | ||
<h2>@ViewData["Title"]</h2> | ||
|
||
<p>Use this page to detail your site's privacy policy.</p> |
11 changes: 11 additions & 0 deletions
11
...core/mvc/views/tag-helpers/th-components/samples/RazorPagesSample/Pages/Privacy.cshtml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using Microsoft.AspNetCore.Mvc.RazorPages; | ||
|
||
namespace RazorPagesSample.Pages | ||
{ | ||
public class PrivacyModel : PageModel | ||
{ | ||
public void OnGet() | ||
{ | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...-helpers/th-components/samples/RazorPagesSample/Pages/Shared/_CookieConsentPartial.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@using Microsoft.AspNetCore.Http.Features | ||
|
||
@{ | ||
var consentFeature = Context.Features.Get<ITrackingConsentFeature>(); | ||
var showBanner = !consentFeature?.CanTrack ?? false; | ||
var cookieString = consentFeature?.CreateConsentCookie(); | ||
} | ||
|
||
@if (showBanner) | ||
{ | ||
<nav id="cookieConsent" class="navbar navbar-default navbar-fixed-top" role="alert"> | ||
<div class="container"> | ||
<div class="navbar-header"> | ||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#cookieConsent .navbar-collapse"> | ||
<span class="sr-only">Toggle cookie consent banner</span> | ||
<span class="icon-bar"></span> | ||
<span class="icon-bar"></span> | ||
<span class="icon-bar"></span> | ||
</button> | ||
<span class="navbar-brand"><span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span></span> | ||
</div> | ||
<div class="collapse navbar-collapse"> | ||
<p class="navbar-text"> | ||
Use this space to summarize your privacy and cookie use policy. | ||
</p> | ||
<div class="navbar-right"> | ||
<a asp-page="/Privacy" class="btn btn-info navbar-btn">Learn More</a> | ||
<button type="button" class="btn btn-default navbar-btn" data-cookie-string="@cookieString">Accept</button> | ||
</div> | ||
</div> | ||
</div> | ||
</nav> | ||
<script> | ||
(function () { | ||
document.querySelector("#cookieConsent button[data-cookie-string]").addEventListener("click", function (el) { | ||
document.cookie = el.target.dataset.cookieString; | ||
document.querySelector("#cookieConsent").classList.add("hidden"); | ||
}, false); | ||
})(); | ||
</script> | ||
} |
Oops, something went wrong.