Add support for get-only indexer properties
- #399 - move invalid `HtmlAttributeNameAttribute.Name` checking to `TagHelperDescriptorFactory` - add a few new error cases - but does not cover all the new error cases e.g. `[HtmlAttributeName(...)]` on a get-only `int` property nit: - `resx` target removed some older resources from `RazorResources.Designer.cs`
This commit is contained in:
parent
1f480386f4
commit
69d8e52bf9
|
|
@ -234,6 +234,22 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
return GetString("TagHelperDescriptorFactory_Tag");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameAttribute
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameAttribute"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameAttribute(object p0, object p1, object p2, object p3)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameAttribute"), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
|
||||
/// </summary>
|
||||
|
|
@ -283,19 +299,67 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefix
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefix"); }
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(object p0, object p1, object p2, object p3)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNull
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefix(object p0, object p1, object p2, object p3, object p4)
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNotNull
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefix"), p0, p1, p2, p3, p4);
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -159,6 +159,9 @@
|
|||
<data name="TagHelperDescriptorFactory_Tag" xml:space="preserve">
|
||||
<value>Tag</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameAttribute" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.</value>
|
||||
</data>
|
||||
|
|
@ -168,7 +171,16 @@
|
|||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributePrefix" xml:space="preserve">
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNull" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNotNull" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.</value>
|
||||
</data>
|
||||
<data name="TagHelperAttributeList_CannotAddWithNullName" xml:space="preserve">
|
||||
|
|
|
|||
|
|
@ -13,23 +13,41 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
private string _dictionaryAttributePrefix;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="HtmlAttributeNameAttribute"/> class with <see cref="Name"/>
|
||||
/// equal to <c>null</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Associated property must not have a public setter and must be compatible with
|
||||
/// <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/> where <c>TKey</c> is
|
||||
/// <see cref="string"/>.
|
||||
/// </remarks>
|
||||
public HtmlAttributeNameAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="HtmlAttributeNameAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">HTML attribute name for the associated property.</param>
|
||||
/// <param name="name">
|
||||
/// HTML attribute name for the associated property. Must be <c>null</c> or empty if associated property does
|
||||
/// not have a public setter and is compatible with
|
||||
/// <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/> where <c>TKey</c> is
|
||||
/// <see cref="string"/>. Otherwise must not be <c>null</c> or empty.
|
||||
/// </param>
|
||||
public HtmlAttributeNameAttribute(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(name));
|
||||
}
|
||||
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTML attribute name of the associated property.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>null</c> or empty if and only if associated property does not have a public setter and is compatible
|
||||
/// with <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/> where <c>TKey</c> is
|
||||
/// <see cref="string"/>.
|
||||
/// </value>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -42,9 +60,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <see cref="string"/>.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// <para>
|
||||
/// If associated property is compatible with
|
||||
/// <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/>, default value is <c>Name + "-"</c>.
|
||||
/// <see cref="Name"/> must not be <c>null</c> or empty in this case.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Otherwise default value is <c>null</c>.
|
||||
/// </para>
|
||||
/// </value>
|
||||
public string DictionaryAttributePrefix
|
||||
{
|
||||
|
|
|
|||
|
|
@ -256,45 +256,72 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
bool designTime,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty);
|
||||
var attributeDescriptors = new List<TagHelperAttributeDescriptor>();
|
||||
|
||||
// Keep indexer descriptors separate to avoid sorting the combined list later.
|
||||
var indexerDescriptors = new List<TagHelperAttributeDescriptor>();
|
||||
|
||||
var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty);
|
||||
foreach (var property in accessibleProperties)
|
||||
{
|
||||
var attributeNameAttribute = property.GetCustomAttribute<HtmlAttributeNameAttribute>(inherit: false);
|
||||
var descriptor = ToAttributeDescriptor(property, attributeNameAttribute, designTime);
|
||||
if (ValidateTagHelperAttributeDescriptor(descriptor, type, errorSink))
|
||||
var hasExplicitName =
|
||||
attributeNameAttribute != null && !string.IsNullOrEmpty(attributeNameAttribute.Name);
|
||||
var attributeName = hasExplicitName ? attributeNameAttribute.Name : ToHtmlCase(property.Name);
|
||||
|
||||
TagHelperAttributeDescriptor mainDescriptor = null;
|
||||
if (property.SetMethod?.IsPublic == true)
|
||||
{
|
||||
bool isInvalid;
|
||||
var indexerDescriptor = ToIndexerAttributeDescriptor(
|
||||
property,
|
||||
attributeNameAttribute,
|
||||
parentType: type,
|
||||
errorSink: errorSink,
|
||||
defaultPrefix: descriptor.Name + "-",
|
||||
designTime: designTime,
|
||||
isInvalid: out isInvalid);
|
||||
|
||||
if (indexerDescriptor != null &&
|
||||
!ValidateTagHelperAttributeDescriptor(indexerDescriptor, type, errorSink))
|
||||
mainDescriptor = ToAttributeDescriptor(property, attributeName, designTime);
|
||||
if (!ValidateTagHelperAttributeDescriptor(mainDescriptor, type, errorSink))
|
||||
{
|
||||
isInvalid = true;
|
||||
}
|
||||
|
||||
if (isInvalid)
|
||||
{
|
||||
// HtmlAttributeNameAttribute was not valid. Ignore this property completely.
|
||||
// HtmlAttributeNameAttribute.Name is invalid. Ignore this property completely.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (hasExplicitName)
|
||||
{
|
||||
// Specified HtmlAttributeNameAttribute.Name though property has no public setter.
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(
|
||||
type.FullName,
|
||||
property.Name,
|
||||
typeof(HtmlAttributeNameAttribute).FullName,
|
||||
nameof(HtmlAttributeNameAttribute.Name)));
|
||||
continue;
|
||||
}
|
||||
|
||||
attributeDescriptors.Add(descriptor);
|
||||
if (indexerDescriptor != null)
|
||||
{
|
||||
indexerDescriptors.Add(indexerDescriptor);
|
||||
}
|
||||
bool isInvalid;
|
||||
var indexerDescriptor = ToIndexerAttributeDescriptor(
|
||||
property,
|
||||
attributeNameAttribute,
|
||||
parentType: type,
|
||||
errorSink: errorSink,
|
||||
defaultPrefix: attributeName + "-",
|
||||
designTime: designTime,
|
||||
isInvalid: out isInvalid);
|
||||
if (indexerDescriptor != null &&
|
||||
!ValidateTagHelperAttributeDescriptor(indexerDescriptor, type, errorSink))
|
||||
{
|
||||
isInvalid = true;
|
||||
}
|
||||
|
||||
if (isInvalid)
|
||||
{
|
||||
// The property type or HtmlAttributeNameAttribute.DictionaryAttributePrefix (or perhaps the
|
||||
// HTML-casing of the property name) is invalid. Ignore this property completely.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mainDescriptor != null)
|
||||
{
|
||||
attributeDescriptors.Add(mainDescriptor);
|
||||
}
|
||||
|
||||
if (indexerDescriptor != null)
|
||||
{
|
||||
indexerDescriptors.Add(indexerDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -309,9 +336,26 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
Type parentType,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var nameOrPrefix = attributeDescriptor.IsIndexer ?
|
||||
Resources.TagHelperDescriptorFactory_Prefix :
|
||||
Resources.TagHelperDescriptorFactory_Name;
|
||||
string nameOrPrefix;
|
||||
if (attributeDescriptor.IsIndexer)
|
||||
{
|
||||
nameOrPrefix = Resources.TagHelperDescriptorFactory_Prefix;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(attributeDescriptor.Name))
|
||||
{
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(
|
||||
parentType.FullName,
|
||||
attributeDescriptor.PropertyName));
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
nameOrPrefix = Resources.TagHelperDescriptorFactory_Name;
|
||||
}
|
||||
|
||||
return ValidateTagHelperAttributeNameOrPrefix(
|
||||
attributeDescriptor.Name,
|
||||
parentType,
|
||||
|
|
@ -329,8 +373,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
if (string.IsNullOrEmpty(attributeNameOrPrefix))
|
||||
{
|
||||
// HtmlAttributeNameAttribute validates Name is non-null and non-empty. Both are valid for
|
||||
// DictionaryAttributePrefix. (Empty DictionaryAttributePrefix is a corner case which would bind every
|
||||
// ValidateTagHelperAttributeDescriptor validates Name is non-null and non-empty. The empty string is
|
||||
// valid for DictionaryAttributePrefix and null is impossible at this point because it means "don't
|
||||
// create a descriptor". (Empty DictionaryAttributePrefix is a corner case which would bind every
|
||||
// attribute of a target element. Likely not particularly useful but unclear what minimum length
|
||||
// should be required and what scenarios a minimum length would break.)
|
||||
return true;
|
||||
|
|
@ -388,13 +433,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
private static TagHelperAttributeDescriptor ToAttributeDescriptor(
|
||||
PropertyInfo property,
|
||||
HtmlAttributeNameAttribute attributeNameAttribute,
|
||||
string attributeName,
|
||||
bool designTime)
|
||||
{
|
||||
var attributeName = attributeNameAttribute != null ?
|
||||
attributeNameAttribute.Name :
|
||||
ToHtmlCase(property.Name);
|
||||
|
||||
return ToAttributeDescriptor(
|
||||
property,
|
||||
attributeName,
|
||||
|
|
@ -413,6 +454,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
out bool isInvalid)
|
||||
{
|
||||
isInvalid = false;
|
||||
var hasPublicSetter = property.SetMethod?.IsPublic == true;
|
||||
var dictionaryTypeArguments = ClosedGenericMatcher.ExtractGenericInterface(
|
||||
property.PropertyType,
|
||||
typeof(IDictionary<,>))
|
||||
|
|
@ -426,13 +468,44 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
isInvalid = true;
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefix(
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(
|
||||
parentType.FullName,
|
||||
property.Name,
|
||||
nameof(HtmlAttributeNameAttribute),
|
||||
nameof(HtmlAttributeNameAttribute.DictionaryAttributePrefix),
|
||||
"IDictionary<string, TValue>"));
|
||||
}
|
||||
else if (attributeNameAttribute != null && !hasPublicSetter)
|
||||
{
|
||||
// Associated an HtmlAttributeNameAttribute with a non-dictionary property that lacks a public
|
||||
// setter.
|
||||
isInvalid = true;
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameAttribute(
|
||||
parentType.FullName,
|
||||
property.Name,
|
||||
nameof(HtmlAttributeNameAttribute),
|
||||
"IDictionary<string, TValue>"));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
else if (!hasPublicSetter &&
|
||||
attributeNameAttribute != null &&
|
||||
!attributeNameAttribute.DictionaryAttributePrefixSet)
|
||||
{
|
||||
// Must set DictionaryAttributePrefix when using HtmlAttributeNameAttribute with a dictionary property
|
||||
// that lacks a public setter.
|
||||
isInvalid = true;
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(
|
||||
parentType.FullName,
|
||||
property.Name,
|
||||
nameof(HtmlAttributeNameAttribute),
|
||||
nameof(HtmlAttributeNameAttribute.DictionaryAttributePrefix),
|
||||
"IDictionary<string, TValue>"));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -482,9 +555,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
private static bool IsAccessibleProperty(PropertyInfo property)
|
||||
{
|
||||
// Accessible properties are those with public getters and setters and without [HtmlAttributeNotBound].
|
||||
// Accessible properties are those with public getters and without [HtmlAttributeNotBound].
|
||||
return property.GetMethod?.IsPublic == true &&
|
||||
property.SetMethod?.IsPublic == true &&
|
||||
property.GetCustomAttribute<HtmlAttributeNotBoundAttribute>(inherit: false) == null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1286,22 +1286,6 @@ namespace Microsoft.AspNet.Razor
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_CannotHaveCSharpInTagDeclaration"), p0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A TagHelperChunkGenerator must only be used with TagHelperBlocks.
|
||||
/// </summary>
|
||||
internal static string TagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock
|
||||
{
|
||||
get { return GetString("TagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A TagHelperChunkGenerator must only be used with TagHelperBlocks.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock()
|
||||
{
|
||||
return GetString("TagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Directive '{0}' must have a value.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -958,7 +958,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
isStringProperty: false,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "valid-prefix",
|
||||
name: "valid-name-",
|
||||
propertyName: nameof(SingleValidHtmlAttributePrefix.DictionaryProperty),
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: true,
|
||||
|
|
@ -1048,6 +1048,20 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
isIndexer: true,
|
||||
isStringProperty: false,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "get-only-dictionary-property-",
|
||||
propertyName: nameof(MultipleValidHtmlAttributePrefix.GetOnlyDictionaryProperty),
|
||||
typeName: typeof(int).FullName,
|
||||
isIndexer: true,
|
||||
isStringProperty: false,
|
||||
designTimeDescriptor: null),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "valid-prefix6",
|
||||
propertyName: nameof(MultipleValidHtmlAttributePrefix.GetOnlyDictionaryPropertyWithAttributePrefix),
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: true,
|
||||
isStringProperty: true,
|
||||
designTimeDescriptor: null),
|
||||
},
|
||||
new string[0]
|
||||
},
|
||||
|
|
@ -1087,6 +1101,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
onError(
|
||||
typeof(MultipleInvalidHtmlAttributePrefix).FullName,
|
||||
nameof(MultipleInvalidHtmlAttributePrefix.DictionaryOfIntSubclassProperty)),
|
||||
onError(
|
||||
typeof(MultipleInvalidHtmlAttributePrefix).FullName,
|
||||
nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryAttributePrefix)),
|
||||
$"Invalid tag helper bound property '{ typeof(MultipleInvalidHtmlAttributePrefix).FullName }." +
|
||||
$"{ nameof(MultipleInvalidHtmlAttributePrefix.GetOnlyDictionaryPropertyWithAttributeName) }'. " +
|
||||
$"'{ typeof(HtmlAttributeNameAttribute).FullName }." +
|
||||
$"{ nameof(HtmlAttributeNameAttribute.Name) }' must be null or empty if property has " +
|
||||
"no public setter.",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -1128,14 +1150,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
TagHelperAttributeDescriptorComparer.Default);
|
||||
}
|
||||
|
||||
public static TheoryData<string> ValidAttributeNameOrPrefixData
|
||||
public static TheoryData<string> ValidAttributeNameData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
null,
|
||||
string.Empty,
|
||||
"data",
|
||||
"dataa-",
|
||||
"ValidName",
|
||||
|
|
@ -1147,7 +1167,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ValidAttributeNameOrPrefixData))]
|
||||
[MemberData(nameof(ValidAttributeNameData))]
|
||||
public void ValidateTagHelperAttributeDescriptor_WithValidName_ReturnsTrue(string name)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -1170,8 +1190,25 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
Assert.Empty(errorSink.Errors);
|
||||
}
|
||||
|
||||
public static TheoryData<string> ValidAttributePrefixData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
string.Empty,
|
||||
"data",
|
||||
"dataa-",
|
||||
"ValidName",
|
||||
"valid-name",
|
||||
"--valid--name--",
|
||||
",,--__..oddly.valid::;;",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ValidAttributeNameOrPrefixData))]
|
||||
[MemberData(nameof(ValidAttributePrefixData))]
|
||||
public void ValidateTagHelperAttributeDescriptor_WithValidPrefix_ReturnsTrue(string prefix)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -1732,7 +1769,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
private class SingleValidHtmlAttributePrefix : TagHelper
|
||||
{
|
||||
[HtmlAttributeName("valid-name", DictionaryAttributePrefix = "valid-prefix")]
|
||||
[HtmlAttributeName("valid-name")]
|
||||
public IDictionary<string, string> DictionaryProperty { get; set; }
|
||||
}
|
||||
|
||||
|
|
@ -1755,6 +1792,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
[HtmlAttributeName("valid-name6")]
|
||||
public string StringProperty { get; set; }
|
||||
|
||||
public IDictionary<string, int> GetOnlyDictionaryProperty { get; }
|
||||
|
||||
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
|
||||
public IDictionary<string, string> GetOnlyDictionaryPropertyWithAttributePrefix { get; }
|
||||
}
|
||||
|
||||
private class SingleInvalidHtmlAttributePrefix : TagHelper
|
||||
|
|
@ -1779,6 +1821,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
[HtmlAttributeName("valid-name5", DictionaryAttributePrefix = "valid-prefix5-")]
|
||||
public DictionaryOfIntSubclass DictionaryOfIntSubclassProperty { get; set; }
|
||||
|
||||
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
|
||||
public IDictionary<int, string> GetOnlyDictionaryAttributePrefix { get; }
|
||||
|
||||
[HtmlAttributeName("invalid-name7")]
|
||||
public IDictionary<string, object> GetOnlyDictionaryPropertyWithAttributeName { get; }
|
||||
}
|
||||
|
||||
private class DictionarySubclass : Dictionary<string, string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue