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:
Ryan Nowak 2018-03-08 23:24:18 -08:00 committed by Steve Sanderson
parent d1cfe0191a
commit c05657c7f2
7 changed files with 35 additions and 21 deletions

View File

@ -423,7 +423,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// 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.");
}
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
if ((cSharpNode = node.Children[0] as CSharpExpressionIntermediateNode) != null)
@ -445,7 +445,9 @@ namespace Microsoft.AspNetCore.Blazor.Razor
context.CodeWriter.Write(" = ");
context.CodeWriter.Write("new ");
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]));
context.CodeWriter.Write(");");
context.CodeWriter.WriteLine();

View File

@ -470,7 +470,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// 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.");
}
else if (node.BoundAttribute.IsUIEventHandlerProperty())
else if (node.BoundAttribute.IsDelegateProperty())
{
// This is a UIEventHandler property. We do some special code generation for this
// 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(node.BoundAttribute.TypeName);
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(")");
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor;
@ -11,7 +12,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
{
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;
@ -113,7 +114,11 @@ namespace Microsoft.AspNetCore.Blazor.Razor
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();
@ -182,12 +187,8 @@ namespace Microsoft.AspNetCore.Blazor.Razor
kind = PropertyKind.Enum;
}
if (kind == PropertyKind.Default &&
property.Type.TypeKind == TypeKind.Delegate &&
property.Type.ToDisplayString(FullNameTypeDisplayFormat) == BlazorApi.UIEventHandler.FullTypeName)
if (kind == PropertyKind.Default && property.Type.TypeKind == TypeKind.Delegate)
{
// For delegate types we do some special code generation when the type
// UIEventHandler.
kind = PropertyKind.Delegate;
}

View File

@ -8,17 +8,27 @@ namespace Microsoft.AspNetCore.Blazor.Razor
{
internal static class TagHelperBoundAttributeDescriptorExtensions
{
public static bool IsUIEventHandlerProperty(this BoundAttributeDescriptor attribute)
public static bool IsDelegateProperty(this BoundAttributeDescriptor attribute)
{
if (attribute == null)
{
throw new ArgumentNullException(nameof(attribute));
}
var key = ComponentTagHelperDescriptorProvider.UIEventHandlerPropertyMetadata;
return
attribute.Metadata.TryGetValue(key, out var value) &&
string.Equals(value, bool.TrueString);
var key = ComponentTagHelperDescriptorProvider.DelegateSignatureMetadata;
return attribute.Metadata.TryGetValue(key, out var value);
}
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;
}
}
}

View File

@ -239,7 +239,7 @@ global::System.Object __typeHelper = ""*, TestAssembly"";
{
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""
Increment()

View File

@ -225,7 +225,7 @@ namespace Test
{
base.BuildRenderTree(builder);
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.AddContent(2, ""\n\n"");
}

View File

@ -282,8 +282,8 @@ namespace Test
Assert.False(attribute.IsStringProperty);
}
[Fact] // UIEventHandler properties have some special intellisense behavior
public void Excecute_UIEventHandlerProperty_CreatesDescriptor()
[Fact]
public void Execute_DelegateProperty_CreatesDescriptor()
{
// Arrange
@ -326,7 +326,7 @@ namespace Test
Assert.False(attribute.IsBooleanProperty);
Assert.False(attribute.IsEnum);
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