From f996a7763a5d09ed514164440eb5a786c24129f8 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 16 Mar 2021 05:10:57 -0500
Subject: [PATCH 1/9] Cascading generic types
---
.../blazor/components/templated-components.md | 95 ++++++++++++++++++-
1 file changed, 93 insertions(+), 2 deletions(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index 9a4fe620d3ae..e051ee3b6a4b 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -94,10 +94,101 @@ When using generic-typed components, the type parameter is inferred if possible.
::: moniker-end
-## Generic type constraints
+## Infer generic types based on ancestor components
+
+::: moniker range=">= aspnetcore-5.0"
+
+Ancestor components must opt in to this behavior. An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name.
+
+For example, define `Grid` and `Column` components.
+
+`Shared/Grid.razor`:
+
+```razor
+@typeparam TItem
+@attribute [CascadingTypeParameter(nameof(TItem))]
+
+
+
+
+ @ChildContent
+
+
+
+ @foreach (var item in Items)
+ {
+
+ | @item.Name |
+ @item.Quantity |
+
+ }
+
+
+
+@code {
+ [Parameter]
+ public IEnumerable Items { get; set; }
+
+ [Parameter]
+ public RenderFragment ChildContent { get; set; }
+}
+```
+
+`Shared/Column.razor`:
+
+```razor
+@typeparam TItem
+
+@Title |
+
+@code {
+ [Parameter]
+ public string Title { get; set; }
+}
+```
+
+Use the `Grid` and `Column` components.
+
+`Pages/GenericCascadedType.razor`:
+
+```razor
+@page "/generic-cascaded-type"
+
+
+
+
+
+
+@code {
+ private IEnumerable GetSaleRecords()
+ {
+ return new List()
+ {
+ new SaleRecord() { Name = "Product 1", Quantity = 100 },
+ new SaleRecord() { Name = "Product 2", Quantity = 200 },
+ new SaleRecord() { Name = "Product 3", Quantity = 50 },
+ };
+ }
+
+ private class SaleRecord
+ {
+ public string Name { get; set; }
+ public int Quantity { get; set; }
+ }
+}
+```
> [!NOTE]
-> Generic type constraints will be supported in a future release. For more information, see [Allow generic type constraints (dotnet/aspnetcore #8433)](https://github.com/dotnet/aspnetcore/issues/8433).
+> The Razor support in Visual Studio Code has not yet been updated to support this feature, so you may get incorrect errors even though the project correctly builds. This will be addressed in an upcoming tooling release.
+
+::: moniker-end
+
+::: moniker range="< aspnetcore-5.0"
+
+> [!NOTE]
+> Generic type constraints are supported in ASP.NET Core 6.0. For more information, see [the 6.0 version of this article](?view=aspnetcore-6.0).
+
+::: moniker-end
## Additional resources
From bb8979ac093b37a8ec2b2a807cc6b36fa1ce20cb Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Tue, 16 Mar 2021 07:32:46 -0500
Subject: [PATCH 2/9] Updates
---
.../blazor/components/templated-components.md | 72 ++++---------------
1 file changed, 13 insertions(+), 59 deletions(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index e051ee3b6a4b..a398627c9d7c 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -98,53 +98,20 @@ When using generic-typed components, the type parameter is inferred if possible.
::: moniker range=">= aspnetcore-5.0"
-Ancestor components must opt in to this behavior. An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name.
+Ancestor components must opt in to this behavior. An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name. The following example cascades a generic type parameter named `TItem`.
For example, define `Grid` and `Column` components.
`Shared/Grid.razor`:
```razor
-@typeparam TItem
-@attribute [CascadingTypeParameter(nameof(TItem))]
-
-
-
-
- @ChildContent
-
-
-
- @foreach (var item in Items)
- {
-
- | @item.Name |
- @item.Quantity |
-
- }
-
-
-
-@code {
- [Parameter]
- public IEnumerable Items { get; set; }
-
- [Parameter]
- public RenderFragment ChildContent { get; set; }
-}
+
```
`Shared/Column.razor`:
```razor
-@typeparam TItem
-
-@Title |
-@code {
- [Parameter]
- public string Title { get; set; }
-}
```
Use the `Grid` and `Column` components.
@@ -152,35 +119,22 @@ Use the `Grid` and `Column` components.
`Pages/GenericCascadedType.razor`:
```razor
-@page "/generic-cascaded-type"
-
-
-
-
-
-
-@code {
- private IEnumerable GetSaleRecords()
- {
- return new List()
- {
- new SaleRecord() { Name = "Product 1", Quantity = 100 },
- new SaleRecord() { Name = "Product 2", Quantity = 200 },
- new SaleRecord() { Name = "Product 3", Quantity = 50 },
- };
- }
-
- private class SaleRecord
- {
- public string Name { get; set; }
- public int Quantity { get; set; }
- }
-}
+
```
> [!NOTE]
> The Razor support in Visual Studio Code has not yet been updated to support this feature, so you may get incorrect errors even though the project correctly builds. This will be addressed in an upcoming tooling release.
+By adding `@attribute [CascadingTypeParameter(...)]` to a component, the specified generic type argument is automatically used by descendants that:
+
+* Are nested as child content for the component in the same `.razor` document.
+* Also declare a `@typeparam` with the exact same name.
+* Don't have another value supplied or inferred for the type parameter. If they do, the other value takes precedence over the cascaded generic type.
+
+When receiving a cascaded type parameter, components obtain it from the closest ancestor that has a `CascadingTypeParameter` with a matching name. The cascaded generic types parameters are overridden within a particular subtree.
+
+Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example `T`. If a developer opts into cascading a type parameter, they are implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components.
+
::: moniker-end
::: moniker range="< aspnetcore-5.0"
From d4bb54ed329eaffbf80c426bccad26ce064b4cb2 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Mon, 22 Mar 2021 06:25:56 -0500
Subject: [PATCH 3/9] Update templated-components.md
---
.../blazor/components/templated-components.md | 25 ++++++-------------
1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index a398627c9d7c..96c14547c46e 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -100,26 +100,15 @@ When using generic-typed components, the type parameter is inferred if possible.
Ancestor components must opt in to this behavior. An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name. The following example cascades a generic type parameter named `TItem`.
-For example, define `Grid` and `Column` components.
-
-`Shared/Grid.razor`:
+For example, the following
```razor
-
-```
-
-`Shared/Column.razor`:
-
-```razor
-
-```
-
-Use the `Grid` and `Column` components.
-
-`Pages/GenericCascadedType.razor`:
-
-```razor
-
+
+
+
+
+
+
```
> [!NOTE]
From f915055ee915525e538d1a4011b7690693323fda Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Mon, 22 Mar 2021 08:07:33 -0500
Subject: [PATCH 4/9] Blazor generic type cascading feature
---
.../blazor/components/templated-components.md | 53 ++++++++++++++++---
1 file changed, 47 insertions(+), 6 deletions(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index 96c14547c46e..324b1ac269f9 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -98,31 +98,72 @@ When using generic-typed components, the type parameter is inferred if possible.
::: moniker range=">= aspnetcore-5.0"
-Ancestor components must opt in to this behavior. An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name. The following example cascades a generic type parameter named `TItem`.
+An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name.
-For example, the following
+For example, the following `Chart` component receives stock price data and cascades a generic type parameter named `TLineData` to its descendent components:
+
+`Shared/Chart.razor`:
+
+```razor
+@typeparam TLineData
+@attribute [CascadingTypeParameter(nameof(TLineData))]
+
+...
+
+@code {
+ [Parameter]
+ public IEnumerable Data { get; set; }
+
+ [Parameter]
+ public RenderFragment ChildContent { get; set; }
+}
+```
+
+`Shared/Line.razor`:
+
+```razor
+@typeparam TLineData
+
+...
+
+@code {
+ [Parameter]
+ public string Title { get; set; }
+
+ [Parameter]
+ public decimal Value { get; set; }
+}
+```
+
+When the `Chart` component is used, `TLineData` isn't specified for each `Line` component of the chart.
+
+`Pages/StockPriceHistory.razor`:
```razor
+...
+
+
+...
```
> [!NOTE]
-> The Razor support in Visual Studio Code has not yet been updated to support this feature, so you may get incorrect errors even though the project correctly builds. This will be addressed in an upcoming tooling release.
+> The Razor support in Visual Studio Code hasn't been updated to support this feature, so you may receive incorrect errors even though the project correctly builds. This will be addressed in an upcoming tooling release.
By adding `@attribute [CascadingTypeParameter(...)]` to a component, the specified generic type argument is automatically used by descendants that:
* Are nested as child content for the component in the same `.razor` document.
-* Also declare a `@typeparam` with the exact same name.
-* Don't have another value supplied or inferred for the type parameter. If they do, the other value takes precedence over the cascaded generic type.
+* Also declare a [`@typeparam`](xref:mvc/views/razor#typeparam) with the exact same name.
+* Don't have another value supplied or inferred for the type parameter. If another value is supplied or inferred, it takes precedence over the cascaded generic type.
When receiving a cascaded type parameter, components obtain it from the closest ancestor that has a `CascadingTypeParameter` with a matching name. The cascaded generic types parameters are overridden within a particular subtree.
-Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example `T`. If a developer opts into cascading a type parameter, they are implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components.
+Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example `T` or `TItem`. If a developer opts into cascading a type parameter, they're implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components.
::: moniker-end
From a1d5732d93f27d75e2dd8a2cf45cfeb60833ee93 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Mon, 22 Mar 2021 08:09:41 -0500
Subject: [PATCH 5/9] Updates
---
aspnetcore/blazor/components/templated-components.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index 324b1ac269f9..fa9b8d5be70c 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -96,7 +96,7 @@ When using generic-typed components, the type parameter is inferred if possible.
## Infer generic types based on ancestor components
-::: moniker range=">= aspnetcore-5.0"
+::: moniker range=">= aspnetcore-6.0"
An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name.
@@ -167,7 +167,7 @@ Matching is only performed by name. Therefore, we recommend avoiding a cascaded
::: moniker-end
-::: moniker range="< aspnetcore-5.0"
+::: moniker range="< aspnetcore-6.0"
> [!NOTE]
> Generic type constraints are supported in ASP.NET Core 6.0. For more information, see [the 6.0 version of this article](?view=aspnetcore-6.0).
From 0355830ea25856ded294a52b301b1554fb5373f0 Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Mon, 22 Mar 2021 08:13:16 -0500
Subject: [PATCH 6/9] Updates
---
aspnetcore/blazor/components/templated-components.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index fa9b8d5be70c..e25258e8f613 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -170,7 +170,7 @@ Matching is only performed by name. Therefore, we recommend avoiding a cascaded
::: moniker range="< aspnetcore-6.0"
> [!NOTE]
-> Generic type constraints are supported in ASP.NET Core 6.0. For more information, see [the 6.0 version of this article](?view=aspnetcore-6.0).
+> Generic type constraints are supported in ASP.NET Core 6.0. For more information, see [the 6.0 version of this article](?preserve-view=true&view=aspnetcore-6.0).
::: moniker-end
From 3843ecaf7596427c4c554f46418b55dc171aad1a Mon Sep 17 00:00:00 2001
From: guardrex <1622880+guardrex@users.noreply.github.com>
Date: Mon, 22 Mar 2021 08:20:24 -0500
Subject: [PATCH 7/9] Updates
---
aspnetcore/blazor/components/templated-components.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index e25258e8f613..a9e2ea432d30 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -132,6 +132,9 @@ For example, the following `Chart` component receives stock price data and casca
[Parameter]
public decimal Value { get; set; }
+
+ [Parameter]
+ public IEnumerable Data { get; set; }
}
```
From aa910def190f1232d00293c37447c5cf7d0771df Mon Sep 17 00:00:00 2001
From: Luke Latham <1622880+guardrex@users.noreply.github.com>
Date: Tue, 23 Mar 2021 06:18:30 -0500
Subject: [PATCH 8/9] Updates
---
aspnetcore/blazor/components/templated-components.md | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index a9e2ea432d30..4b729ded28be 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -100,7 +100,7 @@ When using generic-typed components, the type parameter is inferred if possible.
An ancestor component can cascade a type parameter by name to descendants using the `CascadingTypeParameter` attribute. This attribute allows a generic type inference to use the specified type parameter automatically with descendants that have a type parameter with the same name.
-For example, the following `Chart` component receives stock price data and cascades a generic type parameter named `TLineData` to its descendent components:
+For example, the following `Chart` component receives stock price data and cascades a generic type parameter named `TLineData` to its descendent components.
`Shared/Chart.razor`:
@@ -143,7 +143,7 @@ When the `Chart` component is used, `TLineData` isn't specified for each `Line`
`Pages/StockPriceHistory.razor`:
```razor
-...
+@page "/stock-price-history"
@@ -151,8 +151,6 @@ When the `Chart` component is used, `TLineData` isn't specified for each `Line`
-
-...
```
> [!NOTE]
@@ -164,7 +162,7 @@ By adding `@attribute [CascadingTypeParameter(...)]` to a component, the specifi
* Also declare a [`@typeparam`](xref:mvc/views/razor#typeparam) with the exact same name.
* Don't have another value supplied or inferred for the type parameter. If another value is supplied or inferred, it takes precedence over the cascaded generic type.
-When receiving a cascaded type parameter, components obtain it from the closest ancestor that has a `CascadingTypeParameter` with a matching name. The cascaded generic types parameters are overridden within a particular subtree.
+When receiving a cascaded type parameter, components obtain the parameter value from the closest ancestor that has a `CascadingTypeParameter` with a matching name. Cascaded generic type parameters are overridden within a particular subtree.
Matching is only performed by name. Therefore, we recommend avoiding a cascaded generic type parameter with a generic name, for example `T` or `TItem`. If a developer opts into cascading a type parameter, they're implicitly promising that its name is unique enough not to clash with other cascaded type parameters from unrelated components.
From 7fb6228ecfbb5a350dfda86383a6a3bf8a7b2a75 Mon Sep 17 00:00:00 2001
From: Luke Latham <1622880+guardrex@users.noreply.github.com>
Date: Thu, 25 Mar 2021 07:41:22 -0500
Subject: [PATCH 9/9] Updates
---
aspnetcore/blazor/components/templated-components.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/aspnetcore/blazor/components/templated-components.md b/aspnetcore/blazor/components/templated-components.md
index 4b729ded28be..91caa847c2d0 100644
--- a/aspnetcore/blazor/components/templated-components.md
+++ b/aspnetcore/blazor/components/templated-components.md
@@ -171,7 +171,7 @@ Matching is only performed by name. Therefore, we recommend avoiding a cascaded
::: moniker range="< aspnetcore-6.0"
> [!NOTE]
-> Generic type constraints are supported in ASP.NET Core 6.0. For more information, see [the 6.0 version of this article](?preserve-view=true&view=aspnetcore-6.0).
+> Inferred generic types are supported in ASP.NET Core 6.0 or later. For more information, see a version of this article later than ASP.NET Core 5.0.
::: moniker-end