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:
parent
13397dd4d6
commit
5bad5de7ee
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <code>DateTime</code>.</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: <code>bind-value-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.</value>
|
||||
<value>Binds the provided expression to an attribute and a change event, based on the naming of 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.</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: <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 expressions of type <code>DateTime</code>.</value>
|
||||
<value>Specifies a format to convert the value specified by the corresponding bind attribute. 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>.</value>
|
||||
</data>
|
||||
<data name="ChildContentParameterName_Documentation" xml:space="preserve">
|
||||
<value>Specifies the parameter name for the '{0}' child content expression.</value>
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
public enum RazorDiagnosticSeverity
|
||||
{
|
||||
// Purposely using the same value as Roslyn here.
|
||||
Warning = 2,
|
||||
Error = 3,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace Test
|
|||
,
|
||||
#nullable restore
|
||||
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
|
||||
Format
|
||||
Format
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Test
|
|||
,
|
||||
#nullable restore
|
||||
#line 1 "x:\dir\subdir\Test\TestComponent.cshtml"
|
||||
Format
|
||||
Format
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue