Directive attributes part 1: Support parameters in bound attributes (dotnet/aspnetcore-tooling#597)

* Directive attributes part 1: Support parameters in bound attributes

* update

* Fix test

* feedback

* Updated design

* Do case sensitive comparison

* more

* Bug fix
\n\nCommit migrated from 9bbf240948
This commit is contained in:
Ajay Bhargav Baaskaran 2019-05-21 13:07:23 -07:00 committed by GitHub
parent 13397dd4d6
commit 5bad5de7ee
60 changed files with 1089 additions and 262 deletions

View File

@ -47,6 +47,8 @@ namespace Microsoft.AspNetCore.Razor.Language
public IReadOnlyDictionary<string, string> Metadata { get; protected set; }
public virtual IReadOnlyList<BoundAttributeParameterDescriptor> BoundAttributeParameters { get; protected set; }
public bool HasErrors
{
get

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language
@ -26,5 +27,11 @@ namespace Microsoft.AspNetCore.Razor.Language
public abstract IDictionary<string, string> Metadata { get; }
public abstract RazorDiagnosticCollection Diagnostics { get; }
public virtual IReadOnlyList<BoundAttributeParameterDescriptorBuilder> BoundAttributeParameters { get; }
public virtual void BindAttributeParameter(Action<BoundAttributeParameterDescriptorBuilder> configure)
{
}
}
}

View File

@ -29,9 +29,9 @@ namespace Microsoft.AspNetCore.Razor.Language
throw new ArgumentNullException(nameof(builder));
}
if (builder.Metadata.ContainsKey(TagHelperMetadata.Common.PropertyName))
if (builder.Metadata.TryGetValue(TagHelperMetadata.Common.PropertyName, out var value))
{
return builder.Metadata[TagHelperMetadata.Common.PropertyName];
return value;
}
return null;
@ -51,5 +51,35 @@ namespace Microsoft.AspNetCore.Razor.Language
builder.IndexerAttributeNamePrefix = attributeNamePrefix;
builder.IndexerValueTypeName = valueTypeName;
}
public static void SetPropertyName(this BoundAttributeParameterDescriptorBuilder builder, string propertyName)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (propertyName == null)
{
throw new ArgumentNullException(nameof(propertyName));
}
builder.Metadata[TagHelperMetadata.Common.PropertyName] = propertyName;
}
public static string GetPropertyName(this BoundAttributeParameterDescriptorBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (builder.Metadata.TryGetValue(TagHelperMetadata.Common.PropertyName, out var value))
{
return value;
}
return null;
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language
{
@ -49,5 +50,26 @@ namespace Microsoft.AspNetCore.Razor.Language
var isIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, attribute);
return isIndexerNameMatch && attribute.IsIndexerBooleanProperty;
}
public static bool IsDefaultKind(this BoundAttributeParameterDescriptor parameter)
{
if (parameter == null)
{
throw new ArgumentNullException(nameof(parameter));
}
return string.Equals(parameter.Kind, TagHelperConventions.DefaultKind, StringComparison.Ordinal);
}
public static string GetPropertyName(this BoundAttributeParameterDescriptor parameter)
{
if (parameter == null)
{
throw new ArgumentNullException(nameof(parameter));
}
parameter.Metadata.TryGetValue(TagHelperMetadata.Common.PropertyName, out var propertyName);
return propertyName;
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language
{
public abstract class BoundAttributeParameterDescriptor : IEquatable<BoundAttributeParameterDescriptor>
{
protected BoundAttributeParameterDescriptor(string kind)
{
Kind = kind;
}
public string Kind { get; }
public bool IsEnum { get; protected set; }
public bool IsStringProperty { get; protected set; }
public bool IsBooleanProperty { get; protected set; }
public string Name { get; protected set; }
public string TypeName { get; protected set; }
public string Documentation { get; protected set; }
public string DisplayName { get; protected set; }
public IReadOnlyList<RazorDiagnostic> Diagnostics { get; protected set; }
public IReadOnlyDictionary<string, string> Metadata { get; protected set; }
public bool HasErrors
{
get
{
var errors = Diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
return errors;
}
}
public override string ToString()
{
return DisplayName ?? base.ToString();
}
public bool Equals(BoundAttributeParameterDescriptor other)
{
return BoundAttributeParameterDescriptorComparer.Default.Equals(this, other);
}
public override bool Equals(object obj)
{
return Equals(obj as BoundAttributeParameterDescriptor);
}
public override int GetHashCode()
{
return BoundAttributeParameterDescriptorComparer.Default.GetHashCode(this);
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language
{
public abstract class BoundAttributeParameterDescriptorBuilder
{
public abstract string Name { get; set; }
public abstract string TypeName { get; set; }
public abstract bool IsEnum { get; set; }
public abstract string Documentation { get; set; }
public abstract string DisplayName { get; set; }
public abstract IDictionary<string, string> Metadata { get; }
public abstract RazorDiagnosticCollection Diagnostics { get; }
}
}

View File

@ -0,0 +1,79 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class BoundAttributeParameterDescriptorComparer : IEqualityComparer<BoundAttributeParameterDescriptor>
{
/// <summary>
/// A default instance of the <see cref="BoundAttributeParameterDescriptorComparer"/>.
/// </summary>
public static readonly BoundAttributeParameterDescriptorComparer Default = new BoundAttributeParameterDescriptorComparer();
/// <summary>
/// A default instance of the <see cref="BoundAttributeParameterDescriptorComparer"/> that does case-sensitive comparison.
/// </summary>
internal static readonly BoundAttributeParameterDescriptorComparer CaseSensitive =
new BoundAttributeParameterDescriptorComparer(caseSensitive: true);
private readonly StringComparer _stringComparer;
private readonly StringComparison _stringComparison;
private BoundAttributeParameterDescriptorComparer(bool caseSensitive = false)
{
if (caseSensitive)
{
_stringComparer = StringComparer.Ordinal;
_stringComparison = StringComparison.Ordinal;
}
else
{
_stringComparer = StringComparer.OrdinalIgnoreCase;
_stringComparison = StringComparison.OrdinalIgnoreCase;
}
}
public virtual bool Equals(BoundAttributeParameterDescriptor descriptorX, BoundAttributeParameterDescriptor descriptorY)
{
if (object.ReferenceEquals(descriptorX, descriptorY))
{
return true;
}
if (descriptorX == null ^ descriptorY == null)
{
return false;
}
return
string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
descriptorX.IsEnum == descriptorY.IsEnum &&
string.Equals(descriptorX.Name, descriptorY.Name, _stringComparison) &&
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal) &&
string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
Enumerable.SequenceEqual(
descriptorX.Metadata.OrderBy(propertyX => propertyX.Key, StringComparer.Ordinal),
descriptorY.Metadata.OrderBy(propertyY => propertyY.Key, StringComparer.Ordinal));
}
public virtual int GetHashCode(BoundAttributeParameterDescriptor descriptor)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var hash = HashCodeCombiner.Start();
hash.Add(descriptor.Kind);
hash.Add(descriptor.Name, _stringComparer);
return hash.CombinedHash;
}
}
}

View File

@ -123,14 +123,20 @@
<data name="BindTagHelper_Element_Documentation" xml:space="preserve">
<value>Binds the provided expression to the '{0}' attribute and a change event delegate to the '{1}' attribute.</value>
</data>
<data name="BindTagHelper_Element_Event_Documentation" xml:space="preserve">
<value>Specifies the event handler name to attach for change notifications for the value provided by the '{0}' attribute.</value>
</data>
<data name="BindTagHelper_Element_Format_Documentation" xml:space="preserve">
<value>Specifies a format to convert the value specified by the '{0}' attribute. The format string can currently only be used with expressions of type &lt;code&gt;DateTime&lt;/code&gt;.</value>
</data>
<data name="BindTagHelper_Fallback_Documentation" xml:space="preserve">
<value>Binds the provided expression to an attribute and a change event, based on the naming of the bind attribute. For example: &lt;code&gt;bind-value-onchange="..."&lt;/code&gt; will assign the current value of the expression to the 'value' attribute, and assign a delegate that attempts to set the value to the 'onchange' attribute.</value>
<value>Binds the provided expression to an attribute and a change event, based on the naming of the bind attribute. For example: &lt;code&gt;bind-value="..."&lt;/code&gt; and &lt;code&gt;bind-value:event="onchange"&lt;/code&gt; will assign the current value of the expression to the 'value' attribute, and assign a delegate that attempts to set the value to the 'onchange' attribute.</value>
</data>
<data name="BindTagHelper_Fallback_Event_Documentation" xml:space="preserve">
<value>Specifies the event handler name to attach for change notifications for the value provided by the '{0}' attribute.</value>
</data>
<data name="BindTagHelper_Fallback_Format_Documentation" xml:space="preserve">
<value>Specifies a format to convert the value specified by the corresponding bind attribute. For example: &lt;code&gt;format-value="..."&lt;/code&gt; will apply a format string to the value specified in &lt;code&gt;bind-value-...&lt;/code&gt;. The format string can currently only be used with expressions of type &lt;code&gt;DateTime&lt;/code&gt;.</value>
<value>Specifies a format to convert the value specified by the corresponding bind attribute. For example: &lt;code&gt;bind-value:format="..."&lt;/code&gt; will apply a format string to the value specified in &lt;code&gt;bind-value="..."&lt;/code&gt;. The format string can currently only be used with expressions of type &lt;code&gt;DateTime&lt;/code&gt;.</value>
</data>
<data name="ChildContentParameterName_Documentation" xml:space="preserve">
<value>Specifies the parameter name for the '{0}' child content expression.</value>

View File

@ -30,18 +30,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// For each bind *usage* we need to rewrite the tag helper node to map to basic constructs.
var references = documentNode.FindDescendantReferences<TagHelperPropertyIntermediateNode>();
var parameterReferences = documentNode.FindDescendantReferences<TagHelperAttributeParameterIntermediateNode>();
var parents = new HashSet<IntermediateNode>();
for (var i = 0; i < references.Count; i++)
{
parents.Add(references[i].Parent);
}
for (var i = 0; i < parameterReferences.Count; i++)
{
parents.Add(parameterReferences[i].Parent);
}
foreach (var parent in parents)
{
ProcessDuplicates(parent);
}
// First, collect all the non-parameterized bind or bind-* attributes.
var bindEntries = new Dictionary<string, BindEntry>();
for (var i = 0; i < references.Count; i++)
{
var reference = references[i];
@ -55,14 +62,60 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
if (node.TagHelper.IsBindTagHelper() && node.AttributeName.StartsWith("bind"))
{
// Workaround for https://github.com/aspnet/Blazor/issues/703
var rewritten = RewriteUsage(reference.Parent, node);
reference.Remove();
bindEntries[node.AttributeName] = new BindEntry(reference);
}
}
for (var j = 0; j < rewritten.Length; j++)
// Now collect all the parameterized attributes and store them along with their corresponding bind or bind-* attributes.
for (var i = 0; i < parameterReferences.Count; i++)
{
var parameterReference = parameterReferences[i];
var node = (TagHelperAttributeParameterIntermediateNode)parameterReference.Node;
if (!parameterReference.Parent.Children.Contains(node))
{
// This node was removed as a duplicate, skip it.
continue;
}
if (node.TagHelper.IsBindTagHelper() && node.AttributeName.StartsWith("bind"))
{
if (!bindEntries.TryGetValue(node.AttributeNameWithoutParameter, out var entry))
{
reference.Parent.Children.Add(rewritten[j]);
// There is no corresponding bind node. Add a diagnostic and move on.
parameterReference.Parent.Diagnostics.Add(ComponentDiagnosticFactory.CreateBindAttributeParameter_MissingBind(
node.Source,
node.AttributeName));
}
else if (node.BoundAttributeParameter.Name == "event")
{
entry.BindEventNode = node;
}
else if (node.BoundAttributeParameter.Name == "format")
{
entry.BindFormatNode = node;
}
else
{
// Unsupported bind attribute parameter. This can only happen if bound attribute descriptor
// is configured to expect a parameter other than 'event' and 'format'.
}
// We've extracted what we need from the parameterized bind node. Remove it.
parameterReference.Remove();
}
}
// We now have all the info we need to rewrite the tag helper.
foreach (var entry in bindEntries)
{
var reference = entry.Value.BindNodeReference;
var rewritten = RewriteUsage(reference.Parent, entry.Value);
reference.Remove();
for (var j = 0; j < rewritten.Length; j++)
{
reference.Parent.Children.Add(rewritten[j]);
}
}
}
@ -78,18 +131,42 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
{
// For each usage of the general 'fallback' bind tag helper, it could duplicate
// the usage of a more specific one. Look for duplicates and remove the fallback.
var attribute = node.Children[i] as TagHelperPropertyIntermediateNode;
TagHelperDescriptor tagHelper = null;
string attributeName = null;
var attribute = node.Children[i];
if (attribute is TagHelperPropertyIntermediateNode propertyAttribute)
{
attributeName = propertyAttribute.AttributeName;
tagHelper = propertyAttribute.TagHelper;
}
else if (attribute is TagHelperAttributeParameterIntermediateNode parameterAttribute)
{
attributeName = parameterAttribute.AttributeName;
tagHelper = parameterAttribute.TagHelper;
}
if (attribute != null &&
attribute.TagHelper != null &&
attribute.TagHelper.IsFallbackBindTagHelper())
tagHelper != null &&
tagHelper.IsFallbackBindTagHelper())
{
for (var j = 0; j < node.Children.Count; j++)
{
var duplicate = node.Children[j] as TagHelperPropertyIntermediateNode;
TagHelperDescriptor duplicateTagHelper = null;
string duplicateAttributeName = null;
var duplicate = node.Children[j];
if (duplicate is TagHelperPropertyIntermediateNode duplicatePropertyAttribute)
{
duplicateAttributeName = duplicatePropertyAttribute.AttributeName;
duplicateTagHelper = duplicatePropertyAttribute.TagHelper;
}
else if (duplicate is TagHelperAttributeParameterIntermediateNode duplicateParameterAttribute)
{
duplicateAttributeName = duplicateParameterAttribute.AttributeName;
duplicateTagHelper = duplicateParameterAttribute.TagHelper;
}
if (duplicate != null &&
duplicate.TagHelper != null &&
duplicate.TagHelper.IsBindTagHelper() &&
duplicate.AttributeName == attribute.AttributeName &&
duplicateTagHelper != null &&
duplicateTagHelper.IsBindTagHelper() &&
duplicateAttributeName == attributeName &&
!object.ReferenceEquals(attribute, duplicate))
{
// Found a duplicate - remove the 'fallback' in favor of the
@ -104,16 +181,28 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// This is a workaround for a limitation where you can't write a tag helper that binds only
// when a specific attribute is **not** present.
if (attribute != null &&
attribute.TagHelper != null &&
attribute.TagHelper.IsInputElementFallbackBindTagHelper())
tagHelper != null &&
tagHelper.IsInputElementFallbackBindTagHelper())
{
for (var j = 0; j < node.Children.Count; j++)
{
var duplicate = node.Children[j] as TagHelperPropertyIntermediateNode;
TagHelperDescriptor duplicateTagHelper = null;
string duplicateAttributeName = null;
var duplicate = node.Children[j];
if (duplicate is TagHelperPropertyIntermediateNode duplicatePropertyAttribute)
{
duplicateAttributeName = duplicatePropertyAttribute.AttributeName;
duplicateTagHelper = duplicatePropertyAttribute.TagHelper;
}
else if (duplicate is TagHelperAttributeParameterIntermediateNode duplicateParameterAttribute)
{
duplicateAttributeName = duplicateParameterAttribute.AttributeName;
duplicateTagHelper = duplicateParameterAttribute.TagHelper;
}
if (duplicate != null &&
duplicate.TagHelper != null &&
duplicate.TagHelper.IsInputElementBindTagHelper() &&
duplicate.AttributeName == attribute.AttributeName &&
duplicateTagHelper != null &&
duplicateTagHelper.IsInputElementBindTagHelper() &&
duplicateAttributeName == attributeName &&
!object.ReferenceEquals(attribute, duplicate))
{
// Found a duplicate - remove the 'fallback' input tag helper in favor of the
@ -144,7 +233,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
}
}
private IntermediateNode[] RewriteUsage(IntermediateNode parent, TagHelperPropertyIntermediateNode node)
private IntermediateNode[] RewriteUsage(IntermediateNode parent, BindEntry bindEntry)
{
// Bind works similarly to a macro, it always expands to code that the user could have written.
//
@ -168,10 +257,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// We also assume that the element will be treated as a component for now because
// multiple passes handle 'special' tag helpers. We have another pass that translates
// a tag helper node back into 'regular' element when it doesn't have an associated component
var node = bindEntry.BindNode;
if (!TryComputeAttributeNames(
parent,
node,
node.AttributeName,
bindEntry,
out var valueAttributeName,
out var changeAttributeName,
out var expressionAttributeName,
@ -198,16 +287,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// Look for a matching format node. If we find one then we need to pass the format into the
// two nodes we generate.
IntermediateToken format = null;
if (bindEntry.BindFormatNode != null)
{
format = GetAttributeContent(bindEntry.BindFormatNode);
}
if (TryGetFormatNode(
parent,
node,
valueAttributeName,
out var formatNode))
{
// Don't write the format out as its own attribute, just capture it as a string
// or expression.
// If there is a format- attribute present, add a warning to say that it's unsupported.
parent.Children.Remove(formatNode);
format = GetAttributeContent(formatNode);
parent.Diagnostics.Add(ComponentDiagnosticFactory.CreateBindAttribute_FormatNode_Unsupported(formatNode.Source));
}
var valueExpressionTokens = new List<IntermediateToken>();
@ -336,10 +429,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
}
private bool TryParseBindAttribute(
string attributeName,
BindEntry bindEntry,
out string valueAttributeName,
out string changeAttributeName)
{
var attributeName = bindEntry.BindNode.AttributeName;
valueAttributeName = null;
changeAttributeName = null;
@ -348,6 +442,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
return false;
}
if (bindEntry.BindEventNode != null)
{
changeAttributeName = GetAttributeContent(bindEntry.BindEventNode)?.Content?.Trim('"');
}
if (attributeName == "bind")
{
return true;
@ -369,8 +468,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
return true;
case 3:
changeAttributeName = segments[2];
valueAttributeName = segments[1];
changeAttributeName = segments[2];
bindEntry.BindNode.Diagnostics.Add(ComponentDiagnosticFactory.CreateBindAttribute_UnsupportedFormat(bindEntry.BindNode.Source));
return true;
default:
@ -381,8 +481,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// Attempts to compute the attribute names that should be used for an instance of 'bind'.
private bool TryComputeAttributeNames(
IntermediateNode parent,
TagHelperPropertyIntermediateNode node,
string attributeName,
BindEntry bindEntry,
out string valueAttributeName,
out string changeAttributeName,
out string expressionAttributeName,
@ -397,7 +496,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// Even though some of our 'bind' tag helpers specify the attribute names, they
// should still satisfy one of the valid syntaxes.
if (!TryParseBindAttribute(attributeName, out valueAttributeName, out changeAttributeName))
if (!TryParseBindAttribute(bindEntry, out valueAttributeName, out changeAttributeName))
{
return false;
}
@ -408,6 +507,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
// generated to match a specific tag and has metadata that identify the attributes.
//
// We expect 1 bind tag helper per-node.
var node = bindEntry.BindNode;
var attributeName = node.AttributeName;
valueAttributeName = node.TagHelper.GetValueAttributeName() ?? valueAttributeName;
changeAttributeName = node.TagHelper.GetChangeAttributeName() ?? changeAttributeName;
expressionAttributeName = node.TagHelper.GetExpressionAttributeName() ?? expressionAttributeName;
@ -630,7 +731,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
});
}
private static IntermediateToken GetAttributeContent(TagHelperPropertyIntermediateNode node)
private static IntermediateToken GetAttributeContent(IntermediateNode node)
{
var template = node.FindDescendantNodes<TemplateIntermediateNode>().FirstOrDefault();
if (template != null)
@ -674,5 +775,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
};
}
}
private class BindEntry
{
public BindEntry(IntermediateNodeReference bindNodeReference)
{
BindNodeReference = bindNodeReference;
BindNode = (TagHelperPropertyIntermediateNode)bindNodeReference.Node;
}
public IntermediateNodeReference BindNodeReference { get; }
public TagHelperPropertyIntermediateNode BindNode { get; }
public TagHelperAttributeParameterIntermediateNode BindEventNode { get; set; }
public TagHelperAttributeParameterIntermediateNode BindFormatNode { get; set; }
}
}
}

View File

@ -166,7 +166,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}9991",
() => "The attribute names could not be inferred from bind attribute '{0}'. Bind attributes should be of the form" +
"'bind', 'bind-value' or 'bind-value-change'",
"'bind' or 'bind-value' along with their corresponding optional parameters like 'bind-value:event', 'bind:format' etc.",
RazorDiagnosticSeverity.Error);
public static RazorDiagnostic CreateBindAttribute_InvalidSyntax(SourceSpan? source, string attribute)
@ -332,5 +332,48 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
{
return RazorDiagnostic.Create(UnsupportedComponentImportContent, source ?? SourceSpan.Undefined);
}
public static readonly RazorDiagnosticDescriptor BindAttributeParameter_MissingBind =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10004",
() => "Could not find the non-parameterized bind attribute that corresponds to the attribute '{0}'.",
RazorDiagnosticSeverity.Error);
public static RazorDiagnostic CreateBindAttributeParameter_MissingBind(SourceSpan? source, string attribute)
{
var diagnostic = RazorDiagnostic.Create(
BindAttributeParameter_MissingBind,
source ?? SourceSpan.Undefined,
attribute);
return diagnostic;
}
public static readonly RazorDiagnosticDescriptor BindAttribute_UnsupportedFormat =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10005",
() => "Specifying event handlers in bind attributes are no longer supported. Specify it using the bind:event=... attribute instead.",
RazorDiagnosticSeverity.Warning);
public static RazorDiagnostic CreateBindAttribute_UnsupportedFormat(SourceSpan? source)
{
var diagnostic = RazorDiagnostic.Create(
BindAttribute_UnsupportedFormat,
source ?? SourceSpan.Undefined);
return diagnostic;
}
public static readonly RazorDiagnosticDescriptor BindAttribute_FormatNode_Unsupported =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}10005",
() => "Specifying format using 'format-...' attributes are no longer supported. Specify it using the 'bind-...:format=...' attribute instead.",
RazorDiagnosticSeverity.Warning);
public static RazorDiagnostic CreateBindAttribute_FormatNode_Unsupported(SourceSpan? source)
{
var diagnostic = RazorDiagnostic.Create(
BindAttribute_FormatNode_Unsupported,
source ?? SourceSpan.Undefined);
return diagnostic;
}
}
}

View File

@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Language
string indexerTypeName,
string documentation,
string displayName,
BoundAttributeParameterDescriptor[] parameterDescriptors,
Dictionary<string, string> metadata,
RazorDiagnostic[] diagnostics)
: base(kind)
@ -29,6 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Language
IndexerTypeName = indexerTypeName;
Documentation = documentation;
DisplayName = displayName;
BoundAttributeParameters = parameterDescriptors;
Metadata = metadata;
Diagnostics = diagnostics;

View File

@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.Razor.Language
private readonly DefaultTagHelperDescriptorBuilder _parent;
private readonly string _kind;
private readonly Dictionary<string, string> _metadata;
private List<DefaultBoundAttributeParameterDescriptorBuilder> _attributeParameterBuilders;
private RazorDiagnosticCollection _diagnostics;
@ -73,6 +74,20 @@ namespace Microsoft.AspNetCore.Razor.Language
}
}
public override void BindAttributeParameter(Action<BoundAttributeParameterDescriptorBuilder> configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
EnsureAttributeParameterBuilders();
var builder = new DefaultBoundAttributeParameterDescriptorBuilder(this, _kind);
configure(builder);
_attributeParameterBuilders.Add(builder);
}
public BoundAttributeDescriptor Build()
{
var validationDiagnostics = Validate();
@ -82,6 +97,19 @@ namespace Microsoft.AspNetCore.Razor.Language
diagnostics.UnionWith(_diagnostics);
}
var parameters = Array.Empty<BoundAttributeParameterDescriptor>();
if (_attributeParameterBuilders != null)
{
// Attribute parameters are case-sensitive.
var parameterset = new HashSet<BoundAttributeParameterDescriptor>(BoundAttributeParameterDescriptorComparer.CaseSensitive);
for (var i = 0; i < _attributeParameterBuilders.Count; i++)
{
parameterset.Add(_attributeParameterBuilders[i].Build());
}
parameters = parameterset.ToArray();
}
var descriptor = new DefaultBoundAttributeDescriptor(
_kind,
Name,
@ -92,6 +120,7 @@ namespace Microsoft.AspNetCore.Razor.Language
IndexerValueTypeName,
Documentation,
GetDisplayName(),
parameters,
new Dictionary<string, string>(Metadata),
diagnostics.ToArray());
@ -205,5 +234,13 @@ namespace Microsoft.AspNetCore.Razor.Language
}
}
}
private void EnsureAttributeParameterBuilders()
{
if (_attributeParameterBuilders == null)
{
_attributeParameterBuilders = new List<DefaultBoundAttributeParameterDescriptorBuilder>();
}
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultBoundAttributeParameterDescriptor : BoundAttributeParameterDescriptor
{
public DefaultBoundAttributeParameterDescriptor(
string kind,
string name,
string typeName,
bool isEnum,
string documentation,
string displayName,
Dictionary<string, string> metadata,
RazorDiagnostic[] diagnostics)
: base(kind)
{
Name = name;
TypeName = typeName;
IsEnum = isEnum;
Documentation = documentation;
DisplayName = displayName;
Metadata = metadata;
Diagnostics = diagnostics;
IsStringProperty = typeName == typeof(string).FullName || typeName == "string";
IsBooleanProperty = typeName == typeof(bool).FullName || typeName == "bool";
}
}
}

View File

@ -0,0 +1,106 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.Language
{
internal class DefaultBoundAttributeParameterDescriptorBuilder : BoundAttributeParameterDescriptorBuilder
{
private readonly DefaultBoundAttributeDescriptorBuilder _parent;
private readonly string _kind;
private readonly Dictionary<string, string> _metadata;
private RazorDiagnosticCollection _diagnostics;
public DefaultBoundAttributeParameterDescriptorBuilder(DefaultBoundAttributeDescriptorBuilder parent, string kind)
{
_parent = parent;
_kind = kind;
_metadata = new Dictionary<string, string>();
}
public override string Name { get; set; }
public override string TypeName { get; set; }
public override bool IsEnum { get; set; }
public override string Documentation { get; set; }
public override string DisplayName { get; set; }
public override IDictionary<string, string> Metadata => _metadata;
public override RazorDiagnosticCollection Diagnostics
{
get
{
if (_diagnostics == null)
{
_diagnostics = new RazorDiagnosticCollection();
}
return _diagnostics;
}
}
public BoundAttributeParameterDescriptor Build()
{
var validationDiagnostics = Validate();
var diagnostics = new HashSet<RazorDiagnostic>(validationDiagnostics);
if (_diagnostics != null)
{
diagnostics.UnionWith(_diagnostics);
}
var descriptor = new DefaultBoundAttributeParameterDescriptor(
_kind,
Name,
TypeName,
IsEnum,
Documentation,
GetDisplayName(),
new Dictionary<string, string>(Metadata),
diagnostics.ToArray());
return descriptor;
}
private string GetDisplayName()
{
if (DisplayName != null)
{
return DisplayName;
}
return $":{Name}";
}
private IEnumerable<RazorDiagnostic> Validate()
{
if (string.IsNullOrWhiteSpace(Name))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeParameterNullOrWhitespace(_parent.Name);
yield return diagnostic;
}
else
{
foreach (var character in Name)
{
if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeParameterName(
_parent.Name,
Name,
character);
yield return diagnostic;
}
}
}
}
}
}

View File

@ -1716,6 +1716,7 @@ namespace Microsoft.AspNetCore.Razor.Language
var descriptors = element.TagHelperInfo.BindingResult.Descriptors;
var attributeName = node.Name.GetContent();
var attributeValueNode = node.Value;
var associatedDescriptors = descriptors.Where(descriptor =>
descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor)));
@ -1723,24 +1724,47 @@ namespace Microsoft.AspNetCore.Razor.Language
{
foreach (var associatedDescriptor in associatedDescriptors)
{
var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(a =>
if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch(
attributeName,
associatedDescriptor,
out var associatedAttributeDescriptor,
out var indexerMatch,
out var parameterMatch,
out var associatedAttributeParameterDescriptor))
{
return TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, a);
});
IntermediateNode attributeNode;
if (parameterMatch &&
TagHelperMatchingConventions.TryGetBoundAttributeParameter(attributeName, out var attributeNameWithoutParameter, out var _))
{
attributeNode = new TagHelperAttributeParameterIntermediateNode()
{
AttributeName = attributeName,
AttributeNameWithoutParameter = attributeNameWithoutParameter,
BoundAttributeParameter = associatedAttributeParameterDescriptor,
BoundAttribute = associatedAttributeDescriptor,
TagHelper = associatedDescriptor,
IsIndexerNameMatch = indexerMatch,
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
Source = BuildSourceSpanFromNode(attributeValueNode),
};
}
else
{
attributeNode = new TagHelperPropertyIntermediateNode()
{
AttributeName = attributeName,
BoundAttribute = associatedAttributeDescriptor,
TagHelper = associatedDescriptor,
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
Source = BuildSourceSpanFromNode(attributeValueNode),
IsIndexerNameMatch = indexerMatch,
};
}
var setTagHelperProperty = new TagHelperPropertyIntermediateNode()
{
AttributeName = attributeName,
BoundAttribute = associatedAttributeDescriptor,
TagHelper = associatedDescriptor,
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
Source = BuildSourceSpanFromNode(attributeValueNode),
IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(attributeName, associatedAttributeDescriptor),
};
_builder.Push(setTagHelperProperty);
VisitAttributeValue(attributeValueNode);
_builder.Pop();
_builder.Push(attributeNode);
VisitAttributeValue(attributeValueNode);
_builder.Pop();
}
}
}
else

View File

@ -129,6 +129,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
VisitDefault(node);
}
public virtual void VisitTagHelperAttributeParameter(TagHelperAttributeParameterIntermediateNode node)
{
VisitDefault(node);
}
public virtual void VisitComponent(ComponentIntermediateNode node)
{
VisitDefault(node);

View File

@ -0,0 +1,47 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
{
public sealed class TagHelperAttributeParameterIntermediateNode : IntermediateNode
{
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
public string AttributeName { get; set; }
public string AttributeNameWithoutParameter { get; set; }
public AttributeStructure AttributeStructure { get; set; }
public BoundAttributeParameterDescriptor BoundAttributeParameter { get; set; }
public BoundAttributeDescriptor BoundAttribute { get; set; }
public TagHelperDescriptor TagHelper { get; set; }
public bool IsIndexerNameMatch { get; set; }
public override void Accept(IntermediateNodeVisitor visitor)
{
if (visitor == null)
{
throw new ArgumentNullException(nameof(visitor));
}
visitor.VisitTagHelperAttributeParameter(this);
}
public override void FormatNode(IntermediateNodeFormatter formatter)
{
formatter.WriteContent(AttributeName);
formatter.WriteProperty(nameof(AttributeName), AttributeName);
formatter.WriteProperty(nameof(AttributeStructure), AttributeStructure.ToString());
formatter.WriteProperty(nameof(BoundAttribute), BoundAttribute?.DisplayName);
formatter.WriteProperty(nameof(BoundAttributeParameter), BoundAttributeParameter?.DisplayName);
formatter.WriteProperty(nameof(TagHelper), TagHelper?.DisplayName);
}
}
}

View File

@ -273,17 +273,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
// Determines the full name of the Type of the property corresponding to an attribute with the given name.
private static string GetPropertyType(string name, IEnumerable<TagHelperDescriptor> descriptors)
{
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
var isBoundToIndexer = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, firstBoundAttribute);
foreach (var descriptor in descriptors)
{
if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch(name, descriptor, out var firstBoundAttribute, out var indexerMatch, out var _, out var _))
{
if (indexerMatch)
{
return firstBoundAttribute.IndexerTypeName;
}
else
{
return firstBoundAttribute.TypeName;
}
}
}
if (isBoundToIndexer)
{
return firstBoundAttribute?.IndexerTypeName;
}
else
{
return firstBoundAttribute?.TypeName;
}
return null;
}
// Create a TryParseResult for given name, filling in binding details.
@ -292,13 +297,39 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
IEnumerable<TagHelperDescriptor> descriptors,
HashSet<string> processedBoundAttributeNames)
{
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
var isBoundAttribute = firstBoundAttribute != null;
var isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.ExpectsStringValue(name);
var isBoundBooleanAttribute = isBoundAttribute && firstBoundAttribute.ExpectsBooleanValue(name);
var isMissingDictionaryKey = isBoundAttribute &&
firstBoundAttribute.IndexerNamePrefix != null &&
name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
var isBoundAttribute = false;
var isBoundNonStringAttribute = false;
var isBoundBooleanAttribute = false;
var isMissingDictionaryKey = false;
foreach (var descriptor in descriptors)
{
if (TagHelperMatchingConventions.TryGetFirstBoundAttributeMatch(
name,
descriptor,
out var firstBoundAttribute,
out var indexerMatch,
out var parameterMatch,
out var boundAttributeParameter))
{
isBoundAttribute = true;
if (parameterMatch)
{
isBoundNonStringAttribute = !boundAttributeParameter.IsStringProperty;
isBoundBooleanAttribute = boundAttributeParameter.IsBooleanProperty;
isMissingDictionaryKey = false;
}
else
{
isBoundNonStringAttribute = !firstBoundAttribute.ExpectsStringValue(name);
isBoundBooleanAttribute = firstBoundAttribute.ExpectsBooleanValue(name);
isMissingDictionaryKey = firstBoundAttribute.IndexerNamePrefix != null &&
name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
}
break;
}
}
var isDuplicateAttribute = false;
if (isBoundAttribute && !processedBoundAttributeNames.Add(name))
@ -318,18 +349,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
};
}
// Finds first TagHelperAttributeDescriptor matching given name.
private static BoundAttributeDescriptor FindFirstBoundAttribute(
string name,
IEnumerable<TagHelperDescriptor> descriptors)
{
var firstBoundAttribute = descriptors
.SelectMany(descriptor => descriptor.BoundAttributes)
.FirstOrDefault(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(name, attributeDescriptor));
return firstBoundAttribute;
}
private static string GetAttributeValueContent(RazorSyntaxNode attributeBlock)
{
if (attributeBlock is MarkupTagHelperAttributeSyntax tagHelperAttribute)

View File

@ -779,6 +779,41 @@ namespace Microsoft.AspNetCore.Razor.Language
return diagnostic;
}
internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeParameterNullOrWhitespace =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}3013",
() => Resources.TagHelper_InvalidBoundAttributeParameterNullOrWhitespace,
RazorDiagnosticSeverity.Error);
public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeParameterNullOrWhitespace(string attributeName)
{
var diagnostic = RazorDiagnostic.Create(
TagHelper_InvalidBoundAttributeParameterNullOrWhitespace,
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
attributeName);
return diagnostic;
}
internal static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeParameterName =
new RazorDiagnosticDescriptor(
$"{DiagnosticPrefix}3014",
() => Resources.TagHelper_InvalidBoundAttributeParameterName,
RazorDiagnosticSeverity.Error);
public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeParameterName(
string attributeName,
string invalidName,
char invalidCharacter)
{
var diagnostic = RazorDiagnostic.Create(
TagHelper_InvalidBoundAttributeParameterName,
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
attributeName,
invalidName,
invalidCharacter);
return diagnostic;
}
#endregion
#region Rewriter Errors

View File

@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Razor.Language
public enum RazorDiagnosticSeverity
{
// Purposely using the same value as Roslyn here.
Warning = 2,
Error = 3,
}
}

View File

@ -541,4 +541,10 @@
<data name="NamespaceDirective_NamespaceToken_Name" xml:space="preserve">
<value>Namespace</value>
</data>
<data name="TagHelper_InvalidBoundAttributeParameterName" xml:space="preserve">
<value>Invalid tag helper bound attribute parameter '{1}' on bound attribute '{0}'. Tag helpers cannot bind to HTML attribute parameters with name '{1}' because the name contains a '{3}' character.</value>
</data>
<data name="TagHelper_InvalidBoundAttributeParameterNullOrWhitespace" xml:space="preserve">
<value>Invalid tag helper bound attribute parameter '{0}'. Tag helpers cannot bind to HTML attribute parameters with a null or empty name.</value>
</data>
</root>

View File

@ -128,7 +128,9 @@ namespace Microsoft.AspNetCore.Razor.Language
public static bool CanSatisfyBoundAttribute(string name, BoundAttributeDescriptor descriptor)
{
return SatisfiesBoundAttributeName(name, descriptor) || SatisfiesBoundAttributeIndexer(name, descriptor);
return SatisfiesBoundAttributeName(name, descriptor) ||
SatisfiesBoundAttributeIndexer(name, descriptor) ||
descriptor.BoundAttributeParameters.Any(p => SatisfiesBoundAttributeWithParameter(name, descriptor, p));
}
public static bool SatisfiesBoundAttributeIndexer(string name, BoundAttributeDescriptor descriptor)
@ -138,6 +140,84 @@ namespace Microsoft.AspNetCore.Razor.Language
name.StartsWith(descriptor.IndexerNamePrefix, StringComparison.OrdinalIgnoreCase);
}
public static bool SatisfiesBoundAttributeWithParameter(string name, BoundAttributeDescriptor parent, BoundAttributeParameterDescriptor descriptor)
{
if (TryGetBoundAttributeParameter(name, out var attributeName, out var parameterName))
{
var satisfiesBoundAttributeName = SatisfiesBoundAttributeName(attributeName, parent);
var satisfiesBoundAttributeIndexer = SatisfiesBoundAttributeIndexer(attributeName, parent);
var matchesParameter = string.Equals(descriptor.Name, parameterName, StringComparison.Ordinal);
return (satisfiesBoundAttributeName || satisfiesBoundAttributeIndexer) && matchesParameter;
}
return false;
}
public static bool TryGetBoundAttributeParameter(string fullAttributeName, out string boundAttributeName, out string parameterName)
{
boundAttributeName = null;
parameterName = null;
if (!string.IsNullOrEmpty(fullAttributeName) && fullAttributeName.IndexOf(':') != -1)
{
var segments = fullAttributeName.Split(new[] { ':' }, 2);
boundAttributeName = segments[0];
parameterName = segments[1];
return true;
}
return false;
}
public static bool TryGetFirstBoundAttributeMatch(
string name,
TagHelperDescriptor descriptor,
out BoundAttributeDescriptor boundAttribute,
out bool indexerMatch,
out bool parameterMatch,
out BoundAttributeParameterDescriptor boundAttributeParameter)
{
indexerMatch = false;
parameterMatch = false;
boundAttribute = null;
boundAttributeParameter = null;
if (string.IsNullOrEmpty(name) || descriptor == null)
{
return false;
}
// First, check if we have a bound attribute descriptor that matches the parameter if it exists.
foreach (var attribute in descriptor.BoundAttributes)
{
boundAttributeParameter = attribute.BoundAttributeParameters.FirstOrDefault(
p => SatisfiesBoundAttributeWithParameter(name, attribute, p));
if (boundAttributeParameter != null)
{
boundAttribute = attribute;
indexerMatch = SatisfiesBoundAttributeIndexer(name, attribute);
parameterMatch = true;
return true;
}
}
// If we reach here, either the attribute name doesn't contain a parameter portion or
// the specified parameter isn't supported by any of the BoundAttributeDescriptors.
foreach (var attribute in descriptor.BoundAttributes)
{
if (CanSatisfyBoundAttribute(name, attribute))
{
boundAttribute = attribute;
indexerMatch = SatisfiesBoundAttributeIndexer(name, attribute);
return true;
}
}
// No matches found.
return false;
}
private static bool SatisfiesBoundAttributeName(string name, BoundAttributeDescriptor descriptor)
{
return string.Equals(descriptor.Name, name, StringComparison.OrdinalIgnoreCase);

View File

@ -644,7 +644,7 @@ namespace Test
}"));
// Act
var generated = CompileToCSharp(@"
<MyComponent bind-Value-OnChanged=""ParentValue"" />
<MyComponent bind-Value=""ParentValue"" bind-Value:event=""OnChanged"" />
@code {
public int ParentValue { get; set; } = 42;
}");
@ -674,7 +674,7 @@ namespace Test
}"));
var generated = CompileToCSharp(@"
<MyComponent bind-Value-OnChanged=""ParentValue"" />
<MyComponent bind-Value=""ParentValue"" bind-Value:event=""OnChanged"" />
@code {
public int ParentValue { get; set; } = 42;
}");
@ -982,7 +982,7 @@ namespace Test
// Act
var generated = CompileToCSharp(@"
<input type=""text"" bind=""@CurrentDate"" format-value=""MM/dd/yyyy""/>
<input type=""text"" bind=""@CurrentDate"" bind:format=""MM/dd/yyyy""/>
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");
@ -1000,7 +1000,7 @@ namespace Test
// Act
var generated = CompileToCSharp(@"
<input type=""text"" bind=""@CurrentDate"" format-value=""@Format""/>
<input type=""text"" bind=""@CurrentDate"" bind:format=""@Format""/>
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
@ -1056,7 +1056,7 @@ namespace Test
// Act
var generated = CompileToCSharp(@"
<input type=""text"" bind-value-onchange=""@ParentValue"" />
<input type=""text"" bind-value=""@ParentValue"" bind-value:event=""onchange"" />
@code {
public int ParentValue { get; set; } = 42;
}");
@ -1074,7 +1074,7 @@ namespace Test
// Act
var generated = CompileToCSharp(@"
<input type=""text"" bind-value-onchange=""@CurrentDate"" format-value=""MM/dd"" />
<input type=""text"" bind-value=""@CurrentDate"" bind-value:event=""onchange"" bind-value:format=""MM/dd"" />
@code {
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
}");

View File

@ -23,7 +23,7 @@ namespace Test
__o = Microsoft.AspNetCore.Components.RuntimeHelpers.TypeCheck<System.Int32>(Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
ParentValue
ParentValue
#line default
#line hidden

View File

@ -14,16 +14,16 @@ Document -
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - protected override - void - BuildRenderTree
Component - (0:0,0 [50] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
Component - (0:0,0 [69] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - - CSharp - )
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - __value => ParentValue = __value
HtmlContent - (50:0,50 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (50:0,50 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n
HtmlContent - (69:0,69 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (69:0,69 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n

View File

@ -1,13 +1,13 @@
Source Location: (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml)
|ParentValue|
Generated Location: (1022:25,35 [11] )
Generated Location: (1012:25,25 [11] )
|ParentValue|
Source Location: (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
|
public int ParentValue { get; set; } = 42;
|
Generated Location: (1627:47,7 [50] )
Generated Location: (1617:47,7 [50] )
|
public int ParentValue { get; set; } = 42;
|

View File

@ -23,7 +23,7 @@ namespace Test
__o = Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
ParentValue
ParentValue
#line default
#line hidden

View File

@ -14,16 +14,16 @@ Document -
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - protected override - void - BuildRenderTree
Component - (0:0,0 [50] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
Component - (0:0,0 [69] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - - CSharp - )
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => ParentValue = __value, ParentValue)
HtmlContent - (50:0,50 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (50:0,50 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n
HtmlContent - (69:0,69 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (69:0,69 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n

View File

@ -1,13 +1,13 @@
Source Location: (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml)
|ParentValue|
Generated Location: (951:25,35 [11] )
Generated Location: (941:25,25 [11] )
|ParentValue|
Source Location: (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
|
public int ParentValue { get; set; } = 42;
|
Generated Location: (1595:46,7 [50] )
Generated Location: (1585:46,7 [50] )
|
public int ParentValue { get; set; } = 42;
|

View File

@ -23,7 +23,7 @@ namespace Test
__o = Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
CurrentDate
CurrentDate
#line default
#line hidden

View File

@ -14,21 +14,21 @@ Document -
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [77] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [101] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (41:0,41 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - CurrentDate
IntermediateToken - (32:0,32 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - CurrentDate
IntermediateToken - - CSharp - ,
IntermediateToken - - CSharp - "MM/dd"
IntermediateToken - - CSharp - )
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => CurrentDate = __value, CurrentDate, "MM/dd")
HtmlContent - (77:0,77 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (77:0,77 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (86:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (86:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n
HtmlContent - (101:0,101 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (101:0,101 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (110:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (110:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n

View File

@ -1,13 +1,13 @@
Source Location: (41:0,41 [11] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (32:0,32 [11] x:\dir\subdir\Test\TestComponent.cshtml)
|CurrentDate|
Generated Location: (957:25,41 [11] )
Generated Location: (948:25,32 [11] )
|CurrentDate|
Source Location: (86:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (110:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
|
Generated Location: (1320:36,7 [77] )
Generated Location: (1311:36,7 [77] )
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
|

View File

@ -23,7 +23,7 @@ namespace Test
__o = Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
ParentValue
ParentValue
#line default
#line hidden

View File

@ -14,19 +14,19 @@ Document -
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [56] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [75] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (41:0,41 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - (32:0,32 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - - CSharp - )
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => ParentValue = __value, ParentValue)
HtmlContent - (56:0,56 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (56:0,56 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (65:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (65:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n
HtmlContent - (75:0,75 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (75:0,75 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (84:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (84:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n

View File

@ -1,13 +1,13 @@
Source Location: (41:0,41 [11] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (32:0,32 [11] x:\dir\subdir\Test\TestComponent.cshtml)
|ParentValue|
Generated Location: (957:25,41 [11] )
Generated Location: (948:25,32 [11] )
|ParentValue|
Source Location: (65:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (84:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
|
public int ParentValue { get; set; } = 42;
|
Generated Location: (1302:36,7 [50] )
Generated Location: (1293:36,7 [50] )
|
public int ParentValue { get; set; } = 42;
|

View File

@ -31,7 +31,7 @@ namespace Test
,
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
Format
Format
#line default
#line hidden

View File

@ -14,7 +14,7 @@ Document -
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [63] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [62] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
@ -23,12 +23,12 @@ Document -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (26:0,26 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - CurrentDate
IntermediateToken - - CSharp - ,
IntermediateToken - (54:0,54 [6] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - Format
IntermediateToken - (53:0,53 [6] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - Format
IntermediateToken - - CSharp - )
HtmlAttribute - (25:0,25 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => CurrentDate = __value, CurrentDate, Format)
HtmlContent - (63:0,63 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (63:0,63 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (72:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (72:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n\n public string Format { get; set; } = "MM/dd/yyyy";\n
HtmlContent - (62:0,62 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (62:0,62 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (71:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (71:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n\n public string Format { get; set; } = "MM/dd/yyyy";\n

View File

@ -3,18 +3,18 @@ Source Location: (26:0,26 [11] x:\dir\subdir\Test\TestComponent.cshtml)
Generated Location: (942:25,26 [11] )
|CurrentDate|
Source Location: (54:0,54 [6] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (53:0,53 [6] x:\dir\subdir\Test\TestComponent.cshtml)
|Format|
Generated Location: (1145:33,54 [6] )
Generated Location: (1144:33,53 [6] )
|Format|
Source Location: (72:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (71:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
public string Format { get; set; } = "MM/dd/yyyy";
|
Generated Location: (1493:44,7 [135] )
Generated Location: (1492:44,7 [135] )
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);

View File

@ -14,7 +14,7 @@ Document -
CSharpCode -
IntermediateToken - - CSharp - #pragma warning restore 0414
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [66] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [65] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
@ -28,7 +28,7 @@ Document -
HtmlAttribute - (25:0,25 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => CurrentDate = __value, CurrentDate, "MM/dd/yyyy")
HtmlContent - (66:0,66 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (66:0,66 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (75:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (75:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n
HtmlContent - (65:0,65 [2] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (65:0,65 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n
CSharpCode - (74:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (74:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n

View File

@ -3,7 +3,7 @@ Source Location: (26:0,26 [11] x:\dir\subdir\Test\TestComponent.cshtml)
Generated Location: (942:25,26 [11] )
|CurrentDate|
Source Location: (75:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (74:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
|

View File

@ -17,7 +17,7 @@ namespace Test
builder.AddAttribute(1, "Value", Microsoft.AspNetCore.Components.RuntimeHelpers.TypeCheck<System.Int32>(Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
ParentValue
ParentValue
#line default
#line hidden

View File

@ -7,14 +7,14 @@ Document -
UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
Component - (0:0,0 [50] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
Component - (0:0,0 [69] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - - CSharp - )
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - __value => ParentValue = __value
CSharpCode - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n
CSharpCode - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n

View File

@ -1,8 +1,8 @@
Source Location: (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
|
public int ParentValue { get; set; } = 42;
|
Generated Location: (1202:31,7 [50] )
Generated Location: (1192:31,7 [50] )
|
public int ParentValue { get; set; } = 42;
|

View File

@ -17,7 +17,7 @@ namespace Test
builder.AddAttribute(1, "Value", Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
ParentValue
ParentValue
#line default
#line hidden

View File

@ -7,14 +7,14 @@ Document -
UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
Component - (0:0,0 [50] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
Component - (0:0,0 [69] x:\dir\subdir\Test\TestComponent.cshtml) - MyComponent
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - Value - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - - CSharp - )
ComponentAttribute - (35:0,35 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
ComponentAttribute - (25:0,25 [11] x:\dir\subdir\Test\TestComponent.cshtml) - OnChanged - AttributeStructure.DoubleQuotes
CSharpExpression -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => ParentValue = __value, ParentValue)
CSharpCode - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n
CSharpCode - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n

View File

@ -1,8 +1,8 @@
Source Location: (59:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (78:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
|
public int ParentValue { get; set; } = 42;
|
Generated Location: (1184:31,7 [50] )
Generated Location: (1174:31,7 [50] )
|
public int ParentValue { get; set; } = 42;
|

View File

@ -18,7 +18,7 @@ namespace Test
builder.AddAttribute(2, "value", Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
CurrentDate
CurrentDate
#line default
#line hidden

View File

@ -7,19 +7,19 @@ Document -
UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [77] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [101] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (41:0,41 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - CurrentDate
IntermediateToken - (32:0,32 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - CurrentDate
IntermediateToken - - CSharp - ,
IntermediateToken - - CSharp - "MM/dd"
IntermediateToken - - CSharp - )
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => CurrentDate = __value, CurrentDate, "MM/dd")
CSharpCode - (86:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (86:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n
CSharpCode - (110:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (110:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n

View File

@ -1,8 +1,8 @@
Source Location: (86:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (110:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
|
Generated Location: (1248:32,7 [77] )
Generated Location: (1239:32,7 [77] )
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
|

View File

@ -18,7 +18,7 @@ namespace Test
builder.AddAttribute(2, "value", Microsoft.AspNetCore.Components.BindMethods.GetValue(
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
ParentValue
ParentValue
#line default
#line hidden

View File

@ -7,17 +7,17 @@ Document -
UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [56] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [75] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - value=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (41:0,41 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - (32:0,32 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - ParentValue
IntermediateToken - - CSharp - )
HtmlAttribute - (40:0,40 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
HtmlAttribute - (31:0,31 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => ParentValue = __value, ParentValue)
CSharpCode - (65:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (65:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n
CSharpCode - (84:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (84:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public int ParentValue { get; set; } = 42;\n

View File

@ -1,8 +1,8 @@
Source Location: (65:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (84:1,7 [50] x:\dir\subdir\Test\TestComponent.cshtml)
|
public int ParentValue { get; set; } = 42;
|
Generated Location: (1230:32,7 [50] )
Generated Location: (1221:32,7 [50] )
|
public int ParentValue { get; set; } = 42;
|

View File

@ -26,7 +26,7 @@ namespace Test
,
#nullable restore
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
Format
Format
#line default
#line hidden

View File

@ -7,7 +7,7 @@ Document -
UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [63] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [62] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
@ -16,10 +16,10 @@ Document -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.BindMethods.GetValue(
IntermediateToken - (26:0,26 [11] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - CurrentDate
IntermediateToken - - CSharp - ,
IntermediateToken - (54:0,54 [6] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - Format
IntermediateToken - (53:0,53 [6] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - Format
IntermediateToken - - CSharp - )
HtmlAttribute - (25:0,25 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => CurrentDate = __value, CurrentDate, Format)
CSharpCode - (72:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (72:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n\n public string Format { get; set; } = "MM/dd/yyyy";\n
CSharpCode - (71:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (71:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n\n public string Format { get; set; } = "MM/dd/yyyy";\n

View File

@ -1,10 +1,10 @@
Source Location: (72:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (71:1,7 [135] x:\dir\subdir\Test\TestComponent.cshtml)
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
public string Format { get; set; } = "MM/dd/yyyy";
|
Generated Location: (1421:40,7 [135] )
Generated Location: (1420:40,7 [135] )
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);

View File

@ -7,7 +7,7 @@ Document -
UsingDirective - (104:5,1 [39] ) - Microsoft.AspNetCore.Components
ClassDeclaration - - public - TestComponent - Microsoft.AspNetCore.Components.ComponentBase -
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (0:0,0 [66] x:\dir\subdir\Test\TestComponent.cshtml) - input
MarkupElement - (0:0,0 [65] x:\dir\subdir\Test\TestComponent.cshtml) - input
HtmlAttribute - - type=" - "
HtmlAttributeValue - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) -
IntermediateToken - (13:0,13 [4] x:\dir\subdir\Test\TestComponent.cshtml) - Html - text
@ -21,5 +21,5 @@ Document -
HtmlAttribute - (25:0,25 [12] x:\dir\subdir\Test\TestComponent.cshtml) - onchange=" - "
CSharpExpressionAttributeValue - -
IntermediateToken - - CSharp - Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => CurrentDate = __value, CurrentDate, "MM/dd/yyyy")
CSharpCode - (75:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (75:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n
CSharpCode - (74:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
IntermediateToken - (74:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml) - CSharp - \n public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);\n

View File

@ -1,4 +1,4 @@
Source Location: (75:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
Source Location: (74:1,7 [77] x:\dir\subdir\Test\TestComponent.cshtml)
|
public DateTime CurrentDate { get; set; } = new DateTime(2018, 1, 1);
|

View File

@ -46,7 +46,7 @@ namespace Microsoft.CodeAnalysis.Razor
//
// We handle a few different cases here:
//
// 1. When given an attribute like **anywhere**'bind-value-onchange="@FirstName"' we will
// 1. When given an attribute like **anywhere**'bind-value="@FirstName"' and 'bind-value:event="onchange"' we will
// generate the 'value' attribute and 'onchange' attribute.
//
// We don't do any transformation or inference for this case, because the developer has
@ -143,15 +143,35 @@ namespace Microsoft.CodeAnalysis.Razor
{
attribute.Documentation = ComponentResources.BindTagHelper_Fallback_Documentation;
attribute.Name = "bind-...";
var attributeName = "bind-...";
attribute.Name = attributeName;
attribute.AsDictionary("bind-", typeof(object).FullName);
// WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
// a C# property will crash trying to create the toolips.
attribute.SetPropertyName("Bind");
attribute.TypeName = "System.Collections.Generic.Dictionary<string, object>";
attribute.BindAttributeParameter(parameter =>
{
parameter.Name = "format";
parameter.TypeName = typeof(string).FullName;
parameter.Documentation = ComponentResources.BindTagHelper_Fallback_Format_Documentation;
parameter.SetPropertyName("Format");
});
attribute.BindAttributeParameter(parameter =>
{
parameter.Name = "event";
parameter.TypeName = typeof(string).FullName;
parameter.Documentation = string.Format(ComponentResources.BindTagHelper_Fallback_Event_Documentation, attributeName);
parameter.SetPropertyName("Event");
});
});
// This is no longer supported. This is just here so we can add a diagnostic later on when this matches.
builder.BindAttribute(attribute =>
{
attribute.Documentation = ComponentResources.BindTagHelper_Fallback_Format_Documentation;
@ -237,6 +257,7 @@ namespace Microsoft.CodeAnalysis.Razor
return results;
}
private List<TagHelperDescriptor> CreateElementBindTagHelpers(List<ElementBindData> data)
{
var results = new List<TagHelperDescriptor>();
@ -251,6 +272,8 @@ namespace Microsoft.CodeAnalysis.Razor
var formatName = entry.Suffix == null ? "Format_" + entry.ValueAttribute : "Format_" + entry.Suffix;
var formatAttributeName = entry.Suffix == null ? "format-" + entry.ValueAttribute : "format-" + entry.Suffix;
var eventName = entry.Suffix == null ? "Event_" + entry.ValueAttribute : "Event_" + entry.Suffix;
var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.Bind.TagHelperKind, name, ComponentsApi.AssemblyName);
builder.Documentation = string.Format(
ComponentResources.BindTagHelper_Element_Documentation,
@ -315,14 +338,32 @@ namespace Microsoft.CodeAnalysis.Razor
// WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
// a C# property will crash trying to create the toolips.
a.SetPropertyName(name);
a.BindAttributeParameter(parameter =>
{
parameter.Name = "format";
parameter.TypeName = typeof(string).FullName;
parameter.Documentation = string.Format(ComponentResources.BindTagHelper_Element_Format_Documentation, attributeName);
parameter.SetPropertyName(formatName);
});
a.BindAttributeParameter(parameter =>
{
parameter.Name = "event";
parameter.TypeName = typeof(string).FullName;
parameter.Documentation = string.Format(ComponentResources.BindTagHelper_Element_Event_Documentation, attributeName);
parameter.SetPropertyName(eventName);
});
});
// This is no longer supported. This is just here so we can add a diagnostic later on when this matches.
builder.BindAttribute(attribute =>
{
attribute.Documentation = string.Format(ComponentResources.BindTagHelper_Element_Format_Documentation, attributeName);
attribute.Name = formatAttributeName;
attribute.TypeName = "System.String";
attribute.Documentation = string.Format(ComponentResources.BindTagHelper_Element_Format_Documentation, attributeName);
// WTE has a bug 15.7p1 where a Tag Helper without a display-name that looks like
// a C# property will crash trying to create the toolips.
@ -376,7 +417,6 @@ namespace Microsoft.CodeAnalysis.Razor
if (tagHelper.BoundAttributes[j].Name == valueAttributeName)
{
valueAttribute = tagHelper.BoundAttributes[j];
}
if (tagHelper.BoundAttributes[j].Name == expressionAttributeName)

View File

@ -395,33 +395,28 @@ namespace Test
Assert.False(attribute.IsBooleanProperty);
Assert.False(attribute.IsEnum);
attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format"));
var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format"));
// Invariants
Assert.Empty(attribute.Diagnostics);
Assert.False(attribute.HasErrors);
Assert.Equal(ComponentMetadata.Bind.TagHelperKind, attribute.Kind);
Assert.False(attribute.IsDefaultKind());
Assert.False(attribute.HasIndexer);
Assert.Null(attribute.IndexerNamePrefix);
Assert.Null(attribute.IndexerTypeName);
Assert.False(attribute.IsIndexerBooleanProperty);
Assert.False(attribute.IsIndexerStringProperty);
Assert.Empty(parameter.Diagnostics);
Assert.False(parameter.HasErrors);
Assert.Equal(ComponentMetadata.Bind.TagHelperKind, parameter.Kind);
Assert.False(parameter.IsDefaultKind());
Assert.Equal(
"Specifies a format to convert the value specified by the 'bind' attribute. " +
"The format string can currently only be used with expressions of type <code>DateTime</code>.",
attribute.Documentation);
parameter.Documentation);
Assert.Equal("format-myprop", attribute.Name);
Assert.Equal("Format_myprop", attribute.GetPropertyName());
Assert.Equal("string Test.BindAttributes.Format_myprop", attribute.DisplayName);
Assert.Equal("format", parameter.Name);
Assert.Equal("Format_myprop", parameter.GetPropertyName());
Assert.Equal(":format", parameter.DisplayName);
// Defined from the property type
Assert.Equal("System.String", attribute.TypeName);
Assert.True(attribute.IsStringProperty);
Assert.False(attribute.IsBooleanProperty);
Assert.False(attribute.IsEnum);
Assert.Equal("System.String", parameter.TypeName);
Assert.True(parameter.IsStringProperty);
Assert.False(parameter.IsBooleanProperty);
Assert.False(parameter.IsEnum);
}
[Fact]
@ -529,10 +524,10 @@ namespace Test
Assert.Equal("Bind", attribute.GetPropertyName());
Assert.Equal("object Test.BindAttributes.Bind", attribute.DisplayName);
attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format"));
Assert.Equal("format-myprop", attribute.Name);
Assert.Equal("Format_myprop", attribute.GetPropertyName());
Assert.Equal("string Test.BindAttributes.Format_myprop", attribute.DisplayName);
var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format"));
Assert.Equal("format", parameter.Name);
Assert.Equal("Format_myprop", parameter.GetPropertyName());
Assert.Equal(":format", parameter.DisplayName);
}
[Fact]
@ -597,10 +592,10 @@ namespace Test
Assert.Equal("Bind", attribute.GetPropertyName());
Assert.Equal("object Test.BindAttributes.Bind", attribute.DisplayName);
attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format"));
Assert.Equal("format-myprop", attribute.Name);
Assert.Equal("Format_myprop", attribute.GetPropertyName());
Assert.Equal("string Test.BindAttributes.Format_myprop", attribute.DisplayName);
var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format"));
Assert.Equal("format", parameter.Name);
Assert.Equal("Format_myprop", parameter.GetPropertyName());
Assert.Equal(":format", parameter.DisplayName);
}
[Fact]
@ -665,10 +660,10 @@ namespace Test
Assert.Equal("Bind_somevalue", attribute.GetPropertyName());
Assert.Equal("object Test.BindAttributes.Bind_somevalue", attribute.DisplayName);
attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format"));
Assert.Equal("format-somevalue", attribute.Name);
Assert.Equal("Format_somevalue", attribute.GetPropertyName());
Assert.Equal("string Test.BindAttributes.Format_somevalue", attribute.DisplayName);
var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format"));
Assert.Equal("format", parameter.Name);
Assert.Equal("Format_somevalue", parameter.GetPropertyName());
Assert.Equal(":format", parameter.DisplayName);
}
[Fact]
@ -710,7 +705,7 @@ namespace Test
Assert.Equal(
"Binds the provided expression to an attribute and a change event, based on the naming of " +
"the bind attribute. For example: <code>bind-value-onchange=\"...\"</code> will assign the " +
"the bind attribute. For example: <code>bind-value=\"...\"</code> and <code>bind-value:event=\"onchange\"</code> will assign the " +
"current value of the expression to the 'value' attribute, and assign a delegate that attempts " +
"to set the value to the 'onchange' attribute.",
bind.Documentation);
@ -753,7 +748,7 @@ namespace Test
Assert.Equal(
"Binds the provided expression to an attribute and a change event, based on the naming of " +
"the bind attribute. For example: <code>bind-value-onchange=\"...\"</code> will assign the " +
"the bind attribute. For example: <code>bind-value=\"...\"</code> and <code>bind-value:event=\"onchange\"</code> will assign the " +
"current value of the expression to the 'value' attribute, and assign a delegate that attempts " +
"to set the value to the 'onchange' attribute.",
attribute.Documentation);
@ -770,37 +765,30 @@ namespace Test
Assert.False(attribute.IsBooleanProperty);
Assert.False(attribute.IsEnum);
attribute = Assert.Single(bind.BoundAttributes, a => a.Name.StartsWith("format"));
var parameter = Assert.Single(attribute.BoundAttributeParameters, a => a.Name.Equals("format"));
// Invariants
Assert.Empty(attribute.Diagnostics);
Assert.False(attribute.HasErrors);
Assert.Equal(ComponentMetadata.Bind.TagHelperKind, attribute.Kind);
Assert.False(attribute.IsDefaultKind());
Assert.True(attribute.HasIndexer);
Assert.Equal("format-", attribute.IndexerNamePrefix);
Assert.Equal("System.String", attribute.IndexerTypeName);
Assert.False(attribute.IsIndexerBooleanProperty);
Assert.True(attribute.IsIndexerStringProperty);
Assert.Empty(parameter.Diagnostics);
Assert.False(parameter.HasErrors);
Assert.Equal(ComponentMetadata.Bind.TagHelperKind, parameter.Kind);
Assert.False(parameter.IsDefaultKind());
Assert.Equal(
"Specifies a format to convert the value specified by the corresponding bind attribute. " +
"For example: <code>format-value=\"...\"</code> will apply a format string to the value " +
"specified in <code>bind-value-...</code>. The format string can currently only be used with " +
"For example: <code>bind-value:format=\"...\"</code> will apply a format string to the value " +
"specified in <code>bind-value=\"...\"</code>. The format string can currently only be used with " +
"expressions of type <code>DateTime</code>.",
attribute.Documentation);
parameter.Documentation);
Assert.Equal("format-...", attribute.Name);
Assert.Equal("Format", attribute.GetPropertyName());
Assert.Equal(
"System.Collections.Generic.Dictionary<string, string> Microsoft.AspNetCore.Components.Bind.Format",
attribute.DisplayName);
Assert.Equal("format", parameter.Name);
Assert.Equal("Format", parameter.GetPropertyName());
Assert.Equal(":format", parameter.DisplayName);
// Defined from the property type
Assert.Equal("System.Collections.Generic.Dictionary<string, string>", attribute.TypeName);
Assert.False(attribute.IsStringProperty);
Assert.False(attribute.IsBooleanProperty);
Assert.False(attribute.IsEnum);
Assert.Equal("System.String", parameter.TypeName);
Assert.True(parameter.IsStringProperty);
Assert.False(parameter.IsBooleanProperty);
Assert.False(parameter.IsEnum);
}

View File

@ -128,6 +128,11 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
WriteContentNode(node, node.AttributeName, string.Format("HtmlAttributeValueStyle.{0}", node.AttributeStructure));
}
public override void VisitTagHelperAttributeParameter(TagHelperAttributeParameterIntermediateNode node)
{
WriteContentNode(node, node.AttributeName, string.Format("HtmlAttributeValueStyle.{0}", node.AttributeStructure));
}
public override void VisitComponent(ComponentIntermediateNode node)
{
WriteContentNode(node, node.TagName);