This repository has been archived by the owner on Feb 25, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 647
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
This change removes support for the old syntax used for event handlers and two-way binding. See the relevant issues for details on the new features and improvements: bind https://github.com/aspnet/Blazor/issues/409 event handlers https://github.com/aspnet/Blazor/issues/503 Along with this change we've removed a few additional things Blazor could do that aren't part of Razor's usual syntax. ---- The features that was used to make something like: ``` <button @OnClick(...) /> ``` is an expression that's embedded in a an element's attribute. This feature might be useful in the future if we want to support 'splatting' arbitrary attributes into a tag, but the runtime support for this isn't accessible outside the Blazor core. ---- The features that implement: ``` <button onclick=@{ } /> ``` have been removed in favor of a better design for lambdas, method group conversions and other things for event handler attributes. use `<button onclick=@(x => ...} />` instead. We think is a better approach in general, because we want the app developer to write and see the parameter list. ---- Both syntactic features that have been removed have dedicated error messages in the compiler. If you're porting old code it should help you figure out what to do.
- Loading branch information
Showing
15 changed files
with
162 additions
and
222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,14 +5,12 @@ | |
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
using AngleSharp; | ||
using AngleSharp.Html; | ||
using AngleSharp.Parser.Html; | ||
using Microsoft.AspNetCore.Razor.Language; | ||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration; | ||
using Microsoft.AspNetCore.Razor.Language.Intermediate; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
|
||
namespace Microsoft.AspNetCore.Blazor.Razor | ||
{ | ||
|
@@ -27,27 +25,18 @@ private readonly static HashSet<string> htmlVoidElementsLookup | |
= new HashSet<string>( | ||
new[] { "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr" }, | ||
StringComparer.OrdinalIgnoreCase); | ||
private readonly static Regex bindExpressionRegex = new Regex(@"^bind\((.+)\)$"); | ||
private readonly static CSharpParseOptions bindArgsParseOptions | ||
= CSharpParseOptions.Default.WithKind(CodeAnalysis.SourceCodeKind.Script); | ||
|
||
private readonly ScopeStack _scopeStack = new ScopeStack(); | ||
private string _unconsumedHtml; | ||
private List<IntermediateToken> _currentAttributeValues; | ||
private IDictionary<string, PendingAttribute> _currentElementAttributes = new Dictionary<string, PendingAttribute>(); | ||
private IList<PendingAttributeToken> _currentElementAttributeTokens = new List<PendingAttributeToken>(); | ||
private int _sourceSequence = 0; | ||
|
||
private struct PendingAttribute | ||
{ | ||
public List<IntermediateToken> Values { get; set; } | ||
} | ||
|
||
private struct PendingAttributeToken | ||
{ | ||
public IntermediateToken AttributeValue; | ||
} | ||
|
||
public override void WriteCSharpCode(CodeRenderingContext context, CSharpCodeIntermediateNode node) | ||
{ | ||
if (context == null) | ||
|
@@ -118,27 +107,26 @@ public override void WriteCSharpCodeAttributeValue(CodeRenderingContext context, | |
throw new InvalidOperationException($"Invoked {nameof(WriteCSharpCodeAttributeValue)} while {nameof(_currentAttributeValues)} was null."); | ||
} | ||
|
||
// For attributes like "onsomeevent=@{ /* some C# code */ }", we treat it as if you | ||
// wrote "onsomeevent=@(_ => { /* some C# code */ })" because then it works as an | ||
// event handler and is a reasonable syntax for that. | ||
var innerCSharp = (IntermediateToken)node.Children.Single(); | ||
innerCSharp.Content = $"_ => {{ {innerCSharp.Content} }}"; | ||
_currentAttributeValues.Add(innerCSharp); | ||
// We used to support syntaxes like <elem onsomeevent=@{ /* some C# code */ } /> but this is no longer the | ||
// case. | ||
// | ||
// We provide an error for this case just to be friendly. | ||
var content = string.Join("", node.Children.OfType<IntermediateToken>().Select(t => t.Content)); | ||
context.Diagnostics.Add(BlazorDiagnosticFactory.Create_CodeBlockInAttribute(node.Source, content)); | ||
return; | ||
} | ||
|
||
public override void WriteCSharpExpression(CodeRenderingContext context, CSharpExpressionIntermediateNode node) | ||
{ | ||
// 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 RenderTreeFrame of type Attribute. | ||
// We used to support syntaxes like <elem @completeAttributePair /> but this is no longer the case. | ||
// The APIs that a user would need to do this correctly aren't accessible outside of Blazor's core | ||
// anyway. | ||
// | ||
// We provide an error for this case just to be friendly. | ||
if (_unconsumedHtml != null) | ||
{ | ||
var token = (IntermediateToken)node.Children.Single(); | ||
_currentElementAttributeTokens.Add(new PendingAttributeToken | ||
{ | ||
AttributeValue = token | ||
}); | ||
var content = string.Join("", node.Children.OfType<IntermediateToken>().Select(t => t.Content)); | ||
context.Diagnostics.Add(BlazorDiagnosticFactory.Create_ExpressionInAttributeList(node.Source, content)); | ||
return; | ||
} | ||
|
||
|
@@ -279,15 +267,6 @@ public override void WriteHtmlContent(CodeRenderingContext context, HtmlContentI | |
_currentElementAttributes.Clear(); | ||
} | ||
|
||
if (_currentElementAttributeTokens.Count > 0) | ||
{ | ||
foreach (var token in _currentElementAttributeTokens) | ||
{ | ||
WriteElementAttributeToken(context, nextTag, token); | ||
} | ||
_currentElementAttributeTokens.Clear(); | ||
} | ||
|
||
_scopeStack.OpenScope( tagName: nextTag.Data, isComponent: false); | ||
} | ||
|
||
|
@@ -325,56 +304,6 @@ public override void WriteHtmlContent(CodeRenderingContext context, HtmlContentI | |
} | ||
} | ||
|
||
private void WriteElementAttributeToken(CodeRenderingContext context, HtmlTagToken tag, PendingAttributeToken token) | ||
{ | ||
var bindMatch = bindExpressionRegex.Match(token.AttributeValue.Content); | ||
if (bindMatch.Success) | ||
{ | ||
// TODO: Consider alternatives to the @bind syntax. The following is very strange. | ||
|
||
// The @bind(X, Y, Z, ...) syntax is special. We convert it to a pair of attributes: | ||
// [1] [email protected](X, Y, Z, ...) | ||
var valueParams = bindMatch.Groups[1].Value; | ||
WriteAttribute(context.CodeWriter, "value", new[] | ||
{ | ||
new IntermediateToken | ||
{ | ||
Kind = TokenKind.CSharp, | ||
Content = $"{BlazorApi.BindMethods.GetValue}({valueParams})" | ||
} | ||
}); | ||
|
||
// [2] @onchange(BindSetValue(parsed => { X = parsed; }, X, Y, Z, ...)) | ||
var parsedArgs = CSharpSyntaxTree.ParseText(valueParams, bindArgsParseOptions); | ||
var parsedArgsSplit = parsedArgs.GetRoot().ChildNodes().Select(x => x.ToString()).ToList(); | ||
if (parsedArgsSplit.Count > 0) | ||
{ | ||
parsedArgsSplit.Insert(0, $"_parsedValue_ => {{ {parsedArgsSplit[0]} = _parsedValue_; }}"); | ||
} | ||
var parsedArgsJoined = string.Join(", ", parsedArgsSplit); | ||
var onChangeAttributeToken = new PendingAttributeToken | ||
{ | ||
AttributeValue = new IntermediateToken | ||
{ | ||
Kind = TokenKind.CSharp, | ||
Content = $"onchange({BlazorApi.BindMethods.SetValue}({parsedArgsJoined}))" | ||
} | ||
}; | ||
WriteElementAttributeToken(context, tag, onChangeAttributeToken); | ||
} | ||
else | ||
{ | ||
// For any other attribute token (e.g., @onclick(...)), treat it as an expression | ||
// that will evaluate as an attribute frame | ||
context.CodeWriter | ||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.AddAttribute)}") | ||
.Write((_sourceSequence++).ToString()) | ||
.WriteParameterSeparator() | ||
.Write(token.AttributeValue.Content) | ||
.WriteEndMethodInvocation(); | ||
} | ||
} | ||
|
||
public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node) | ||
{ | ||
context.CodeWriter.WriteUsing(node.Content, endLine: true); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.