Add support general delegate types
This builds upon existing support for UIEventHandler-typed component properties and applies the same principle to any delegate type. We try to help by generating the LSH of the lambda `=>` allowing you to write `OnClick="Foo()"` rather than `OnClick="(e) => Foo()"`. You can of course use @ as an escape. The only rough edge here is that if the parameter names aren't memorable for the delgate type, it's not super helpful.
This commit is contained in:
parent
d1cfe0191a
commit
c05657c7f2
|
|
@ -423,7 +423,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
// We don't expect this to happen, we just want to know if it can.
|
// We don't expect this to happen, we just want to know if it can.
|
||||||
throw new InvalidOperationException("Attribute nodes should either be minimized or a single content node.");
|
throw new InvalidOperationException("Attribute nodes should either be minimized or a single content node.");
|
||||||
}
|
}
|
||||||
else if (node.BoundAttribute.IsUIEventHandlerProperty())
|
else if (node.BoundAttribute.IsDelegateProperty())
|
||||||
{
|
{
|
||||||
// See the runtime version of this code for a thorough description of what we're doing here
|
// See the runtime version of this code for a thorough description of what we're doing here
|
||||||
if ((cSharpNode = node.Children[0] as CSharpExpressionIntermediateNode) != null)
|
if ((cSharpNode = node.Children[0] as CSharpExpressionIntermediateNode) != null)
|
||||||
|
|
@ -445,7 +445,9 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
context.CodeWriter.Write(" = ");
|
context.CodeWriter.Write(" = ");
|
||||||
context.CodeWriter.Write("new ");
|
context.CodeWriter.Write("new ");
|
||||||
context.CodeWriter.Write(node.BoundAttribute.TypeName);
|
context.CodeWriter.Write(node.BoundAttribute.TypeName);
|
||||||
context.CodeWriter.Write("(e => ");
|
context.CodeWriter.Write("(");
|
||||||
|
context.CodeWriter.Write(node.BoundAttribute.GetDelegateSignature());
|
||||||
|
context.CodeWriter.Write(" => ");
|
||||||
WriteCSharpToken(context, ((IntermediateToken)node.Children[0]));
|
WriteCSharpToken(context, ((IntermediateToken)node.Children[0]));
|
||||||
context.CodeWriter.Write(");");
|
context.CodeWriter.Write(");");
|
||||||
context.CodeWriter.WriteLine();
|
context.CodeWriter.WriteLine();
|
||||||
|
|
|
||||||
|
|
@ -470,7 +470,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
// We don't expect this to happen, we just want to know if it can.
|
// We don't expect this to happen, we just want to know if it can.
|
||||||
throw new InvalidOperationException("Attribute nodes should either be minimized or a single content node.");
|
throw new InvalidOperationException("Attribute nodes should either be minimized or a single content node.");
|
||||||
}
|
}
|
||||||
else if (node.BoundAttribute.IsUIEventHandlerProperty())
|
else if (node.BoundAttribute.IsDelegateProperty())
|
||||||
{
|
{
|
||||||
// This is a UIEventHandler property. We do some special code generation for this
|
// This is a UIEventHandler property. We do some special code generation for this
|
||||||
// case so that it's easier to write for common cases.
|
// case so that it's easier to write for common cases.
|
||||||
|
|
@ -502,7 +502,8 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
context.CodeWriter.Write("new ");
|
context.CodeWriter.Write("new ");
|
||||||
context.CodeWriter.Write(node.BoundAttribute.TypeName);
|
context.CodeWriter.Write(node.BoundAttribute.TypeName);
|
||||||
context.CodeWriter.Write("(");
|
context.CodeWriter.Write("(");
|
||||||
context.CodeWriter.Write("e => ");
|
context.CodeWriter.Write(node.BoundAttribute.GetDelegateSignature());
|
||||||
|
context.CodeWriter.Write(" => ");
|
||||||
context.CodeWriter.Write(((IntermediateToken)node.Children[0]).Content);
|
context.CodeWriter.Write(((IntermediateToken)node.Children[0]).Content);
|
||||||
context.CodeWriter.Write(")");
|
context.CodeWriter.Write(")");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Razor;
|
using Microsoft.CodeAnalysis.Razor;
|
||||||
|
|
@ -11,7 +12,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
{
|
{
|
||||||
internal class ComponentTagHelperDescriptorProvider : RazorEngineFeatureBase, ITagHelperDescriptorProvider
|
internal class ComponentTagHelperDescriptorProvider : RazorEngineFeatureBase, ITagHelperDescriptorProvider
|
||||||
{
|
{
|
||||||
public static readonly string UIEventHandlerPropertyMetadata = "Blazor.IsUIEventHandler";
|
public static readonly string DelegateSignatureMetadata = "Blazor.DelegateSignature";
|
||||||
|
|
||||||
public readonly static string ComponentTagHelperKind = ComponentDocumentClassifierPass.ComponentDocumentKind;
|
public readonly static string ComponentTagHelperKind = ComponentDocumentClassifierPass.ComponentDocumentKind;
|
||||||
|
|
||||||
|
|
@ -113,7 +114,11 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
|
|
||||||
if (property.kind == PropertyKind.Delegate)
|
if (property.kind == PropertyKind.Delegate)
|
||||||
{
|
{
|
||||||
pb.Metadata.Add(UIEventHandlerPropertyMetadata, bool.TrueString);
|
var propertyType = (INamedTypeSymbol)property.property.Type;
|
||||||
|
var parameters = propertyType.DelegateInvokeMethod.Parameters;
|
||||||
|
|
||||||
|
var signature = "(" + string.Join(", ", parameters.Select(p => p.Name)) + ")";
|
||||||
|
pb.Metadata.Add(DelegateSignatureMetadata, signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
xml = property.property.GetDocumentationCommentXml();
|
xml = property.property.GetDocumentationCommentXml();
|
||||||
|
|
@ -182,12 +187,8 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
kind = PropertyKind.Enum;
|
kind = PropertyKind.Enum;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == PropertyKind.Default &&
|
if (kind == PropertyKind.Default && property.Type.TypeKind == TypeKind.Delegate)
|
||||||
property.Type.TypeKind == TypeKind.Delegate &&
|
|
||||||
property.Type.ToDisplayString(FullNameTypeDisplayFormat) == BlazorApi.UIEventHandler.FullTypeName)
|
|
||||||
{
|
{
|
||||||
// For delegate types we do some special code generation when the type
|
|
||||||
// UIEventHandler.
|
|
||||||
kind = PropertyKind.Delegate;
|
kind = PropertyKind.Delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,27 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
{
|
{
|
||||||
internal static class TagHelperBoundAttributeDescriptorExtensions
|
internal static class TagHelperBoundAttributeDescriptorExtensions
|
||||||
{
|
{
|
||||||
public static bool IsUIEventHandlerProperty(this BoundAttributeDescriptor attribute)
|
public static bool IsDelegateProperty(this BoundAttributeDescriptor attribute)
|
||||||
{
|
{
|
||||||
if (attribute == null)
|
if (attribute == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(attribute));
|
throw new ArgumentNullException(nameof(attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = ComponentTagHelperDescriptorProvider.UIEventHandlerPropertyMetadata;
|
var key = ComponentTagHelperDescriptorProvider.DelegateSignatureMetadata;
|
||||||
return
|
return attribute.Metadata.TryGetValue(key, out var value);
|
||||||
attribute.Metadata.TryGetValue(key, out var value) &&
|
}
|
||||||
string.Equals(value, bool.TrueString);
|
|
||||||
|
public static string GetDelegateSignature(this BoundAttributeDescriptor attribute)
|
||||||
|
{
|
||||||
|
if (attribute == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(attribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = ComponentTagHelperDescriptorProvider.DelegateSignatureMetadata;
|
||||||
|
attribute.Metadata.TryGetValue(key, out var value);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ global::System.Object __typeHelper = ""*, TestAssembly"";
|
||||||
{
|
{
|
||||||
base.BuildRenderTree(builder);
|
base.BuildRenderTree(builder);
|
||||||
|
|
||||||
__o = new Microsoft.AspNetCore.Blazor.UIEventHandler(e =>
|
__o = new Microsoft.AspNetCore.Blazor.UIEventHandler(eventArgs =>
|
||||||
#line 2 ""x:\dir\subdir\Test\TestComponent.cshtml""
|
#line 2 ""x:\dir\subdir\Test\TestComponent.cshtml""
|
||||||
Increment()
|
Increment()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ namespace Test
|
||||||
{
|
{
|
||||||
base.BuildRenderTree(builder);
|
base.BuildRenderTree(builder);
|
||||||
builder.OpenComponent<Test.MyComponent>(0);
|
builder.OpenComponent<Test.MyComponent>(0);
|
||||||
builder.AddAttribute(1, ""OnClick"", new Microsoft.AspNetCore.Blazor.UIEventHandler(e => Increment()));
|
builder.AddAttribute(1, ""OnClick"", new Microsoft.AspNetCore.Blazor.UIEventHandler(eventArgs => Increment()));
|
||||||
builder.CloseComponent();
|
builder.CloseComponent();
|
||||||
builder.AddContent(2, ""\n\n"");
|
builder.AddContent(2, ""\n\n"");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -282,8 +282,8 @@ namespace Test
|
||||||
Assert.False(attribute.IsStringProperty);
|
Assert.False(attribute.IsStringProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact] // UIEventHandler properties have some special intellisense behavior
|
[Fact]
|
||||||
public void Excecute_UIEventHandlerProperty_CreatesDescriptor()
|
public void Execute_DelegateProperty_CreatesDescriptor()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
||||||
|
|
@ -326,7 +326,7 @@ namespace Test
|
||||||
Assert.False(attribute.IsBooleanProperty);
|
Assert.False(attribute.IsBooleanProperty);
|
||||||
Assert.False(attribute.IsEnum);
|
Assert.False(attribute.IsEnum);
|
||||||
Assert.False(attribute.IsStringProperty);
|
Assert.False(attribute.IsStringProperty);
|
||||||
Assert.True(attribute.IsUIEventHandlerProperty());
|
Assert.True(attribute.IsDelegateProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// For simplicity in testing, exlude the built-in components. We'll add more and we
|
// For simplicity in testing, exlude the built-in components. We'll add more and we
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue