Implement basics of @onclick()-type shorthand syntax
This commit is contained in:
parent
946e25462e
commit
d1f96153d3
|
|
@ -31,6 +31,7 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
|
|||
private string _unconsumedHtml;
|
||||
private IList<object> _currentAttributeValues;
|
||||
private IDictionary<string, object> _currentElementAttributes = new Dictionary<string, object>();
|
||||
private IList<IntermediateToken> _currentElementAttributeTokens = new List<IntermediateToken>();
|
||||
|
||||
public override void BeginWriterScope(CodeRenderingContext context, string writer)
|
||||
{
|
||||
|
|
@ -91,8 +92,19 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
|
|||
|
||||
public override void WriteCSharpExpression(CodeRenderingContext context, CSharpExpressionIntermediateNode node)
|
||||
{
|
||||
// Render as text node. Later, we'll need to add different handling for expressions
|
||||
// that appear inside elements, e.g., <a href="@url">
|
||||
// To support syntax like <elem @completeAttributePair /> (which in turn supports syntax
|
||||
// like <elem @OnSomeEvent(Handler) />), check whether we are currently in the middle of
|
||||
// writing an element. If so, treat this C# expression as something that should evaluate
|
||||
// as a RenderTreeNode of type Attribute.
|
||||
if (_unconsumedHtml != null)
|
||||
{
|
||||
var token = (IntermediateToken)node.Children.Single();
|
||||
_currentElementAttributeTokens.Add(token);
|
||||
return;
|
||||
}
|
||||
|
||||
// Since we're not in the middle of writing an element, this must evaluate as some
|
||||
// text to display
|
||||
context.CodeWriter
|
||||
.WriteStartMethodInvocation($"{builderVarName}.{nameof(RenderTreeBuilder.AddText)}");
|
||||
|
||||
|
|
@ -206,6 +218,18 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
|
|||
_currentElementAttributes.Clear();
|
||||
}
|
||||
|
||||
if (_currentElementAttributeTokens.Count > 0)
|
||||
{
|
||||
foreach (var token in _currentElementAttributeTokens)
|
||||
{
|
||||
codeWriter
|
||||
.WriteStartMethodInvocation($"{builderVarName}.{nameof(RenderTreeBuilder.AddAttribute)}")
|
||||
.Write(token.Content)
|
||||
.WriteEndMethodInvocation();
|
||||
}
|
||||
_currentElementAttributeTokens.Clear();
|
||||
}
|
||||
|
||||
if (nextToken.Type == HtmlTokenType.EndTag
|
||||
|| nextTag.IsSelfClosing
|
||||
|| htmlVoidElementsLookup.Contains(nextTag.Data))
|
||||
|
|
|
|||
|
|
@ -34,5 +34,13 @@ namespace Microsoft.Blazor.Components
|
|||
/// <returns>Always throws an exception.</returns>
|
||||
public virtual Task ExecuteAsync()
|
||||
=> throw new NotImplementedException($"Blazor components do not implement {nameof(ExecuteAsync)}.");
|
||||
|
||||
/// <summary>
|
||||
/// Handles click events by invoking <paramref name="handler"/>.
|
||||
/// </summary>
|
||||
/// <param name="handler">The handler to be invoked when the event occurs.</param>
|
||||
/// <returns>A <see cref="RenderTreeNode"/> that represents the event handler.</returns>
|
||||
protected RenderTreeNode onclick(Action handler)
|
||||
=> RenderTreeNode.Attribute("onclick", _ => handler());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,23 @@ namespace Microsoft.Blazor.RenderTree
|
|||
Append(RenderTreeNode.Attribute(name, value.ToString()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a node representing an attribute.
|
||||
/// The attribute is associated with the most recently added element.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the attribute.</param>
|
||||
/// <param name="value">The value of the attribute.</param>
|
||||
public void AddAttribute(RenderTreeNode node)
|
||||
{
|
||||
if (node.NodeType != RenderTreeNodeType.Attribute)
|
||||
{
|
||||
throw new ArgumentException($"The {nameof(node.NodeType)} must be {RenderTreeNodeType.Attribute}.");
|
||||
}
|
||||
|
||||
AssertCanAddAttribute();
|
||||
Append(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a node representing a child component.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -303,6 +303,35 @@ namespace Microsoft.Blazor.Build.Test
|
|||
node => AssertNode.Text(node, typeof(List<string>).FullName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SupportsAttributeNodesEvaluatedInline()
|
||||
{
|
||||
// Arrange/Act
|
||||
var component = CompileToComponent(
|
||||
@"<elem @onclick(MyHandler) />
|
||||
@functions {
|
||||
public bool DidInvokeCode { get; set; } = false;
|
||||
void MyHandler()
|
||||
{
|
||||
DidInvokeCode = true;
|
||||
}
|
||||
}");
|
||||
var didInvokeCodeProperty = component.GetType().GetProperty("DidInvokeCode");
|
||||
|
||||
// Assert
|
||||
Assert.False((bool)didInvokeCodeProperty.GetValue(component));
|
||||
Assert.Collection(GetRenderTree(component).Where(NotWhitespace),
|
||||
node => AssertNode.Element(node, "elem", 1),
|
||||
node =>
|
||||
{
|
||||
Assert.Equal(RenderTreeNodeType.Attribute, node.NodeType);
|
||||
Assert.NotNull(node.AttributeEventHandlerValue);
|
||||
|
||||
node.AttributeEventHandlerValue(null);
|
||||
Assert.True((bool)didInvokeCodeProperty.GetValue(component));
|
||||
});
|
||||
}
|
||||
|
||||
private static bool NotWhitespace(RenderTreeNode node)
|
||||
=> node.NodeType != RenderTreeNodeType.Text
|
||||
|| !string.IsNullOrWhiteSpace(node.TextContent);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
<h1>Counter</h1>
|
||||
<p>Current count: @currentCount</p>
|
||||
<button onclick=@{ currentCount++; }>Click me</button>
|
||||
<button @onclick(IncrementCount)>Click me</button>
|
||||
|
||||
@functions {
|
||||
int currentCount = 0;
|
||||
|
||||
void IncrementCount()
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue