CshtmlComponent - ASP.NET Core MVC and Razor Pages Component V3.1.0
Using components in ASP.NET Core MVC or Razor Pages, out of the box, is annoying to say the least (read: a real PITA). Tag Helpers do not support Razor syntax, View Components can not access nested child content. Razor Components do not support runtime compilation and do not work too well in standard MVC or Razor Page projects. CshtmlComponent, from the perspective of an MVC or Razor Pages app, combines the best features of these technologies.
Note: This document assumes that you have a good understanding of C#, Razor markup and ASP.NET Core.
Install the Nuget package.
See more detailed Usage & Installation instructions.
CshtmlComponent
- Razor Syntax
- Nested Child Content
- Runtime Compilation
- MVC & Razor Pages
- Lenient File Structure
- Named Slots
- Reference Capturing
- One-Off Content [1]
- Head Content Injection [2]
- Body Content Injection [3]
Tag Helper
- Razor Syntax
- Nested Child Content
- Runtime Compilation
- MVC & Razor Pages
- Lenient File Structure
- Named Slots
- Reference Capturing
- One-Off Content [1]
- Head Content Injection [2]
- Body Content Injection [3]
View Component
- Razor Syntax
- Nested Child Content
- Runtime Compilation
- MVC & Razor Pages
- Lenient File Structure
- Named Slots
- Reference Capturing
- One-Off Content [1]
- Head Content Injection [2]
- Body Content Injection [3]
Razor Component
- Razor Syntax
- Nested Child Content
- Runtime Compilation
- MVC & Razor Pages
- Lenient File Structure
- Named Slots
- Reference Capturing
- One-Off Content [1]
- Head Content Injection [2]
- Body Content Injection [3]
✓ Feature Supported ◻ Feature Partially Supported ✕ Feature Not Supported
[1] One-Off ContentComponent content that is rendered only for the first instantiated component.
[2] Head Content Injection
Inject HTML content to the head
tag from a component.
Inject HTML content to the body
tag from a component.
Video Demonstration
Examples
This section contains examples of how CshtmlComponents can be coded.
Basic Example
A basic CshtmlComponent that features attributes and child content.
C# Code (ExampleComponent.cshtml.cs)
using Acmion.CshtmlComponent;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SampleRazorPagesApplication.Pages.Components.Example
{
// The associated tag of the component.
[HtmlTargetElement("ExampleComponent")]
public class ExampleComponent : CshtmlComponentBase
{
// Explicitly named attribute.
[HtmlAttributeName("Title")]
public string Title { get; set; } = "";
public ExampleComponent(IHtmlHelper htmlHelper) : base(htmlHelper)
{
// The constructor.
// Note: Only dependency injected arguments.
// This component resolves the name of the associated .cshtml file automatically.
// The path to the .cshtml file must match the namespace and class name perfectly.
// For this component the resolving logic is the following:
// Namespace = SampleRazorPagesApplication.Pages.Components.Example
// Class Name = ExampleComponent
//
// 1. Remove first part of namespace and replace dots with '/'.
// 2. Append a '/'.
// 3. Append Class Name.
// 4. Append ".cshtml".
//
// Associated .cshtml file = /Pages/Components/Example/ExampleComponent.cshtml
}
}
}
Cshtml Code (ExampleComponent.cshtml)
@* Reference the associated component as model. *@
@using SampleRazorPagesApplication.Pages.Components.Example
@model ExampleComponent
<!-- The content of the component. -->
<div class="example-component" style="border: medium solid rgba(0, 0, 0, 0.1); padding: 1rem;">
<h1>
ExampleComponent: @Model.Title
</h1>
<div class="example-component-child-content">
<!-- Render the child content. -->
@Html.Raw(Model.ChildContent)
</div>
</div>
Component Instantiation
<ExampleComponent Title="Some title">
<div>
Some custom HTML content.
<Box>
Supports nested components.
</Box>
</div>
</ExampleComponent>
Advanced Example
An advanced CshtmlComponent that features attributes, child content, named slots, typed slots, reference capturing, head content injection, body content injection, one-off content and model mutation.
C# Code (AdvancedExampleComponent.cshtml.cs)
using Acmion.CshtmlComponent;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SampleRazorPagesApplication.Pages.Components.Example
{
// The associated tag of the component.
[HtmlTargetElement("AdvancedExampleComponent")]
public class AdvancedExampleComponent : CshtmlComponentBase
{
// Explicitly named attribute.
[HtmlAttributeName("Title")]
public string Title { get; set; } = "";
// These properties will default to their kebab-cased variants.
public string FontSize { get; set; } = "1rem";
public string BackgroundColor { get; set; } = "rgba(255, 0, 0, 0.1)";
// A not HTML bound property, which can not be accessed as a attribute in the component tag.
[HtmlAttributeNotBound]
public string UppercaseTitle { get; set; } = "";
public AdvancedExampleComponent(IHtmlHelper htmlHelper) : base(htmlHelper, "/Pages/Components/Example/AdvancedExampleComponent.cshtml", "div", TagMode.StartTagAndEndTag)
{
// The constructor.
// Note: Only dependency injected arguments.
// "/Pages/Components/Example/AdvancedExampleComponent.cshtml" is the path to the associated .cshtml file.
// "div" is the output tag name. Defaults to null.
// TagMode.StartTagAndEndTag determines the tag structure, optional parameter. Defaults to TagMode.StartTagAndEndTag.
// Properties should not be accessed here, because they will not yet be set.
}
protected override Task ProcessComponent(TagHelperContext context, TagHelperOutput output)
{
// This method is called just before the associated .cshtml file is executed.
// Properties have been initialized and can be accessed.
// The property ChildContent is a string that contains the child content.
// Use this method to edit some other properties or fields.
UppercaseTitle = Title.ToUpperInvariant();
return base.ProcessComponent(context, output);
}
}
}
Cshtml Code (AdvancedExampleComponent.cshtml)
@* Reference the associated component as model. *@
@using SampleRazorPagesApplication.Pages.Components.Example
@model AdvancedExampleComponent
<!-- Content that is injected in to the <head> tag. -->
<!-- ---------------------------------------------- -->
<!-- ---------------------------------------------- -->
<!-- ---------------------------------------------- -->
<CshtmlHead>
<!-- This content will be appended to <head>, when this component is first instantiated. -->
<meta name="description" content="CshtmlHead, Key: default, ContentOrder: default" />
<link rel="stylesheet" href="~/css/advanced-example-component.css" />
<script>console.log("CshtmlHead, Key: default, ContentOrder: default");</script>
</CshtmlHead>
<CshtmlHead>
<!-- This content will NOT be appended to <head>, because the default key is already used. -->
<meta name="description" content="CshtmlHead, Key: default, ContentOrder: default" />
<script>console.log("CshtmlHead, Key: default, ContentOrder: default");</script>
</CshtmlHead>
<CshtmlHead Key="0" ContentOrder="0">
<!-- This content will be appended to <head> and will appear before <CshtmlHead>, when this component is first instantiated. -->
<meta name="description" content="CshtmlHead, Key: default, ContentOrder: 0" />
<script>console.log("CshtmlHead, Key: default, ContentOrder: 0");</script>
</CshtmlHead>
<CshtmlHead Key="-1" ContentOrder="-1">
<!-- This content will be prepended to <head>, when this component is first instantiated. -->
<meta name="description" content="CshtmlHead, Key: -1, ContentOrder: -1" />
<script>console.log("CshtmlHead, Key: -1, ContentOrder: -1");</script>
</CshtmlHead>
<CshtmlHead Multiple="true" ContentOrder="-2">
<!-- This content will be prepended to <head> and will appear before <CshtmlHead ContentOrder="-1">, every time this component is instantiated. -->
<meta name="description" content="CshtmlHead, Multiple: true, ContentOrder: -2" />
<script>console.log("CshtmlHead, Multiple: true, ContentOrder: -2");</script>
</CshtmlHead>
<!-- Content that is injected in to the <body> tag. -->
<!-- ---------------------------------------------- -->
<!-- ---------------------------------------------- -->
<!-- ---------------------------------------------- -->
<CshtmlBody>
<!-- This content will be appended to <body>, when this component is first instantiated. -->
<meta name="description" content="CshtmlBody, Key: default, ContentOrder: default" />
<script>console.log("CshtmlBody, Key: default, ContentOrder: default");</script>
</CshtmlBody>
<CshtmlBody>
<!-- This content will NOT be appended to <body>, because the default key is already used. -->
<meta name="description" content="CshtmlBody, Key: default, ContentOrder: default" />
<script>console.log("CshtmlBody, Key: default, ContentOrder: default");</script>
</CshtmlBody>
<CshtmlBody Key="0" ContentOrder="0">
<!-- This content will be appended to <body> and will appear before <CshtmlBody>, when this component is first instantiated. -->
<meta name="description" content="CshtmlBody, Key: default, ContentOrder: 0" />
<script>console.log("CshtmlBody, Key: default, ContentOrder: 0");</script>
</CshtmlBody>
<CshtmlBody Key="-1" ContentOrder="-1">
<!-- This content will be prepended to <body>, when this component is first instantiated. -->
<meta name="description" content="CshtmlBody, Key: -1, ContentOrder: -1" />
<script>console.log("CshtmlBody, Key: -1, ContentOrder: -1");</script>
</CshtmlBody>
<CshtmlBody Multiple="true" ContentOrder="-2">
<!-- This content will be prepended to <body> and will appear before <CshtmlBody ContentOrder="-1">, every time this component is instantiated. -->
<meta name="description" content="CshtmlBody, Multiple: true, ContentOrder: -2" />
<script>console.log("CshtmlBody, Multiple: true, ContentOrder: -2");</script>
</CshtmlBody>
<!-- One off content that is rendered as is and wherever this component is instantiated. -->
<!-- ----------------------------------------------------------------------------------- -->
<!-- ----------------------------------------------------------------------------------- -->
<!-- ----------------------------------------------------------------------------------- -->
<CshtmlInitial>
<!-- This content will be rendered, as is, wherever this component is first instantiated. -->
<script>console.log("CshtmlInitial, Key: default");</script>
</CshtmlInitial>
<CshtmlInitial>
<!-- This content will NOT be rendered, because the default key is already used. -->
<script>console.log("CshtmlInitial, Key: default 2");</script>
</CshtmlInitial>
<CshtmlInitial Key="I1">
<!-- Explicitly keyed. This content will be rendered, as is, wherever this component is first instantiated. -->
<script>console.log("CshtmlInitial, Key: I1");</script>
@{
var a = 0;
}
</CshtmlInitial>
<!-- The actual content of this component. -->
<!-- ------------------------------------- -->
<!-- ------------------------------------- -->
<!-- ------------------------------------- -->
<div class="advanced-example-component" style="background-color: @Model.BackgroundColor; font-size: @Model.FontSize">
@{
// Assign a variable to a component reference.
// The Box component inherits CshtmlComponentReferenceableBase<Box> (a class that inherits CshtmlComponentBase),
// which implements this logic. Useful if component properties have to be used outside the component's own context.
// This is not the default behavior due to the mandatory, but slightly weird syntax:
// public Box : CshtmlComponentReferenceableBase<Box> ....
// See the source code of Pages/Components/Box.cshtml.cs for how the component class looks like.
var boxReference = new CshtmlComponentReference<Box>();
<Box Reference="boxReference">
<h1>
AdvancedExampleComponent: @Model.UppercaseTitle
</h1>
<p>
The fontsize of the referenced Box is: @boxReference.Component.FontSize
</p>
</Box>
}
<br />
<div class="advanced-example-component-slot0">
<!-- Render a named slot. -->
@Html.Raw(Model.NamedSlots["SlotName0"])
</div>
<br />
<div class="advanced-example-component-child-content">
<!-- Render the child content. -->
@Html.Raw(Model.ChildContent)
</div>
<br />
<div class="advanced-example-component-slot1">
<!-- Render a typed (or named) slot. -->
@Html.Raw(Model.NamedSlots[AdvancedExampleComponentTypedSlot.SlotName])
</div>
</div>
Component Instantiation
<AdvancedExampleComponent Title="Some title" font-size="1.2rem" background-color="rgba(0, 0, 255, 0.1)">
<div>
Some custom HTML content.
<Box>
Supports nested components.
</Box>
</div>
<CshtmlSlot Name="SlotName0">
Some custom HTML content within a named slot. The parent component decides where
this content is placed.
</CshtmlSlot>
<AdvancedExampleComponentTypedSlot>
Additional custom HTML content within a <strong>typed</strong> slot.
See the source code of <code>/Pages/Components/Example/AdvancedExampleComponentTypesSlot.cs</code>.
<Box>
Supports nested components.
</Box>
</AdvancedExampleComponentTypedSlot>
</AdvancedExampleComponent>
Core
This section documents the main built in classes and components of CshtmlComponent.
CshtmlComponentBase
CshtmlComponentBase
is the base class which all components should inherit.
The class inherits TagHelper
and all of its functionality.
See the documentation for TagHelper
here.
Properties
The properties of CshtmlComponentBase
(not including inherited).
ViewContext
Type: ViewContext
Default Value: [Auto Generated]
Description: The current ViewContext. Automatically set by ASP.NET Core.
PartialViewName
Type: string?
Default Value: null
Description: The path to the associated .cshtml file. The contents of this file will be rendered.
OutputTagName
Type: string?
Default Value: null
Description: Describes within which tag the component output should be wrapped. Default value null means that no extra wrapping component is created.
OutputTagMode
Type: TagMode
Default Value: TagMode.StartTagAndEndTag
Description: Describes how the resulting component should be closed. Mainly related to the optional OutputTagName.
ChildContent
Type: string
Default Value: [Auto Generated]
Description: The child content of the component.
NamedSlots
Type: Dictionary<string, string>
Default Value: [Empty Dictionary]
Description: The content of the optional named slots.
Constructors
The constructors of CshtmlComponentBase
, their arguments and the key properties that they set.
public CshtmlComponentBase(IHtmlHelper htmlHelper)
PartialViewName: The path to the associated .cshtml file is auto generated by analyzing the namespace and the class name of the component. For example, if the namespace is "SampleRazorPagesApplication.Pages.Components.Example" and the class name is "ExampleComponent", then PartialViewName will be "/Pages/Components/Example/ExampleComponent.cshtml".
OutputTagName: null
OutputTagMode: TagMode.StartTagAndEndTag
public CshtmlComponentBase(IHtmlHelper htmlHelper, string? partialViewName)
PartialViewName: partialViewName
OutputTagName: null
OutputTagMode: TagMode.StartTagAndEndTag
public CshtmlComponentBase(IHtmlHelper htmlHelper, string? partialViewName, string? outputTagName = null)
OutputTagName: outputTagName
OutputTagMode: TagMode.StartTagAndEndTag
public CshtmlComponentBase(IHtmlHIHtmlHelper htmlHelper, string? partialViewName, string? outputTagName = null, TagMode outputTagMode = TagMode.StartTagAndEndTag)
PartialViewName: partialViewName
OutputTagName: outputTagName
OutputTagMode: outputTagMode
CshtmlComponentReferenceableBase<ReferencableComponentType>
CshtmlComponentReferenceableBase
is the base class which components that
require reference capturing should inherit. This is not the default behavior due to the
slightly unorthodox inheritance syntax. For example, if you define the component "Box",
then in the class declaration you must use this syntax:
public class Box : CshtmlComponentReferenceableBase<Box>
The class inherits CshtmlComponentBase
and all of its functionality.
Properties
The properties of CshtmlComponentReferenceableBase<ReferencableComponentType>
(not including inherited).
Reference
Type: CshtmlComponentReferenceableBase<ReferencableComponentType>
Default Value: null
Description: A property that allows you to capture the reference to a component from a .cshtml file. Useful if, for example, one must access an id that another component defines. See the Advanced Example for how to accomplish this.
Constructors
The constructors of CshtmlComponentReferenceableBase<ReferencableComponentType>
are exactly the same as those of
CshtmlComponentBase
.
CshtmlHead
CshtmlHead
allows one to inject content in to the head
tag of a document. By default,
only one CshtmlHead
tag is supported per component, but this can be overridden with the use of attributes.
Note that instantiating multiple components of the same type will not render the content of a CshtmlHead
tag
multiple times, unless explicitly specified.
Attributes
The attributes of CshtmlHead
.
Key
Type: string
Default Value: ""
Description: Sets a key for the tag. Multiple CshtmlHead
tags with unique keys are supported per component.
Only the first CshtmlHead
tag with a unique key is rendered (within the context of a component).
Multiple
Type: bool
Default Value: false
Description: If true, then renders the content of a CshtmlHead
tag, even if the key is not unique. Additionally,
if true, then instantiating multiple components of the same type in a request will render the content of a CshtmlHead
tag multiple
times. This feature should almost never be used.
ContentOrder
Type: int
Default Value: int.MaxValue
Description: Sets the order of CshtmlHead
tags. The content of CshtmlHead
tags
with negative values are prepended to the document head
element, so that the most negative ones are rendered first.
The content of CshtmlHead
tags with positive values (including zero) are appended to the document head
element, so that the least positive ones are rendered first. The default behavior will append everything to the head
element. Useful if certain components require, for example, the JS of other components. One component should in most cases
use only one ContentOrder.
CshtmlHead Ordering Example
An example that shows how CshtmlHead
affects the content of the head
element.
<!DOCTYPE html>
<html>
<head>
<!--
<CshtmlHead ContentOrder="int.MinValue"> content here.
<CshtmlHead ContentOrder="int.MinValue + 1"> content here.
<CshtmlHead ContentOrder="int.MinValue + 2"> content here.
...
<CshtmlHead ContentOrder="-999"> content here.
<CshtmlHead ContentOrder="-998"> content here.
<CshtmlHead ContentOrder="-997"> content here.
...
<CshtmlHead ContentOrder="-3"> content here.
<CshtmlHead ContentOrder="-2"> content here.
<CshtmlHead ContentOrder="-1"> content here.
-->
<!--
Standard <head> content here. Often specified in a _layout.cshtml file.
-->
<!--
<CshtmlHead ContentOrder="0"> content here.
<CshtmlHead ContentOrder="1"> content here.
<CshtmlHead ContentOrder="2"> content here.
...
<CshtmlHead ContentOrder="997"> content here.
<CshtmlHead ContentOrder="998"> content here.
<CshtmlHead ContentOrder="999"> content here.
...
<CshtmlHead ContentOrder="int.MaxValue - 2"> content here.
<CshtmlHead ContentOrder="int.MaxValue - 1"> content here.
<CshtmlHead ContentOrder="int.MaxValue"> content here. Default location.
-->
</head>
<body>
<!-- Some content here. -->
</body>
</html>
CshtmlBody
CshtmlBody
allows one to inject content in to the body
tag of a document. By default,
only one CshtmlBody
tag is supported per component, but this can be overridden with the use of attributes.
Note that instantiating multiple components of the same type will not render the content of a CshtmlBody
tag
multiple times, unless explicitly specified.
Attributes
The attributes of CshtmlBody
.
Key
Type: string
Default Value: ""
Description: Sets a key for the tag. Multiple CshtmlBody
tags with unique keys are supported per component.
Only the first CshtmlBody
tag with a unique key is rendered (within the context of a component).
Multiple
Type: bool
Default Value: false
Description: If true, then renders the content of a CshtmlBody
tag, even if the key is not unique. Additionally,
if true, then instantiating multiple components of the same type in a request will render the content of a CshtmlBody
tag multiple
times. This feature should almost never be used.
ContentOrder
Type: int
Default Value: int.MaxValue
Description: Sets the order of CshtmlBody
tags. The content of CshtmlBody
tags
with negative values are prepended to the document body
element, so that the most negative ones are rendered first.
The content of CshtmlBody
tags with positive values (including zero) are appended to the document body
element, so that the least positive ones are rendered first. The default behavior will append everything to the body
element. Useful if certain components require, for example, the JS of other components. One component should in most cases
use only one ContentOrder.
CshtmlBody Ordering Example
An example that shows how CshtmlBody
affects the content of the head
element.
<!DOCTYPE html>
<html>
<head>
<!-- Some content here. -->
</head>
<body>
<!--
<CshtmlBody ContentOrder="int.MinValue"> content here.
<CshtmlBody ContentOrder="int.MinValue + 1"> content here.
<CshtmlBody ContentOrder="int.MinValue + 2"> content here.
...
<CshtmlBody ContentOrder="-999"> content here.
<CshtmlBody ContentOrder="-998"> content here.
<CshtmlBody ContentOrder="-997"> content here.
...
<CshtmlBody ContentOrder="-3"> content here.
<CshtmlBody ContentOrder="-2"> content here.
<CshtmlBody ContentOrder="-1"> content here.
-->
<!--
Standard <body> content here.
-->
<!--
<CshtmlBody ContentOrder="0"> content here.
<CshtmlBody ContentOrder="1"> content here.
<CshtmlBody ContentOrder="2"> content here.
...
<CshtmlBody ContentOrder="997"> content here.
<CshtmlBody ContentOrder="998"> content here.
<CshtmlBody ContentOrder="999"> content here.
...
<CshtmlBody ContentOrder="int.MaxValue - 2"> content here.
<CshtmlBody ContentOrder="int.MaxValue - 1"> content here.
<CshtmlBody ContentOrder="int.MaxValue"> content here. Default location.
-->
</body>
</html>
CshtmlInitial
CshtmlInitial
allows one to inject one-off content in to a component. By default,
only one CshtmlInitial
tag is supported per component, but this can be overridden with the use of attributes.
Note that instantiating multiple components of the same type will not render the content of a CshtmlInitial
tag
multiple times, unless explicitly specified.
Attributes
The attributes of CshtmlInitial
.
Key
Type: string
Default Value: ""
Description: Sets a key for the tag. Multiple CshtmlInitial
tags with unique keys are supported per component.
Only the first CshtmlInitial
tag with a unique key is rendered (within the context of a component).
Multiple
Type: bool
Default Value: false
Description: If true, then renders the content of a CshtmlInitial
tag, even if the key is not unique. Additionally,
if true, then instantiating multiple components of the same type in a request will render the content of a CshtmlInitial
tag multiple
times. This feature should almost never be used.
CshtmlSlot
CshtmlSlot
allows one to specify named slots. The parent component then
decides where this content is rendered. Can be inherited to create typed slots. Typed slots
do not require the specification of the Name attribute, which improves maintenance.
Attributes
The attributes of CshtmlSlot
.
Name
Type: string
Default Value: ""
Description: The name of the slot. The content of the slot is stored in a dictionary, where Name is the key. Components that implement slots, should then render this content in an appropriate place.
Usage
This section documents how CshtmlComponent should be installed and how to author custom components.
Installation & Initialization
- Install the Nuget package.
-
Dependency inject some classes in
Startup.cs
:public void ConfigureServices(IServiceCollection services)
{
// Some other configuration here.
services.AddScoped<ICshtmlComponentTracker, CshtmlComponentTracker>();
services.AddScoped<ITagHelperComponent, CshtmlComponentInjectionContentHandler>();
services.AddScoped<ICshtmlComponentInjectionContentStore, CshtmlComponentInjectionContentStore>();
// Some other configuration here.
} -
In an appropriate
_ViewImports.cshtml
(usually located directly under thePages
directory) add the following:// Built in CshtmlComponent components.
@addTagHelper *, Acmion.CshtmlComponent
// Components defined in your project.
@addTagHelper *, YourProjectName
Authoring Custom CshtmlComponents
-
Create the file
ExampleComponent.cshtml.cs
somewhere under your ASP.NET Core Project (usually under thePages
orView
directory, but anything should work). -
Create the file
ExampleComponent.cshtml
in the same location. -
In
ExampleComponent.cs
:-
Inherit
CshtmlComponentBase
. If you want to capture the reference to this component, then you should instead inheritCshtmlComponentReferenceableBase<ExampleComponent>
. No other changes necessary. -
Implement one of the constructor so that all arguments are dependency injected arguments. In most cases,
it is enough that
IHtmlHelper htmlHelper
is the only argument. -
Add
[HtmlTargetElement("ExampleComponentTag")]
to the component class, whereExampleComponentTag
is the tag that the component will be associated with. -
Optionally, specify how the tag should be closed in
HtmlTargetElement
, see more about how this is achieved in the TagHelper docs. -
List all component attributes as C# properties. ASP.NET Core will translate the properties to their kebabcased
variants, unless otherwise specified with
[HtmlAttributeName("AttributeName")]
, whereAttributeName
is the name of the attribute. -
Create fields or properties for all other values you wish to use in
ExampleComponent.cshtml
. -
Optionally, override
protected virtual Task ProcessComponent()
, which is called beforeExampleComponent.cshtml
is executed. Here you can extract information from properties and access other fields etc. This can not be done in the constructor, since any provided properties will not have been set yet. The nested child content can be accessed in this method by accessingChildContent
. The named slots can be accessed by accessingNamedSlots
-
Inherit
-
In
ExampleComponent.cshtml
:-
Set the model to
ExampleComponent
with@model ExampleComponent
. You may need to add appropriate using statements. -
Component properties and field are accessible with
Model.PropertyName
. -
The nested child content can be accessed with
Model.ChildContent
. To render the child content, you can useHtml.Raw(Model.ChildContent)
, but note that this does not encode the HTML, which means that XSS attacks are possible if non-validated user input is used as child content. -
Any named slots can be accessed with
Model.NamedSlots
. To render the slot content, you can useHtml.Raw(Model.NamedSlots["SlotName"])
, but note that this does not encode the HTML, which means that XSS attacks are possible if non-validated user input is used as child content. The named slots have to be defined, in the initializing context, with<CshtmlSlot Name="SlotName">[CONTENT HERE]<CshtmlSlot>
or by using typed slots. See the advanced example in Examples. -
Use
<CshtmlHead>[CONTENT HERE]<CshtmlHead>
,<CshtmlBody>[CONTENT HERE]<CshtmlBody>
and<CshtmlInitial>[CONTENT HERE]<CshtmlInitial>
to render or inject content according to the rules in Components. -
Render your markup and use
.cshtml
files as you wish.
-
Set the model to
Notes
- See the sample project in the CshtmlComponent GitHub repository for concrete examples.
- A CshtmlComponent is just a TagHelper with some "magic". In practice, everything that applies to ASP.NET Core TagHelpers apply to CshtmlComponents.
-
The entire CshtmlComponent is passed as Model to the
.cshtml
file. This includes properties inherited from TagHelper.
Changelog
V3.1.0
V3.1.0 DocumentationPublished: Oct 30, 2020
Added constructors to CshtmlComponentBase
and CshtmlComponentReferenceableBase
to make development more convenient.
The main change was that from now on the path to the associated .cshtml file can be automatically generated by analyzing the namespace and
class name. This improves code maintainability and reduces unnecessary bugs. The path to the associated .csthml file can still be defined manually.
V3.0.0
V3.0.0 DocumentationPublished: Oct 11, 2020
Breaking changes. Added support for head and body content injection with CshtmlHead
and CshtmlBody
.
Rewrote CshtmlInitial
to no longer depend on the obsolete Context
attribute. Upgrading from a
previous version should only minimally affect previous usage of CshtmlInitial
(might even in most cases work
without any changes). Additionally, slight changes in dependency injection (Startup.cs).
V2.2.0
V2.2.0 DocumentationPublished: Sep 26, 2020
Added support component reference capturing. This grants access to component properties outside the components own context. See the Razor code in Example for an example of how this is done. Note: This feature must be enabled at a component level.
V2.1.0
V2.1.0 DocumentationPublished: Sep 20, 2020
Added support for CshtmlInitial
, which allows component code be rendered once
per request, regardless of the number of instantiated components.
V2.0.0
V2.0.0 DocumentationPublished: Sep 19, 2020
Breaking changes. Added support for named slots and added tag helper context and output as arguments
to ProcessComponent
.
V1.1.0
V1.1.0 DocumentationPublished: Sep 17, 2020
Added support for TagMode
, which provides some extra customizability.
V1.0.0
V1.0.0 DocumentationPublished: Sep 15, 2020
The initial release.
Credits
CshtmlComponent was developed by Acmion (GitHub).
Contribute to CshtmlComponent in it's GitHub repository.