Add `[HtmlAttributeName(..., DictionaryAttributePrefix="prefix")]` part III
- #89 remainder - support adding attributes (that aren't otherwise bound) to a tag helper dictionary - use two `TagHelperAttributeDescriptor`s for dictionary and indexer - most exising descriptor properties have two meanings depending on new `IsIndexer` - add `TagHelperAttributeDescriptor.IsNameMatch()` - create no `TagHelperAttributeDescriptor`s if property name or `HtmlAttributeNameAttribute` is invalid - avoid corner case misfeatures where invalidity removes just one descriptor - extend handling of invalid attribute names to include `[HtmlAttributeName]` - handle prefix matches in `TagHelperBlockRewriter` - add parse error when resolved dictionary key is `string.Empty` - generate code for indexer property assignments - add code generation for runtime error if using indexer when property is `null` - use new `GeneratedTagHelperContext.FormatInvalidIndexerAssignmentMethodName` for message - code generation now handles attributes in source order; thus above errors occur only when expected if dictionary is also initialized in the Razor source - surprisingly generation order change did not break existing tests! nits: - improve `TagHelperDescriptorFactory_InvalidBoundAttributeName` wording - rename resource to `TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart` - correct order of arguments to `FormatTagHelperDescriptorFactory_InvalidBoundAttributeName` - correct `TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed` resource - correct `TagHelperDescriptorFactoryTest` test names - remove a few unnecessary `ToArray()` calls - update `TagHelperAttributeDescriptorComparer` comments
This commit is contained in:
parent
1879ac6427
commit
60c47c8874
|
|
@ -186,6 +186,38 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
return GetString("TagHelperDescriptorFactory_Attribute");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// name
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_Name
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_Name"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// name
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_Name()
|
||||
{
|
||||
return GetString("TagHelperDescriptorFactory_Name");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// prefix
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_Prefix
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_Prefix"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// prefix
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_Prefix()
|
||||
{
|
||||
return GetString("TagHelperDescriptorFactory_Prefix");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tag
|
||||
/// </summary>
|
||||
|
|
@ -203,19 +235,67 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes beginning with '{2}'.
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidBoundAttributeName
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidBoundAttributeName"); }
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes beginning with '{2}'.
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidBoundAttributeName(object p0, object p1, object p2)
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidBoundAttributeName"), p0, p1, p2);
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefix
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefix"); }
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefix"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
|
@ -150,11 +150,26 @@
|
|||
<data name="TagHelperDescriptorFactory_Attribute" xml:space="preserve">
|
||||
<value>Attribute</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_Name" xml:space="preserve">
|
||||
<value>name</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_Prefix" xml:space="preserve">
|
||||
<value>prefix</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_Tag" xml:space="preserve">
|
||||
<value>Tag</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidBoundAttributeName" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes beginning with '{2}'.</value>
|
||||
<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>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart" xml:space="preserve">
|
||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.</value>
|
||||
</data>
|
||||
<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">
|
||||
<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">
|
||||
<value>Cannot add a '{0}' with a null '{1}'.</value>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class HtmlAttributeNameAttribute : Attribute
|
||||
{
|
||||
private string _dictionaryAttributePrefix;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="HtmlAttributeNameAttribute"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -29,5 +31,39 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// HTML attribute name of the associated property.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the prefix used to match HTML attribute names. Matching attributes are added to the
|
||||
/// associated property (an <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/>).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If non-<c>null</c> associated property must be compatible with
|
||||
/// <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/> where <c>TKey</c> is
|
||||
/// <see cref="string"/>.
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// If associated property is compatible with
|
||||
/// <see cref="System.Collections.Generic.IDictionary{TKey, TValue}"/>, default value is <c>Name + "-"</c>.
|
||||
/// Otherwise default value is <c>null</c>.
|
||||
/// </value>
|
||||
public string DictionaryAttributePrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dictionaryAttributePrefix;
|
||||
}
|
||||
set
|
||||
{
|
||||
_dictionaryAttributePrefix = value;
|
||||
DictionaryAttributePrefixSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication whether <see cref="DictionaryAttributePrefix"/> has been set. Used to distinguish an
|
||||
/// uninitialized <see cref="DictionaryAttributePrefix"/> value from an explicit <c>null</c> setting.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if <see cref="DictionaryAttributePrefix"/> was set. <c>false</c> otherwise.</value>
|
||||
public bool DictionaryAttributePrefixSet { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
/// <param name="assemblyName">The assembly name that contains <paramref name="type"/>.</param>
|
||||
/// <param name="type">The type to create a <see cref="TagHelperDescriptor"/> from.</param>
|
||||
/// <returns>A <see cref="TagHelperDescriptor"/> that describes the given <paramref name="type"/>.</returns>
|
||||
/// <returns>
|
||||
/// A collection of <see cref="TagHelperDescriptor"/>s that describe the given <paramref name="type"/>.
|
||||
/// </returns>
|
||||
public static IEnumerable<TagHelperDescriptor> CreateDescriptors(
|
||||
string assemblyName,
|
||||
[NotNull] Type type,
|
||||
|
|
@ -209,48 +211,195 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
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>();
|
||||
|
||||
foreach (var property in accessibleProperties)
|
||||
{
|
||||
var descriptor = ToAttributeDescriptor(property);
|
||||
var attributeNameAttribute = property.GetCustomAttribute<HtmlAttributeNameAttribute>(inherit: false);
|
||||
var descriptor = ToAttributeDescriptor(property, attributeNameAttribute);
|
||||
if (ValidateTagHelperAttributeDescriptor(descriptor, type, errorSink))
|
||||
{
|
||||
bool isInvalid;
|
||||
var indexerDescriptor = ToIndexerAttributeDescriptor(
|
||||
property,
|
||||
attributeNameAttribute,
|
||||
parentType: type,
|
||||
errorSink: errorSink,
|
||||
defaultPrefix: descriptor.Name + "-",
|
||||
isInvalid: out isInvalid);
|
||||
|
||||
if (indexerDescriptor != null &&
|
||||
!ValidateTagHelperAttributeDescriptor(indexerDescriptor, type, errorSink))
|
||||
{
|
||||
isInvalid = true;
|
||||
}
|
||||
|
||||
if (isInvalid)
|
||||
{
|
||||
// HtmlAttributeNameAttribute was not valid. Ignore this property completely.
|
||||
continue;
|
||||
}
|
||||
|
||||
attributeDescriptors.Add(descriptor);
|
||||
if (indexerDescriptor != null)
|
||||
{
|
||||
indexerDescriptors.Add(indexerDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attributeDescriptors.AddRange(indexerDescriptors);
|
||||
|
||||
return attributeDescriptors;
|
||||
}
|
||||
|
||||
private static bool ValidateTagHelperAttributeDescriptor(
|
||||
// Internal for testing.
|
||||
internal static bool ValidateTagHelperAttributeDescriptor(
|
||||
TagHelperAttributeDescriptor attributeDescriptor,
|
||||
Type parentType,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var nameOrPrefix = attributeDescriptor.IsIndexer ?
|
||||
Resources.TagHelperDescriptorFactory_Prefix :
|
||||
Resources.TagHelperDescriptorFactory_Name;
|
||||
return ValidateTagHelperAttributeNameOrPrefix(
|
||||
attributeDescriptor.Name,
|
||||
parentType,
|
||||
attributeDescriptor.PropertyName,
|
||||
errorSink,
|
||||
nameOrPrefix);
|
||||
}
|
||||
|
||||
private static bool ValidateTagHelperAttributeNameOrPrefix(
|
||||
string attributeNameOrPrefix,
|
||||
Type parentType,
|
||||
string propertyName,
|
||||
ErrorSink errorSink,
|
||||
string nameOrPrefix)
|
||||
{
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(attributeNameOrPrefix))
|
||||
{
|
||||
// Provide a single error if the entire name is whitespace, not an error per character.
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(
|
||||
parentType.FullName,
|
||||
propertyName,
|
||||
nameOrPrefix));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// data-* attributes are explicitly not implemented by user agents and are not intended for use on
|
||||
// the server; therefore it's invalid for TagHelpers to bind to them.
|
||||
if (attributeDescriptor.Name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
if (attributeNameOrPrefix.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidBoundAttributeName(
|
||||
attributeDescriptor.PropertyName,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart(
|
||||
parentType.FullName,
|
||||
propertyName,
|
||||
nameOrPrefix,
|
||||
attributeNameOrPrefix,
|
||||
DataDashPrefix));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
var isValid = true;
|
||||
foreach (var character in attributeNameOrPrefix)
|
||||
{
|
||||
if (char.IsWhiteSpace(character) || InvalidNonWhitespaceNameCharacters.Contains(character))
|
||||
{
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter(
|
||||
parentType.FullName,
|
||||
propertyName,
|
||||
nameOrPrefix,
|
||||
attributeNameOrPrefix,
|
||||
character));
|
||||
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private static TagHelperAttributeDescriptor ToAttributeDescriptor(PropertyInfo property)
|
||||
private static TagHelperAttributeDescriptor ToAttributeDescriptor(
|
||||
PropertyInfo property,
|
||||
HtmlAttributeNameAttribute attributeNameAttribute)
|
||||
{
|
||||
var attributeNameAttribute = property.GetCustomAttribute<HtmlAttributeNameAttribute>(inherit: false);
|
||||
var attributeName = attributeNameAttribute != null ?
|
||||
attributeNameAttribute.Name :
|
||||
ToHtmlCase(property.Name);
|
||||
|
||||
return new TagHelperAttributeDescriptor(attributeName, property.Name, property.PropertyType.FullName);
|
||||
return new TagHelperAttributeDescriptor(
|
||||
attributeName,
|
||||
property.Name,
|
||||
property.PropertyType.FullName,
|
||||
isIndexer: false);
|
||||
}
|
||||
|
||||
private static TagHelperAttributeDescriptor ToIndexerAttributeDescriptor(
|
||||
PropertyInfo property,
|
||||
HtmlAttributeNameAttribute attributeNameAttribute,
|
||||
Type parentType,
|
||||
ErrorSink errorSink,
|
||||
string defaultPrefix,
|
||||
out bool isInvalid)
|
||||
{
|
||||
isInvalid = false;
|
||||
var dictionaryTypeArguments = ClosedGenericMatcher.ExtractGenericInterface(
|
||||
property.PropertyType,
|
||||
typeof(IDictionary<,>))
|
||||
?.GenericTypeArguments;
|
||||
if (dictionaryTypeArguments?[0] != typeof(string))
|
||||
{
|
||||
if (attributeNameAttribute?.DictionaryAttributePrefix != null)
|
||||
{
|
||||
// DictionaryAttributePrefix is not supported unless associated with an
|
||||
// IDictionary<string, TValue> property.
|
||||
isInvalid = true;
|
||||
errorSink.OnError(
|
||||
SourceLocation.Zero,
|
||||
Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefix(
|
||||
parentType.FullName,
|
||||
property.Name,
|
||||
nameof(HtmlAttributeNameAttribute),
|
||||
nameof(HtmlAttributeNameAttribute.DictionaryAttributePrefix),
|
||||
"IDictionary<string, TValue>"));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Potential prefix case. Use default prefix (based on name)?
|
||||
var useDefault = attributeNameAttribute == null || !attributeNameAttribute.DictionaryAttributePrefixSet;
|
||||
|
||||
var prefix = useDefault ? defaultPrefix : attributeNameAttribute.DictionaryAttributePrefix;
|
||||
if (prefix == null)
|
||||
{
|
||||
// DictionaryAttributePrefix explicitly set to null. Ignore.
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TagHelperAttributeDescriptor(
|
||||
name: prefix,
|
||||
propertyName: property.Name,
|
||||
typeName: dictionaryTypeArguments[1].FullName,
|
||||
isIndexer: true);
|
||||
}
|
||||
|
||||
private static bool IsAccessibleProperty(PropertyInfo property)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"dependencies": {
|
||||
"Microsoft.AspNet.Razor": "4.0.0-*",
|
||||
"Microsoft.Framework.BufferEntryCollection.Sources": { "type": "build", "version": "1.0.0-*" },
|
||||
"Microsoft.Framework.ClosedGenericMatcher.Sources": { "type": "build", "version": "1.0.0-*" },
|
||||
"Microsoft.Framework.CopyOnWriteDictionary.Sources": { "type": "build", "version": "1.0.0-*" },
|
||||
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -69,10 +69,9 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
|
||||
// Determine what attributes exist in the element and divide them up.
|
||||
var htmlAttributes = chunk.Attributes;
|
||||
var attributeDescriptors = tagHelperDescriptors.SelectMany(descriptor => descriptor.Attributes);
|
||||
var unboundHtmlAttributes = htmlAttributes.Where(
|
||||
attribute => !attributeDescriptors.Any(
|
||||
descriptor => string.Equals(attribute.Key, descriptor.Name, StringComparison.OrdinalIgnoreCase)));
|
||||
var attributeDescriptors = tagHelperDescriptors.SelectMany(descriptor => descriptor.Attributes).ToArray();
|
||||
var unboundHtmlAttributes = htmlAttributes.Where(attribute => !attributeDescriptors.Any(
|
||||
attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Key)));
|
||||
|
||||
RenderUnboundHTMLAttributes(unboundHtmlAttributes);
|
||||
|
||||
|
|
@ -182,6 +181,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
// Render all of the bound attribute values for the tag helper.
|
||||
RenderBoundHTMLAttributes(
|
||||
chunk.Attributes,
|
||||
tagHelperDescriptor.TypeName,
|
||||
tagHelperVariableName,
|
||||
tagHelperDescriptor.Attributes,
|
||||
htmlAttributeValues);
|
||||
|
|
@ -190,81 +190,126 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
|
||||
private void RenderBoundHTMLAttributes(
|
||||
IList<KeyValuePair<string, Chunk>> chunkAttributes,
|
||||
string tagHelperTypeName,
|
||||
string tagHelperVariableName,
|
||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||
Dictionary<string, string> htmlAttributeValues)
|
||||
{
|
||||
foreach (var attributeDescriptor in attributeDescriptors)
|
||||
// Track dictionary properties we have confirmed are non-null.
|
||||
var confirmedDictionaries = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
// First attribute wins, even if there are duplicates.
|
||||
var distinctAttributes = chunkAttributes.Distinct(KeyValuePairKeyComparer.Default);
|
||||
|
||||
// Go through the HTML attributes in source order, assigning to properties or indexers as we go.
|
||||
foreach (var attributeKeyValuePair in distinctAttributes)
|
||||
{
|
||||
var matchingAttributes = chunkAttributes.Where(
|
||||
kvp => string.Equals(kvp.Key, attributeDescriptor.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (matchingAttributes.Any())
|
||||
var attributeName = attributeKeyValuePair.Key;
|
||||
var attributeValueChunk = attributeKeyValuePair.Value;
|
||||
if (attributeValueChunk == null)
|
||||
{
|
||||
// First attribute wins, even if there's duplicates.
|
||||
var firstAttribute = matchingAttributes.First();
|
||||
var attributeValueChunk = firstAttribute.Value;
|
||||
// Minimized attributes are not valid for bound attributes. TagHelperBlockRewriter has already
|
||||
// logged an error if it was a bound attribute; so we can skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Minimized attributes are not valid for bound attributes. There will be an error for the bound
|
||||
// attribute logged by TagHelperBlockRewriter already so we can skip.
|
||||
if (attributeValueChunk == null)
|
||||
// Find the matching TagHelperAttributeDescriptor.
|
||||
var attributeDescriptor = attributeDescriptors.FirstOrDefault(
|
||||
descriptor => descriptor.IsNameMatch(attributeName));
|
||||
if (attributeDescriptor == null)
|
||||
{
|
||||
// Attribute is not bound to a property or indexer in this tag helper.
|
||||
continue;
|
||||
}
|
||||
|
||||
// We capture the tag helper's property value accessor so we can retrieve it later (if we need to).
|
||||
var valueAccessor = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}.{1}",
|
||||
tagHelperVariableName,
|
||||
attributeDescriptor.PropertyName);
|
||||
|
||||
if (attributeDescriptor.IsIndexer)
|
||||
{
|
||||
// Need a different valueAccessor in this case. But first need to throw a reasonable Exception at
|
||||
// runtime if the property is null. The check is not required at design time.
|
||||
if (!_designTimeMode && confirmedDictionaries.Add(attributeDescriptor.PropertyName))
|
||||
{
|
||||
_writer
|
||||
.Write("if (")
|
||||
.Write(valueAccessor)
|
||||
.WriteLine(" == null)");
|
||||
using (_writer.BuildScope())
|
||||
{
|
||||
// System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName
|
||||
// of InvalidOperationException type.
|
||||
_writer
|
||||
.Write("throw ")
|
||||
.WriteStartNewObject(nameof(InvalidOperationException))
|
||||
.WriteStartMethodInvocation(_tagHelperContext.FormatInvalidIndexerAssignmentMethodName)
|
||||
.WriteStringLiteral(attributeName)
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(tagHelperTypeName)
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(attributeDescriptor.PropertyName)
|
||||
.WriteEndMethodInvocation(endLine: false) // End of method call
|
||||
.WriteEndMethodInvocation(endLine: true); // End of new expression / throw statement
|
||||
}
|
||||
}
|
||||
|
||||
var dictionaryKey = attributeName.Substring(attributeDescriptor.Name.Length);
|
||||
valueAccessor = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}.{1}[\"{2}\"]",
|
||||
tagHelperVariableName,
|
||||
attributeDescriptor.PropertyName,
|
||||
dictionaryKey);
|
||||
}
|
||||
|
||||
// If we haven't recorded this attribute value before then we need to record its value.
|
||||
var attributeValueRecorded = htmlAttributeValues.ContainsKey(attributeName);
|
||||
if (!attributeValueRecorded)
|
||||
{
|
||||
// We only need to create attribute values once per HTML element (not once per tag helper).
|
||||
// We're saving the value accessor so we can retrieve it later if there are more tag
|
||||
// helpers that need the value.
|
||||
htmlAttributeValues.Add(attributeName, valueAccessor);
|
||||
|
||||
// Bufferable attributes are attributes that can have Razor code inside of them. Such
|
||||
// attributes have string values and may be calculated using a temporary TextWriter or other
|
||||
// buffer.
|
||||
var bufferableAttribute = attributeDescriptor.IsStringProperty;
|
||||
|
||||
RenderNewAttributeValueAssignment(
|
||||
attributeDescriptor,
|
||||
bufferableAttribute,
|
||||
attributeValueChunk,
|
||||
valueAccessor);
|
||||
|
||||
// Execution contexts are a runtime feature.
|
||||
if (_designTimeMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We capture the tag helpers property value accessor so we can retrieve it later (if we need to).
|
||||
var valueAccessor = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"{0}.{1}",
|
||||
tagHelperVariableName,
|
||||
attributeDescriptor.PropertyName);
|
||||
|
||||
// If we haven't recorded this attribute value before then we need to record its value.
|
||||
var attributeValueRecorded = htmlAttributeValues.ContainsKey(attributeDescriptor.Name);
|
||||
if (!attributeValueRecorded)
|
||||
{
|
||||
// We only need to create attribute values once per HTML element (not once per tag helper).
|
||||
// We're saving the value accessor so we can retrieve it later if there are more tag
|
||||
// helpers that need the value.
|
||||
htmlAttributeValues.Add(attributeDescriptor.Name, valueAccessor);
|
||||
|
||||
// Bufferable attributes are attributes that can have Razor code inside of them. Such
|
||||
// attributes have string values and may be calculated using a temporary TextWriter or other
|
||||
// buffer.
|
||||
var bufferableAttribute = attributeDescriptor.IsStringProperty;
|
||||
|
||||
RenderNewAttributeValueAssignment(
|
||||
attributeDescriptor,
|
||||
bufferableAttribute,
|
||||
attributeValueChunk,
|
||||
valueAccessor);
|
||||
|
||||
// Execution contexts are a runtime feature.
|
||||
if (_designTimeMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to inform the context of the attribute value.
|
||||
var attributeName = firstAttribute.Key;
|
||||
_writer
|
||||
.WriteStartInstanceMethodInvocation(
|
||||
ExecutionContextVariableName,
|
||||
_tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName)
|
||||
.WriteStringLiteral(attributeName)
|
||||
.WriteParameterSeparator()
|
||||
.Write(valueAccessor)
|
||||
.WriteEndMethodInvocation();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute value has already been recorded, lets retrieve it from the stored value
|
||||
// accessors.
|
||||
_writer
|
||||
.WriteStartAssignment(valueAccessor)
|
||||
.Write(htmlAttributeValues[attributeDescriptor.Name])
|
||||
.WriteLine(";");
|
||||
}
|
||||
// We need to inform the context of the attribute value.
|
||||
_writer
|
||||
.WriteStartInstanceMethodInvocation(
|
||||
ExecutionContextVariableName,
|
||||
_tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName)
|
||||
.WriteStringLiteral(attributeName)
|
||||
.WriteParameterSeparator()
|
||||
.Write(valueAccessor)
|
||||
.WriteEndMethodInvocation();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The attribute value has already been recorded, lets retrieve it from the stored value
|
||||
// accessors.
|
||||
_writer
|
||||
.WriteStartAssignment(valueAccessor)
|
||||
.Write(htmlAttributeValues[attributeName])
|
||||
.WriteLine(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -580,6 +625,26 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
|
|||
return true;
|
||||
}
|
||||
|
||||
// An IEqualityComparer for string -> Chunk mappings which compares only the Key.
|
||||
private class KeyValuePairKeyComparer : IEqualityComparer<KeyValuePair<string, Chunk>>
|
||||
{
|
||||
public static KeyValuePairKeyComparer Default = new KeyValuePairKeyComparer();
|
||||
|
||||
private KeyValuePairKeyComparer()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Equals(KeyValuePair<string, Chunk> keyValuePairX, KeyValuePair<string, Chunk> keyValuePairY)
|
||||
{
|
||||
return string.Equals(keyValuePairX.Key, keyValuePairY.Key, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public int GetHashCode(KeyValuePair<string, Chunk> keyValuePair)
|
||||
{
|
||||
return keyValuePair.Key.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
// A CSharpCodeVisitor which does not HTML encode values. Used when rendering bound string attribute values.
|
||||
private class CSharpLiteralCodeVisitor : CSharpCodeVisitor
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Razor.Generator
|
|||
ExecutionContextAddMinimizedHtmlAttributeMethodName = "AddMinimizedHtmlAttribute";
|
||||
ExecutionContextAddHtmlAttributeMethodName = "AddHtmlAttribute";
|
||||
ExecutionContextOutputPropertyName = "Output";
|
||||
FormatInvalidIndexerAssignmentMethodName = "FormatInvalidIndexerAssignment";
|
||||
MarkAsHtmlEncodedMethodName = "Html.Raw";
|
||||
StartTagHelperWritingScopeMethodName = "StartTagHelperWritingScope";
|
||||
EndTagHelperWritingScopeMethodName = "EndTagHelperWritingScope";
|
||||
|
|
@ -78,6 +79,21 @@ namespace Microsoft.AspNet.Razor.Generator
|
|||
/// </summary>
|
||||
public string ExecutionContextOutputPropertyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method used to format an error message about using an indexer when the tag helper property
|
||||
/// is <c>null</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Method signature should be
|
||||
/// <code>
|
||||
/// public string FormatInvalidIndexerAssignment(
|
||||
/// string attributeName, // Name of the HTML attribute associated with the indexer.
|
||||
/// string tagHelperTypeName, // Full name of the tag helper type.
|
||||
/// string propertyName) // Dictionary property in the tag helper.
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public string FormatInvalidIndexerAssignmentMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method used to wrap a <see cref="string"/> value and mark it as HTML-encoded.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
|
|
@ -65,16 +64,18 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
// Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
|
||||
if (result != null)
|
||||
{
|
||||
SourceLocation? errorLocation = null;
|
||||
|
||||
// Check if it's a bound attribute that is minimized or if it's a bound non-string attribute that
|
||||
// is null or whitespace.
|
||||
if ((result.IsBoundAttribute && result.AttributeValueNode == null) ||
|
||||
(result.IsBoundNonStringAttribute &&
|
||||
IsNullOrWhitespaceAttributeValue(result.AttributeValueNode)))
|
||||
{
|
||||
var errorLocation = GetAttributeNameStartLocation(child);
|
||||
errorLocation = GetAttributeNameStartLocation(child);
|
||||
|
||||
errorSink.OnError(
|
||||
errorLocation,
|
||||
errorLocation.Value,
|
||||
RazorResources.FormatRewriterError_EmptyTagHelperBoundAttribute(
|
||||
result.AttributeName,
|
||||
tagName,
|
||||
|
|
@ -82,6 +83,23 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
result.AttributeName.Length);
|
||||
}
|
||||
|
||||
// Check if the attribute was a prefix match for a tag helper dictionary property but the
|
||||
// dictionary key would be the empty string.
|
||||
if (result.IsMissingDictionaryKey)
|
||||
{
|
||||
if (!errorLocation.HasValue)
|
||||
{
|
||||
errorLocation = GetAttributeNameStartLocation(child);
|
||||
}
|
||||
|
||||
errorSink.OnError(
|
||||
errorLocation.Value,
|
||||
RazorResources.FormatTagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey(
|
||||
result.AttributeName,
|
||||
tagName),
|
||||
result.AttributeName.Length);
|
||||
}
|
||||
|
||||
attributes.Add(
|
||||
new KeyValuePair<string, SyntaxTreeNode>(result.AttributeName, result.AttributeValueNode));
|
||||
}
|
||||
|
|
@ -224,24 +242,19 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
{
|
||||
errorSink.OnError(
|
||||
span.Start,
|
||||
RazorResources.TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed,
|
||||
RazorResources.TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed,
|
||||
span.Content.Length);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isBoundNonStringAttribute;
|
||||
var result = new TryParseResult
|
||||
{
|
||||
IsBoundAttribute = IsBoundAttribute(name, descriptors, out isBoundNonStringAttribute),
|
||||
AttributeName = name,
|
||||
};
|
||||
result.IsBoundNonStringAttribute = isBoundNonStringAttribute;
|
||||
var result = CreateTryParseResult(name, descriptors);
|
||||
|
||||
// If we're not after an equal then we should treat the value as if it were a minimized attribute.
|
||||
var attributeValueBuilder = afterEquals ? builder : null;
|
||||
result.AttributeValueNode = CreateMarkupAttribute(name, attributeValueBuilder, isBoundNonStringAttribute);
|
||||
result.AttributeValueNode =
|
||||
CreateMarkupAttribute(name, attributeValueBuilder, result.IsBoundNonStringAttribute);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -286,13 +299,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
}
|
||||
|
||||
// Have a name now. Able to determine correct isBoundNonStringAttribute value.
|
||||
bool isBoundNonStringAttribute;
|
||||
var result = new TryParseResult
|
||||
{
|
||||
IsBoundAttribute = IsBoundAttribute(name, descriptors, out isBoundNonStringAttribute),
|
||||
AttributeName = name,
|
||||
};
|
||||
result.IsBoundNonStringAttribute = isBoundNonStringAttribute;
|
||||
var result = CreateTryParseResult(name, descriptors);
|
||||
|
||||
// Remove first child i.e. foo="
|
||||
builder.Children.RemoveAt(0);
|
||||
|
|
@ -324,13 +331,13 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
if (block.Children.Count() == 1)
|
||||
{
|
||||
var child = block.Children.First() as Span;
|
||||
|
||||
if (child != null)
|
||||
{
|
||||
// After pulling apart the block we just have a value span.
|
||||
var spanBuilder = new SpanBuilder(child);
|
||||
|
||||
result.AttributeValueNode = CreateMarkupAttribute(name, spanBuilder, isBoundNonStringAttribute);
|
||||
result.AttributeValueNode =
|
||||
CreateMarkupAttribute(name, spanBuilder, result.IsBoundNonStringAttribute);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -485,17 +492,23 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
return firstBoundAttribute?.TypeName;
|
||||
}
|
||||
|
||||
// Determines whether an attribute with the given name is bound to a non-string tag helper property.
|
||||
private static bool IsBoundAttribute(
|
||||
string name,
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
out bool isBoundNonStringAttribute)
|
||||
// Create a TryParseResult for given name, filling in binding details.
|
||||
private static TryParseResult CreateTryParseResult(string name, IEnumerable<TagHelperDescriptor> descriptors)
|
||||
{
|
||||
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
||||
var isBoundAttribute = firstBoundAttribute != null;
|
||||
isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.IsStringProperty;
|
||||
var isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.IsStringProperty;
|
||||
var isMissingDictionaryKey = isBoundAttribute &&
|
||||
firstBoundAttribute.IsIndexer &&
|
||||
name.Length == firstBoundAttribute.Name.Length;
|
||||
|
||||
return isBoundAttribute;
|
||||
return new TryParseResult
|
||||
{
|
||||
AttributeName = name,
|
||||
IsBoundAttribute = isBoundAttribute,
|
||||
IsBoundNonStringAttribute = isBoundNonStringAttribute,
|
||||
IsMissingDictionaryKey = isMissingDictionaryKey,
|
||||
};
|
||||
}
|
||||
|
||||
// Finds first TagHelperAttributeDescriptor matching given name.
|
||||
|
|
@ -503,9 +516,13 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
string name,
|
||||
IEnumerable<TagHelperDescriptor> descriptors)
|
||||
{
|
||||
return descriptors
|
||||
// Non-indexers (exact HTML attribute name matches) have higher precedence than indexers (prefix matches).
|
||||
// Attributes already sorted to ensure this precedence.
|
||||
var firstBoundAttribute = descriptors
|
||||
.SelectMany(descriptor => descriptor.Attributes)
|
||||
.FirstOrDefault(attribute => string.Equals(attribute.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||
.FirstOrDefault(attributeDescriptor => attributeDescriptor.IsNameMatch(name));
|
||||
|
||||
return firstBoundAttribute;
|
||||
}
|
||||
|
||||
private static bool IsQuote(HtmlSymbol htmlSymbol)
|
||||
|
|
@ -523,6 +540,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
public bool IsBoundAttribute { get; set; }
|
||||
|
||||
public bool IsBoundNonStringAttribute { get; set; }
|
||||
|
||||
public bool IsMissingDictionaryKey { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1367,19 +1367,35 @@ namespace Microsoft.AspNet.Razor
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// TagHelper attributes must be welformed.
|
||||
/// TagHelper attributes must be well-formed.
|
||||
/// </summary>
|
||||
internal static string TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed
|
||||
internal static string TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed
|
||||
{
|
||||
get { return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed"); }
|
||||
get { return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TagHelper attributes must be welformed.
|
||||
/// TagHelper attributes must be well-formed.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed()
|
||||
internal static string FormatTagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed()
|
||||
{
|
||||
return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed");
|
||||
return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tag helper attribute '{0}' in element '{1}' is missing a key. The syntax is '<{1} {0}{{ key }}="value">'.
|
||||
/// </summary>
|
||||
internal static string TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey
|
||||
{
|
||||
get { return GetString("TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tag helper attribute '{0}' in element '{1}' is missing a key. The syntax is '<{1} {0}{{ key }}="value">'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
|
|
@ -26,36 +26,36 @@
|
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
|
@ -390,8 +390,11 @@ Instead, wrap the contents of the block in "{{}}":
|
|||
<data name="TagHelpersParseTreeRewriter_MissingCloseAngle" xml:space="preserve">
|
||||
<value>Missing close angle for tag helper '{0}'.</value>
|
||||
</data>
|
||||
<data name="TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed" xml:space="preserve">
|
||||
<value>TagHelper attributes must be welformed.</value>
|
||||
<data name="TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed" xml:space="preserve">
|
||||
<value>TagHelper attributes must be well-formed.</value>
|
||||
</data>
|
||||
<data name="TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey" xml:space="preserve">
|
||||
<value>The tag helper attribute '{0}' in element '{1}' is missing a key. The syntax is '<{1} {0}{{ key }}="value">'.</value>
|
||||
</data>
|
||||
<data name="TagHelpers_AttributeExpressionRequired" xml:space="preserve">
|
||||
<value>Non-string tag helper attribute values must not be empty. Add an expression to this attribute value.</value>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
name,
|
||||
propertyInfo.Name,
|
||||
propertyInfo.PropertyType.FullName,
|
||||
isIndexer: false,
|
||||
isStringProperty: propertyInfo.PropertyType == typeof(string))
|
||||
{
|
||||
}
|
||||
|
|
@ -25,48 +26,77 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="TagHelperAttributeDescriptor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The HTML attribute name.</param>
|
||||
/// <param name="name">
|
||||
/// The HTML attribute name or, if <paramref name="isIndexer"/> is <c>true</c>, the prefix for matching
|
||||
/// attribute names.
|
||||
/// </param>
|
||||
/// <param name="propertyName">The name of the CLR property that corresponds to the HTML attribute.</param>
|
||||
/// <param name="typeName">
|
||||
/// The full name of the named (see <paramref name="propertyName"/>) property's <see cref="System.Type"/>.
|
||||
/// The full name of the named (see <paramref name="propertyName"/>) property's <see cref="Type"/> or,
|
||||
/// if <paramref name="isIndexer"/> is <c>true</c>, the full name of the indexer's value <see cref="Type"/>.
|
||||
/// </param>
|
||||
/// <param name="isIndexer">
|
||||
/// If <c>true</c> this <see cref="TagHelperAttributeDescriptor"/> is used for dictionary indexer assignments.
|
||||
/// Otherwise this <see cref="TagHelperAttributeDescriptor"/> is used for property assignment.
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// HTML attribute names are matched case-insensitively, regardless of <paramref name="isIndexer"/>.
|
||||
/// </remarks>
|
||||
public TagHelperAttributeDescriptor(
|
||||
[NotNull] string name,
|
||||
[NotNull] string propertyName,
|
||||
[NotNull] string typeName)
|
||||
[NotNull] string typeName,
|
||||
bool isIndexer)
|
||||
: this(
|
||||
name,
|
||||
propertyName,
|
||||
typeName,
|
||||
isIndexer,
|
||||
isStringProperty: string.Equals(typeName, typeof(string).FullName, StringComparison.Ordinal))
|
||||
{
|
||||
}
|
||||
|
||||
// Internal for testing i.e. for confirming above constructor sets `IsStringProperty` as expected.
|
||||
// Internal for testing i.e. for confirming above constructor sets IsStringProperty as expected.
|
||||
internal TagHelperAttributeDescriptor(
|
||||
[NotNull] string name,
|
||||
[NotNull] string propertyName,
|
||||
[NotNull] string typeName,
|
||||
bool isIndexer,
|
||||
bool isStringProperty)
|
||||
{
|
||||
Name = name;
|
||||
PropertyName = propertyName;
|
||||
TypeName = typeName;
|
||||
IsIndexer = isIndexer;
|
||||
IsStringProperty = isStringProperty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication whether this property is of type <see cref="string"/>.
|
||||
/// Gets an indication whether this <see cref="TagHelperAttributeDescriptor"/> is used for dictionary indexer
|
||||
/// assignments.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// If <c>true</c> this <see cref="TagHelperAttributeDescriptor"/> should be associated with all HTML
|
||||
/// attributes that have names starting with <see cref="Name"/>. Otherwise this
|
||||
/// <see cref="TagHelperAttributeDescriptor"/> is used for property assignment and is only associated with an
|
||||
/// HTML attribute that has the exact <see cref="Name"/>.
|
||||
/// </value>
|
||||
public bool IsIndexer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an indication whether this property is of type <see cref="string"/> or, if <see cref="IsIndexer"/> is
|
||||
/// <c>true</c>, whether the indexer's value is of type <see cref="string"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// If <c>true</c> the <see cref="TypeName"/> is for <see cref="string"/>. This causes the Razor parser
|
||||
/// to allow empty values for attributes that have names matching <see cref="Name"/>. If <c>false</c>
|
||||
/// empty values for such matching attributes lead to errors.
|
||||
/// to allow empty values for HTML attributes matching this <see cref="TagHelperAttributeDescriptor"/>. If
|
||||
/// <c>false</c> empty values for such matching attributes lead to errors.
|
||||
/// </value>
|
||||
public bool IsStringProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The HTML attribute name.
|
||||
/// The HTML attribute name or, if <see cref="IsIndexer"/> is <c>true</c>, the prefix for matching attribute
|
||||
/// names.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
|
|
@ -76,8 +106,30 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
public string PropertyName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full name of the named (see <see name="PropertyName"/>) property's <see cref="System.Type"/>.
|
||||
/// The full name of the named (see <see name="PropertyName"/>) property's <see cref="Type"/> or, if
|
||||
/// <see cref="IsIndexer"/> is <c>true</c>, the full name of the indexer's value <see cref="Type"/>.
|
||||
/// </summary>
|
||||
public string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether HTML attribute <paramref name="name"/> matches this
|
||||
/// <see cref="TagHelperAttributeDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the HTML attribute to check.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this <see cref="TagHelperAttributeDescriptor"/> matches <paramref name="name"/>.
|
||||
/// <c>false</c> otherwise.
|
||||
/// </returns>
|
||||
public bool IsNameMatch(string name)
|
||||
{
|
||||
if (IsIndexer)
|
||||
{
|
||||
return name.StartsWith(Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Equals(name, Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,9 +29,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Determines equality based on <see cref="TagHelperAttributeDescriptor.Name"/>,
|
||||
/// <see cref="TagHelperAttributeDescriptor.PropertyName"/>,
|
||||
/// and <see cref="TagHelperAttributeDescriptor.TypeName"/>.
|
||||
/// Determines equality based on <see cref="TagHelperAttributeDescriptor.IsIndexer"/>,
|
||||
/// <see cref="TagHelperAttributeDescriptor.Name"/>, <see cref="TagHelperAttributeDescriptor.PropertyName"/>,
|
||||
/// and <see cref="TagHelperAttributeDescriptor.TypeName"/>. Ignores
|
||||
/// <see cref="TagHelperAttributeDescriptor.IsStringProperty"/> because it can be inferred directly from
|
||||
/// <see cref="TagHelperAttributeDescriptor.TypeName"/>.
|
||||
/// </remarks>
|
||||
public virtual bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
|
||||
{
|
||||
|
|
@ -40,7 +42,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
return true;
|
||||
}
|
||||
|
||||
// Check Name and TypeName though each property in a particular tag helper has at most two
|
||||
// TagHelperAttributeDescriptors (one for the indexer and one not). May be comparing attributes between
|
||||
// tag helpers and should be as specific as we can.
|
||||
return descriptorX != null &&
|
||||
descriptorX.IsIndexer == descriptorY.IsIndexer &&
|
||||
string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(descriptorX.PropertyName, descriptorY.PropertyName, StringComparison.Ordinal) &&
|
||||
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
|
||||
|
|
@ -49,7 +55,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <inheritdoc />
|
||||
public virtual int GetHashCode([NotNull] TagHelperAttributeDescriptor descriptor)
|
||||
{
|
||||
// Rarely if ever hash TagHelperAttributeDescriptor. If we do, include the Name and TypeName since context
|
||||
// information is not available in the hash.
|
||||
return HashCodeCombiner.Start()
|
||||
.Add(descriptor.IsIndexer)
|
||||
.Add(descriptor.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.Add(descriptor.PropertyName, StringComparer.Ordinal)
|
||||
.Add(descriptor.TypeName, StringComparer.Ordinal)
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
return true;
|
||||
}
|
||||
|
||||
// Normal comparer doesn't care about case, in tests we do. Also double-check IsStringProperty though
|
||||
// it is inferred from TypeName.
|
||||
// Base comparer does not care about Name case but in tests we do. Also double-check IsStringProperty
|
||||
// though it is inferred from TypeName.
|
||||
return base.Equals(descriptorX, descriptorY) &&
|
||||
descriptorX.IsStringProperty == descriptorY.IsStringProperty &&
|
||||
string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal);
|
||||
|
|
@ -33,8 +33,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
public override int GetHashCode(TagHelperAttributeDescriptor descriptor)
|
||||
{
|
||||
// Rarely if ever hash TagHelperAttributeDescriptor. If we do, ignore IsStringProperty since it should
|
||||
// not vary for a given TypeName i.e. will not change the bucket.
|
||||
// Ignore IsStringProperty because it is directly inferred from TypeName and thus won't vary the hash
|
||||
// bucket. Base comparer does not care about Name case in its hash code but in tests we do.
|
||||
return HashCodeCombiner.Start()
|
||||
.Add(base.GetHashCode(descriptor))
|
||||
.Add(descriptor.Name, StringComparer.Ordinal)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -34,7 +34,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
new TagHelperAttributeDescriptor(
|
||||
"catchall-bound-string",
|
||||
"BoundRequiredString",
|
||||
typeof(string).FullName),
|
||||
typeof(string).FullName,
|
||||
isIndexer: false),
|
||||
},
|
||||
requiredAttributes: new[] { "catchall-unbound-required" }),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -46,11 +47,13 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
new TagHelperAttributeDescriptor(
|
||||
"input-bound-required-string",
|
||||
"BoundRequiredString",
|
||||
typeof(string).FullName),
|
||||
typeof(string).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
"input-bound-string",
|
||||
"BoundString",
|
||||
typeof(string).FullName)
|
||||
typeof(string).FullName,
|
||||
isIndexer: false)
|
||||
},
|
||||
requiredAttributes: new[] { "input-bound-required-string", "input-unbound-required" }),
|
||||
};
|
||||
|
|
@ -152,6 +155,80 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<TagHelperDescriptor> PrefixedAttributeTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper1",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "int-prefix-grabber",
|
||||
propertyName: "IntProperty",
|
||||
typeName: typeof(int).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "int-dictionary",
|
||||
propertyName: "IntDictionaryProperty",
|
||||
typeName: typeof(IDictionary<string, int>).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "string-dictionary",
|
||||
propertyName: "StringDictionaryProperty",
|
||||
typeName: "Namespace.DictionaryWithoutParameterlessConstructor<string, string>",
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "string-prefix-grabber",
|
||||
propertyName: "StringProperty",
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "int-prefix-",
|
||||
propertyName: "IntDictionaryProperty",
|
||||
typeName: typeof(int).FullName,
|
||||
isIndexer: true),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "string-prefix-",
|
||||
propertyName: "StringDictionaryProperty",
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: true),
|
||||
}),
|
||||
new TagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper2",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "int-dictionary",
|
||||
propertyName: "IntDictionaryProperty",
|
||||
typeName: typeof(IDictionary<string, int>).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "string-dictionary",
|
||||
propertyName: "StringDictionaryProperty",
|
||||
typeName: "Namespace.DictionaryWithoutParameterlessConstructor<string, string>",
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "int-prefix-",
|
||||
propertyName: "IntDictionaryProperty",
|
||||
typeName: typeof(int).FullName,
|
||||
isIndexer: true),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "string-prefix-",
|
||||
propertyName: "StringDictionaryProperty",
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: true),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static TheoryData TagHelperDescriptorFlowTestData
|
||||
{
|
||||
get
|
||||
|
|
@ -498,6 +575,101 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
contentLength: 4)
|
||||
}
|
||||
},
|
||||
{
|
||||
"PrefixedAttributeTagHelpers",
|
||||
"PrefixedAttributeTagHelpers.DesignTime",
|
||||
PrefixedAttributeTagHelperDescriptors,
|
||||
new List<LineMapping>
|
||||
{
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 14,
|
||||
documentLineIndex: 0,
|
||||
generatedAbsoluteIndex: 499,
|
||||
generatedLineIndex: 15,
|
||||
characterOffsetIndex: 14,
|
||||
contentLength: 17),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 37,
|
||||
documentLineIndex: 2,
|
||||
generatedAbsoluteIndex: 996,
|
||||
generatedLineIndex: 34,
|
||||
characterOffsetIndex: 2,
|
||||
contentLength: 242),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 370,
|
||||
documentLineIndex: 15,
|
||||
generatedAbsoluteIndex: 1430,
|
||||
generatedLineIndex: 50,
|
||||
characterOffsetIndex: 43,
|
||||
contentLength: 13),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 404,
|
||||
documentLineIndex: 15,
|
||||
generatedAbsoluteIndex: 1601,
|
||||
generatedLineIndex: 55,
|
||||
characterOffsetIndex: 77,
|
||||
contentLength: 16),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 468,
|
||||
documentLineIndex: 16,
|
||||
generatedAbsoluteIndex: 2077,
|
||||
generatedLineIndex: 64,
|
||||
characterOffsetIndex: 43,
|
||||
contentLength: 13),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 502,
|
||||
documentLineIndex: 16,
|
||||
generatedAbsoluteIndex: 2248,
|
||||
generatedLineIndex: 69,
|
||||
characterOffsetIndex: 77,
|
||||
contentLength: 2),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 526,
|
||||
documentLineIndex: 16,
|
||||
generatedAbsoluteIndex: 2432,
|
||||
generatedLineIndex: 74,
|
||||
characterOffsetIndex: 101,
|
||||
contentLength: 2),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 590,
|
||||
documentLineIndex: 18,
|
||||
documentCharacterOffsetIndex: 31,
|
||||
generatedAbsoluteIndex: 2994,
|
||||
generatedLineIndex: 84,
|
||||
generatedCharacterOffsetIndex: 32,
|
||||
contentLength: 2),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 611,
|
||||
documentLineIndex: 18,
|
||||
generatedAbsoluteIndex: 3129,
|
||||
generatedLineIndex: 89,
|
||||
characterOffsetIndex: 52,
|
||||
contentLength: 2),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 634,
|
||||
documentLineIndex: 18,
|
||||
generatedAbsoluteIndex: 3287,
|
||||
generatedLineIndex: 94,
|
||||
characterOffsetIndex: 75,
|
||||
contentLength: 2),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 783,
|
||||
documentLineIndex: 20,
|
||||
documentCharacterOffsetIndex: 42,
|
||||
generatedAbsoluteIndex: 3521,
|
||||
generatedLineIndex: 101,
|
||||
generatedCharacterOffsetIndex: 6,
|
||||
contentLength: 8),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 826,
|
||||
documentLineIndex: 21,
|
||||
documentCharacterOffsetIndex: 29,
|
||||
generatedAbsoluteIndex: 4552,
|
||||
generatedLineIndex: 115,
|
||||
generatedCharacterOffsetIndex: 51,
|
||||
contentLength: 2),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -523,28 +695,36 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
{
|
||||
// Test resource name, expected TagHelperDescriptors
|
||||
// Note: The baseline resource name is equivalent to the test resource name.
|
||||
return new TheoryData<string, IEnumerable<TagHelperDescriptor>>
|
||||
return new TheoryData<string, string, IEnumerable<TagHelperDescriptor>>
|
||||
{
|
||||
{ "SingleTagHelper", DefaultPAndInputTagHelperDescriptors },
|
||||
{ "BasicTagHelpers", DefaultPAndInputTagHelperDescriptors },
|
||||
{ "BasicTagHelpers.RemoveTagHelper", DefaultPAndInputTagHelperDescriptors },
|
||||
{ "BasicTagHelpers.Prefixed", PrefixedPAndInputTagHelperDescriptors },
|
||||
{ "ComplexTagHelpers", DefaultPAndInputTagHelperDescriptors },
|
||||
{ "DuplicateTargetTagHelper", DuplicateTargetTagHelperDescriptors },
|
||||
{ "EmptyAttributeTagHelpers", DefaultPAndInputTagHelperDescriptors },
|
||||
{ "EscapedTagHelpers", DefaultPAndInputTagHelperDescriptors },
|
||||
{ "AttributeTargetingTagHelpers", AttributeTargetingTagHelperDescriptors },
|
||||
{ "SingleTagHelper", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "BasicTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "BasicTagHelpers.RemoveTagHelper", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "BasicTagHelpers.Prefixed", null, PrefixedPAndInputTagHelperDescriptors },
|
||||
{ "ComplexTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "DuplicateTargetTagHelper", null, DuplicateTargetTagHelperDescriptors },
|
||||
{ "EmptyAttributeTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "EscapedTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "AttributeTargetingTagHelpers", null, AttributeTargetingTagHelperDescriptors },
|
||||
{ "PrefixedAttributeTagHelpers", null, PrefixedAttributeTagHelperDescriptors },
|
||||
{
|
||||
"PrefixedAttributeTagHelpers",
|
||||
"PrefixedAttributeTagHelpers.Reversed",
|
||||
PrefixedAttributeTagHelperDescriptors.Reverse()
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(RuntimeTimeTagHelperTestData))]
|
||||
public void TagHelpers_GenerateExpectedRuntimeOutput(string testName,
|
||||
IEnumerable<TagHelperDescriptor> tagHelperDescriptors)
|
||||
public void TagHelpers_GenerateExpectedRuntimeOutput(
|
||||
string testName,
|
||||
string baseLineName,
|
||||
IEnumerable<TagHelperDescriptor> tagHelperDescriptors)
|
||||
{
|
||||
// Act & Assert
|
||||
RunTagHelperTest(testName, tagHelperDescriptors: tagHelperDescriptors);
|
||||
// Arrange & Act & Assert
|
||||
RunTagHelperTest(testName, baseLineName, tagHelperDescriptors: tagHelperDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
var noErrors = new RazorError[0];
|
||||
var errorFormat = "Attribute '{0}' on tag helper element '{1}' requires a value. Tag helper bound " +
|
||||
"attributes of type '{2}' cannot be empty or contain only whitespace.";
|
||||
var emptyKeyFormat = "The tag helper attribute '{0}' in element '{1}' is missing a key. The " +
|
||||
"syntax is '<{1} {0}{{ key }}=\"value\">'.";
|
||||
var stringType = typeof(string).FullName;
|
||||
var intType = typeof(int).FullName;
|
||||
var expressionString = "@DateTime.Now + 1";
|
||||
|
|
@ -269,6 +271,196 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
})),
|
||||
new[] { new RazorError(string.Format(errorFormat, "bound-int", "p", intType), 3, 0, 3, 9) }
|
||||
},
|
||||
{
|
||||
"<input int-dictionary/>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("int-dictionary", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "int-dictionary", "input", typeof(IDictionary<string, int>).FullName),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 14),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input string-dictionary />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("string-dictionary", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "string-dictionary", "input", typeof(IDictionary<string, string>).FullName),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 17),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input int-prefix- />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("int-prefix-", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "int-prefix-", "input", typeof(int).FullName),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 11),
|
||||
new RazorError(
|
||||
string.Format(emptyKeyFormat, "int-prefix-", "input"),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 11),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input string-prefix-/>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("string-prefix-", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "string-prefix-", "input", typeof(string).FullName),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 14),
|
||||
new RazorError(
|
||||
string.Format(emptyKeyFormat, "string-prefix-", "input"),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 14),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input int-prefix-value/>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("int-prefix-value", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "int-prefix-value", "input", typeof(int).FullName),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 16),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input string-prefix-value />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("string-prefix-value", null),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "string-prefix-value", "input", typeof(string).FullName),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 19),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input int-prefix-value='' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("int-prefix-value", new MarkupBlock()),
|
||||
})),
|
||||
new[]
|
||||
{
|
||||
new RazorError(
|
||||
string.Format(errorFormat, "int-prefix-value", "input", typeof(int).FullName),
|
||||
absoluteIndex: 7,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
length: 16),
|
||||
}
|
||||
},
|
||||
{
|
||||
"<input string-prefix-value=''/>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("string-prefix-value", new MarkupBlock()),
|
||||
})),
|
||||
new RazorError[0]
|
||||
},
|
||||
{
|
||||
"<input int-prefix-value='3'/>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("int-prefix-value", factory.CodeMarkup("3")),
|
||||
})),
|
||||
new RazorError[0]
|
||||
},
|
||||
{
|
||||
"<input string-prefix-value='some string' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock(
|
||||
"input",
|
||||
selfClosing: true,
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("string-prefix-value", new MarkupBlock(
|
||||
factory.Markup("some"),
|
||||
factory.Markup(" string"))),
|
||||
})),
|
||||
new RazorError[0]
|
||||
},
|
||||
{
|
||||
"<input unbound-required bound-required-string />",
|
||||
new MarkupBlock(
|
||||
|
|
@ -878,7 +1070,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
"bound-required-string",
|
||||
"BoundRequiredString",
|
||||
typeof(string).FullName)
|
||||
typeof(string).FullName,
|
||||
isIndexer: false)
|
||||
},
|
||||
requiredAttributes: new[] { "unbound-required" }),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -890,7 +1083,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
"bound-required-string",
|
||||
"BoundRequiredString",
|
||||
typeof(string).FullName)
|
||||
typeof(string).FullName,
|
||||
isIndexer: false)
|
||||
},
|
||||
requiredAttributes: new[] { "bound-required-string" }),
|
||||
new TagHelperDescriptor(
|
||||
|
|
@ -902,9 +1096,38 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
"bound-required-int",
|
||||
"BoundRequiredInt",
|
||||
typeof(int).FullName)
|
||||
typeof(int).FullName,
|
||||
isIndexer: false)
|
||||
},
|
||||
requiredAttributes: new[] { "bound-required-int" }),
|
||||
new TagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper3",
|
||||
assemblyName: "SomeAssembly",
|
||||
attributes: new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor(
|
||||
"int-dictionary",
|
||||
"DictionaryOfIntProperty",
|
||||
typeof(IDictionary<string, int>).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
"string-dictionary",
|
||||
"DictionaryOfStringProperty",
|
||||
typeof(IDictionary<string, string>).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
"int-prefix-",
|
||||
"DictionaryOfIntProperty",
|
||||
typeof(int).FullName,
|
||||
isIndexer: true),
|
||||
new TagHelperAttributeDescriptor(
|
||||
"string-prefix-",
|
||||
"DictionaryOfStringProperty",
|
||||
typeof(string).FullName,
|
||||
isIndexer: true),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>()),
|
||||
new TagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper",
|
||||
|
|
@ -914,11 +1137,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
"bound-string",
|
||||
"BoundRequiredString",
|
||||
typeof(string).FullName),
|
||||
typeof(string).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
"bound-int",
|
||||
"BoundRequiredString",
|
||||
typeof(int).FullName)
|
||||
typeof(int).FullName,
|
||||
isIndexer: false)
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>()),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,11 +52,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute one",
|
||||
propertyName: "property name",
|
||||
typeName: "property type name"),
|
||||
typeName: "property type name",
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute two",
|
||||
propertyName: "property name",
|
||||
typeName: typeof(string).FullName),
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: false),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>());
|
||||
var expectedSerializedDescriptor =
|
||||
|
|
@ -66,11 +68,62 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"}}," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"}}]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]}}";
|
||||
|
||||
// Act
|
||||
var serializedDescriptor = JsonConvert.SerializeObject(descriptor);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TagHelperDescriptor_WithIndexerAttributes_CanBeSerialized()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new TagHelperDescriptor(
|
||||
prefix: "prefix:",
|
||||
tagName: "tag name",
|
||||
typeName: "type name",
|
||||
assemblyName: "assembly name",
|
||||
attributes: new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute one",
|
||||
propertyName: "property name",
|
||||
typeName: "property type name",
|
||||
isIndexer: true),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute two",
|
||||
propertyName: "property name",
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: true),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>());
|
||||
var expectedSerializedDescriptor =
|
||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"}}," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"}}]," +
|
||||
|
|
@ -129,11 +182,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"}}," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"}}]," +
|
||||
|
|
@ -148,11 +203,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute one",
|
||||
propertyName: "property name",
|
||||
typeName: "property type name"),
|
||||
typeName: "property type name",
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute two",
|
||||
propertyName: "property name",
|
||||
typeName: typeof(string).FullName),
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: false),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>());
|
||||
|
||||
|
|
@ -166,7 +223,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal);
|
||||
Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal);
|
||||
Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal(2, descriptor.Attributes.Count);
|
||||
Assert.Equal(expectedDescriptor.Attributes[0].IsIndexer, descriptor.Attributes[0].IsIndexer);
|
||||
Assert.Equal(expectedDescriptor.Attributes[0].IsStringProperty, descriptor.Attributes[0].IsStringProperty);
|
||||
Assert.Equal(expectedDescriptor.Attributes[0].Name, descriptor.Attributes[0].Name, StringComparer.Ordinal);
|
||||
Assert.Equal(
|
||||
|
|
@ -177,6 +236,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
expectedDescriptor.Attributes[0].TypeName,
|
||||
descriptor.Attributes[0].TypeName,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal(expectedDescriptor.Attributes[1].IsIndexer, descriptor.Attributes[1].IsIndexer);
|
||||
Assert.Equal(expectedDescriptor.Attributes[1].IsStringProperty, descriptor.Attributes[1].IsStringProperty);
|
||||
Assert.Equal(expectedDescriptor.Attributes[1].Name, descriptor.Attributes[1].Name, StringComparer.Ordinal);
|
||||
Assert.Equal(
|
||||
|
|
@ -187,6 +248,88 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
expectedDescriptor.Attributes[1].TypeName,
|
||||
descriptor.Attributes[1].TypeName,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
Assert.Empty(descriptor.RequiredAttributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TagHelperDescriptor_WithIndexerAttributes_CanBeDeserialized()
|
||||
{
|
||||
// Arrange
|
||||
var serializedDescriptor =
|
||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"}}," +
|
||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"}}]," +
|
||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]}}";
|
||||
var expectedDescriptor = new TagHelperDescriptor(
|
||||
prefix: "prefix:",
|
||||
tagName: "tag name",
|
||||
typeName: "type name",
|
||||
assemblyName: "assembly name",
|
||||
attributes: new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute one",
|
||||
propertyName: "property name",
|
||||
typeName: "property type name",
|
||||
isIndexer: true),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "attribute two",
|
||||
propertyName: "property name",
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: true),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>());
|
||||
|
||||
// Act
|
||||
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(descriptor);
|
||||
Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal);
|
||||
Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal);
|
||||
Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal);
|
||||
Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal);
|
||||
Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal(2, descriptor.Attributes.Count);
|
||||
Assert.Equal(expectedDescriptor.Attributes[0].IsIndexer, descriptor.Attributes[0].IsIndexer);
|
||||
Assert.Equal(expectedDescriptor.Attributes[0].IsStringProperty, descriptor.Attributes[0].IsStringProperty);
|
||||
Assert.Equal(expectedDescriptor.Attributes[0].Name, descriptor.Attributes[0].Name, StringComparer.Ordinal);
|
||||
Assert.Equal(
|
||||
expectedDescriptor.Attributes[0].PropertyName,
|
||||
descriptor.Attributes[0].PropertyName,
|
||||
StringComparer.Ordinal);
|
||||
Assert.Equal(
|
||||
expectedDescriptor.Attributes[0].TypeName,
|
||||
descriptor.Attributes[0].TypeName,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal(expectedDescriptor.Attributes[1].IsIndexer, descriptor.Attributes[1].IsIndexer);
|
||||
Assert.Equal(expectedDescriptor.Attributes[1].IsStringProperty, descriptor.Attributes[1].IsStringProperty);
|
||||
Assert.Equal(expectedDescriptor.Attributes[1].Name, descriptor.Attributes[1].Name, StringComparer.Ordinal);
|
||||
Assert.Equal(
|
||||
expectedDescriptor.Attributes[1].PropertyName,
|
||||
descriptor.Attributes[1].PropertyName,
|
||||
StringComparer.Ordinal);
|
||||
Assert.Equal(
|
||||
expectedDescriptor.Attributes[1].TypeName,
|
||||
descriptor.Attributes[1].TypeName,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
Assert.Empty(descriptor.RequiredAttributes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -921,7 +921,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
name: "bound",
|
||||
propertyName: "Bound",
|
||||
typeName: typeof(bool).FullName),
|
||||
typeName: typeof(bool).FullName,
|
||||
isIndexer: false),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>())
|
||||
};
|
||||
|
|
@ -944,7 +945,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
name: "bound",
|
||||
propertyName: "Bound",
|
||||
typeName: typeof(bool).FullName),
|
||||
typeName: typeof(bool).FullName,
|
||||
isIndexer: false),
|
||||
},
|
||||
requiredAttributes: Enumerable.Empty<string>())
|
||||
};
|
||||
|
|
@ -1508,11 +1510,13 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
new TagHelperAttributeDescriptor(
|
||||
name: "bound",
|
||||
propertyName: "Bound",
|
||||
typeName: typeof(bool).FullName),
|
||||
typeName: typeof(bool).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
name: "name",
|
||||
propertyName: "Name",
|
||||
typeName: typeof(string).FullName)
|
||||
typeName: typeof(string).FullName,
|
||||
isIndexer: false)
|
||||
})
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
|
@ -3396,7 +3400,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"),
|
||||
SourceLocation.Zero),
|
||||
new RazorError(
|
||||
"TagHelper attributes must be welformed.",
|
||||
"TagHelper attributes must be well-formed.",
|
||||
absoluteIndex: 12,
|
||||
lineIndex: 0,
|
||||
columnIndex: 12)
|
||||
|
|
@ -3962,9 +3966,13 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
|
|||
new TagHelperDescriptor("person", "PersonTagHelper", "personAssembly",
|
||||
attributes: new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor("age", "Age", typeof(int).FullName),
|
||||
new TagHelperAttributeDescriptor("birthday", "BirthDay", typeof(DateTime).FullName),
|
||||
new TagHelperAttributeDescriptor("name", "Name", typeof(string).FullName),
|
||||
new TagHelperAttributeDescriptor("age", "Age", typeof(int).FullName, isIndexer: false),
|
||||
new TagHelperAttributeDescriptor(
|
||||
"birthday",
|
||||
"BirthDay",
|
||||
typeof(DateTime).FullName,
|
||||
isIndexer: false),
|
||||
new TagHelperAttributeDescriptor("name", "Name", typeof(string).FullName, isIndexer: false),
|
||||
})
|
||||
};
|
||||
var providerContext = new TagHelperDescriptorProvider(descriptors);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
namespace TestOutput
|
||||
{
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class PrefixedAttributeTagHelpers
|
||||
{
|
||||
private static object @__o;
|
||||
private void @__RazorDesignTimeHelpers__()
|
||||
{
|
||||
#pragma warning disable 219
|
||||
string __tagHelperDirectiveSyntaxHelper = null;
|
||||
__tagHelperDirectiveSyntaxHelper =
|
||||
#line 1 "PrefixedAttributeTagHelpers.cshtml"
|
||||
"something, nice"
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
;
|
||||
#pragma warning restore 219
|
||||
}
|
||||
#line hidden
|
||||
private InputTagHelper1 __InputTagHelper1 = null;
|
||||
private InputTagHelper2 __InputTagHelper2 = null;
|
||||
#line hidden
|
||||
public PrefixedAttributeTagHelpers()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
#line 3 "PrefixedAttributeTagHelpers.cshtml"
|
||||
|
||||
var literate = "or illiterate";
|
||||
var intDictionary = new Dictionary<string, int>
|
||||
{
|
||||
{ "three", 3 },
|
||||
};
|
||||
var stringDictionary = new SortedDictionary<string, string>
|
||||
{
|
||||
{ "name", "value" },
|
||||
};
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
#line 16 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty = intDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#line 16 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.StringDictionaryProperty = stringDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__InputTagHelper2.IntDictionaryProperty = __InputTagHelper1.IntDictionaryProperty;
|
||||
__InputTagHelper2.StringDictionaryProperty = __InputTagHelper1.StringDictionaryProperty;
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty = intDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["garlic"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntProperty = 42;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__InputTagHelper2.IntDictionaryProperty = __InputTagHelper1.IntDictionaryProperty;
|
||||
__InputTagHelper2.IntDictionaryProperty["garlic"] = __InputTagHelper1.IntDictionaryProperty["garlic"];
|
||||
__InputTagHelper2.IntDictionaryProperty["grabber"] = __InputTagHelper1.IntProperty;
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntProperty = 42;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["salt"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["pepper"] = 98;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__InputTagHelper1.StringProperty = "string";
|
||||
__InputTagHelper1.StringDictionaryProperty["paprika"] = "another string";
|
||||
#line 21 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__o = literate;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__InputTagHelper1.StringDictionaryProperty["cumin"] = string.Empty;
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__InputTagHelper2.IntDictionaryProperty["grabber"] = __InputTagHelper1.IntProperty;
|
||||
__InputTagHelper2.IntDictionaryProperty["salt"] = __InputTagHelper1.IntDictionaryProperty["salt"];
|
||||
__InputTagHelper2.IntDictionaryProperty["pepper"] = __InputTagHelper1.IntDictionaryProperty["pepper"];
|
||||
__InputTagHelper2.StringDictionaryProperty["grabber"] = __InputTagHelper1.StringProperty;
|
||||
__InputTagHelper2.StringDictionaryProperty["paprika"] = __InputTagHelper1.StringDictionaryProperty["paprika"];
|
||||
__InputTagHelper2.StringDictionaryProperty["cumin"] = __InputTagHelper1.StringDictionaryProperty["cumin"];
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
#line 22 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["value"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__InputTagHelper1.StringDictionaryProperty["thyme"] = "string";
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__InputTagHelper2.IntDictionaryProperty["value"] = __InputTagHelper1.IntDictionaryProperty["value"];
|
||||
__InputTagHelper2.StringDictionaryProperty["thyme"] = __InputTagHelper1.StringDictionaryProperty["thyme"];
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
#pragma checksum "PrefixedAttributeTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "4e7fe9697b745af1a07d41f6a8532fdc288fa046"
|
||||
namespace TestOutput
|
||||
{
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class PrefixedAttributeTagHelpers
|
||||
{
|
||||
#line hidden
|
||||
#pragma warning disable 0414
|
||||
private TagHelperContent __tagHelperStringValueBuffer = null;
|
||||
#pragma warning restore 0414
|
||||
private TagHelperExecutionContext __tagHelperExecutionContext = null;
|
||||
private TagHelperRunner __tagHelperRunner = null;
|
||||
private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
|
||||
private InputTagHelper2 __InputTagHelper2 = null;
|
||||
private InputTagHelper1 __InputTagHelper1 = null;
|
||||
#line hidden
|
||||
public PrefixedAttributeTagHelpers()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
__tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
|
||||
Instrumentation.BeginContext(33, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
#line 3 "PrefixedAttributeTagHelpers.cshtml"
|
||||
|
||||
var literate = "or illiterate";
|
||||
var intDictionary = new Dictionary<string, int>
|
||||
{
|
||||
{ "three", 3 },
|
||||
};
|
||||
var stringDictionary = new SortedDictionary<string, string>
|
||||
{
|
||||
{ "name", "value" },
|
||||
};
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
Instrumentation.BeginContext(280, 51, true);
|
||||
WriteLiteral("\r\n\r\n<div class=\"randomNonTagHelperAttribute\">\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
#line 16 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty = intDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-dictionary", __InputTagHelper2.IntDictionaryProperty);
|
||||
#line 16 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.StringDictionaryProperty = stringDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-dictionary", __InputTagHelper2.StringDictionaryProperty);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
__InputTagHelper1.IntDictionaryProperty = __InputTagHelper2.IntDictionaryProperty;
|
||||
__InputTagHelper1.StringDictionaryProperty = __InputTagHelper2.StringDictionaryProperty;
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("checkbox"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(423, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty = intDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-dictionary", __InputTagHelper2.IntDictionaryProperty);
|
||||
if (__InputTagHelper2.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-garlic", "InputTagHelper2", "IntDictionaryProperty"));
|
||||
}
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty["garlic"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-garlic", __InputTagHelper2.IntDictionaryProperty["garlic"]);
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty["grabber"] = 42;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-grabber", __InputTagHelper2.IntDictionaryProperty["grabber"]);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
__InputTagHelper1.IntDictionaryProperty = __InputTagHelper2.IntDictionaryProperty;
|
||||
if (__InputTagHelper1.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-garlic", "InputTagHelper1", "IntDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper1.IntDictionaryProperty["garlic"] = __InputTagHelper2.IntDictionaryProperty["garlic"];
|
||||
__InputTagHelper1.IntProperty = __InputTagHelper2.IntDictionaryProperty["grabber"];
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("password"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(532, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
if (__InputTagHelper2.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-grabber", "InputTagHelper2", "IntDictionaryProperty"));
|
||||
}
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty["grabber"] = 42;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-grabber", __InputTagHelper2.IntDictionaryProperty["grabber"]);
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty["salt"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-salt", __InputTagHelper2.IntDictionaryProperty["salt"]);
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty["pepper"] = 98;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-pepper", __InputTagHelper2.IntDictionaryProperty["pepper"]);
|
||||
if (__InputTagHelper2.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-grabber", "InputTagHelper2", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper2.StringDictionaryProperty["grabber"] = "string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-grabber", __InputTagHelper2.StringDictionaryProperty["grabber"]);
|
||||
__InputTagHelper2.StringDictionaryProperty["paprika"] = "another string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-paprika", __InputTagHelper2.StringDictionaryProperty["paprika"]);
|
||||
StartTagHelperWritingScope();
|
||||
WriteLiteral("literate ");
|
||||
#line 21 "PrefixedAttributeTagHelpers.cshtml"
|
||||
WriteLiteral(literate);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("?");
|
||||
__tagHelperStringValueBuffer = EndTagHelperWritingScope();
|
||||
__InputTagHelper2.StringDictionaryProperty["cumin"] = __tagHelperStringValueBuffer.ToString();
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-cumin", __InputTagHelper2.StringDictionaryProperty["cumin"]);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
__InputTagHelper1.IntProperty = __InputTagHelper2.IntDictionaryProperty["grabber"];
|
||||
if (__InputTagHelper1.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-salt", "InputTagHelper1", "IntDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper1.IntDictionaryProperty["salt"] = __InputTagHelper2.IntDictionaryProperty["salt"];
|
||||
__InputTagHelper1.IntDictionaryProperty["pepper"] = __InputTagHelper2.IntDictionaryProperty["pepper"];
|
||||
__InputTagHelper1.StringProperty = __InputTagHelper2.StringDictionaryProperty["grabber"];
|
||||
if (__InputTagHelper1.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-paprika", "InputTagHelper1", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper1.StringDictionaryProperty["paprika"] = __InputTagHelper2.StringDictionaryProperty["paprika"];
|
||||
__InputTagHelper1.StringDictionaryProperty["cumin"] = __InputTagHelper2.StringDictionaryProperty["cumin"];
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("radio"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(795, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
if (__InputTagHelper2.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-value", "InputTagHelper2", "IntDictionaryProperty"));
|
||||
}
|
||||
#line 22 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper2.IntDictionaryProperty["value"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-value", __InputTagHelper2.IntDictionaryProperty["value"]);
|
||||
if (__InputTagHelper2.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-thyme", "InputTagHelper2", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper2.StringDictionaryProperty["thyme"] = "string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-thyme", __InputTagHelper2.StringDictionaryProperty["thyme"]);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
if (__InputTagHelper1.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-value", "InputTagHelper1", "IntDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper1.IntDictionaryProperty["value"] = __InputTagHelper2.IntDictionaryProperty["value"];
|
||||
if (__InputTagHelper1.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-thyme", "InputTagHelper1", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper1.StringDictionaryProperty["thyme"] = __InputTagHelper2.StringDictionaryProperty["thyme"];
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(861, 8, true);
|
||||
WriteLiteral("\r\n</div>");
|
||||
Instrumentation.EndContext();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,231 @@
|
|||
#pragma checksum "PrefixedAttributeTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "4e7fe9697b745af1a07d41f6a8532fdc288fa046"
|
||||
namespace TestOutput
|
||||
{
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class PrefixedAttributeTagHelpers
|
||||
{
|
||||
#line hidden
|
||||
#pragma warning disable 0414
|
||||
private TagHelperContent __tagHelperStringValueBuffer = null;
|
||||
#pragma warning restore 0414
|
||||
private TagHelperExecutionContext __tagHelperExecutionContext = null;
|
||||
private TagHelperRunner __tagHelperRunner = null;
|
||||
private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
|
||||
private InputTagHelper1 __InputTagHelper1 = null;
|
||||
private InputTagHelper2 __InputTagHelper2 = null;
|
||||
#line hidden
|
||||
public PrefixedAttributeTagHelpers()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
__tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
|
||||
Instrumentation.BeginContext(33, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
#line 3 "PrefixedAttributeTagHelpers.cshtml"
|
||||
|
||||
var literate = "or illiterate";
|
||||
var intDictionary = new Dictionary<string, int>
|
||||
{
|
||||
{ "three", 3 },
|
||||
};
|
||||
var stringDictionary = new SortedDictionary<string, string>
|
||||
{
|
||||
{ "name", "value" },
|
||||
};
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
Instrumentation.BeginContext(280, 51, true);
|
||||
WriteLiteral("\r\n\r\n<div class=\"randomNonTagHelperAttribute\">\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
#line 16 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty = intDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-dictionary", __InputTagHelper1.IntDictionaryProperty);
|
||||
#line 16 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.StringDictionaryProperty = stringDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-dictionary", __InputTagHelper1.StringDictionaryProperty);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
__InputTagHelper2.IntDictionaryProperty = __InputTagHelper1.IntDictionaryProperty;
|
||||
__InputTagHelper2.StringDictionaryProperty = __InputTagHelper1.StringDictionaryProperty;
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("checkbox"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(423, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty = intDictionary;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-dictionary", __InputTagHelper1.IntDictionaryProperty);
|
||||
if (__InputTagHelper1.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-garlic", "InputTagHelper1", "IntDictionaryProperty"));
|
||||
}
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["garlic"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-garlic", __InputTagHelper1.IntDictionaryProperty["garlic"]);
|
||||
#line 17 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntProperty = 42;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-grabber", __InputTagHelper1.IntProperty);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
__InputTagHelper2.IntDictionaryProperty = __InputTagHelper1.IntDictionaryProperty;
|
||||
if (__InputTagHelper2.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-garlic", "InputTagHelper2", "IntDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper2.IntDictionaryProperty["garlic"] = __InputTagHelper1.IntDictionaryProperty["garlic"];
|
||||
__InputTagHelper2.IntDictionaryProperty["grabber"] = __InputTagHelper1.IntProperty;
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("password"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(532, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntProperty = 42;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-grabber", __InputTagHelper1.IntProperty);
|
||||
if (__InputTagHelper1.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-salt", "InputTagHelper1", "IntDictionaryProperty"));
|
||||
}
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["salt"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-salt", __InputTagHelper1.IntDictionaryProperty["salt"]);
|
||||
#line 19 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["pepper"] = 98;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-pepper", __InputTagHelper1.IntDictionaryProperty["pepper"]);
|
||||
__InputTagHelper1.StringProperty = "string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-grabber", __InputTagHelper1.StringProperty);
|
||||
if (__InputTagHelper1.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-paprika", "InputTagHelper1", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper1.StringDictionaryProperty["paprika"] = "another string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-paprika", __InputTagHelper1.StringDictionaryProperty["paprika"]);
|
||||
StartTagHelperWritingScope();
|
||||
WriteLiteral("literate ");
|
||||
#line 21 "PrefixedAttributeTagHelpers.cshtml"
|
||||
WriteLiteral(literate);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
WriteLiteral("?");
|
||||
__tagHelperStringValueBuffer = EndTagHelperWritingScope();
|
||||
__InputTagHelper1.StringDictionaryProperty["cumin"] = __tagHelperStringValueBuffer.ToString();
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-cumin", __InputTagHelper1.StringDictionaryProperty["cumin"]);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
if (__InputTagHelper2.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-grabber", "InputTagHelper2", "IntDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper2.IntDictionaryProperty["grabber"] = __InputTagHelper1.IntProperty;
|
||||
__InputTagHelper2.IntDictionaryProperty["salt"] = __InputTagHelper1.IntDictionaryProperty["salt"];
|
||||
__InputTagHelper2.IntDictionaryProperty["pepper"] = __InputTagHelper1.IntDictionaryProperty["pepper"];
|
||||
if (__InputTagHelper2.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-grabber", "InputTagHelper2", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper2.StringDictionaryProperty["grabber"] = __InputTagHelper1.StringProperty;
|
||||
__InputTagHelper2.StringDictionaryProperty["paprika"] = __InputTagHelper1.StringDictionaryProperty["paprika"];
|
||||
__InputTagHelper2.StringDictionaryProperty["cumin"] = __InputTagHelper1.StringDictionaryProperty["cumin"];
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("type", Html.Raw("radio"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(795, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__InputTagHelper1 = CreateTagHelper<InputTagHelper1>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper1);
|
||||
if (__InputTagHelper1.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-value", "InputTagHelper1", "IntDictionaryProperty"));
|
||||
}
|
||||
#line 22 "PrefixedAttributeTagHelpers.cshtml"
|
||||
__InputTagHelper1.IntDictionaryProperty["value"] = 37;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("int-prefix-value", __InputTagHelper1.IntDictionaryProperty["value"]);
|
||||
if (__InputTagHelper1.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-thyme", "InputTagHelper1", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper1.StringDictionaryProperty["thyme"] = "string";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("string-prefix-thyme", __InputTagHelper1.StringDictionaryProperty["thyme"]);
|
||||
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
|
||||
__tagHelperExecutionContext.Add(__InputTagHelper2);
|
||||
if (__InputTagHelper2.IntDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("int-prefix-value", "InputTagHelper2", "IntDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper2.IntDictionaryProperty["value"] = __InputTagHelper1.IntDictionaryProperty["value"];
|
||||
if (__InputTagHelper2.StringDictionaryProperty == null)
|
||||
{
|
||||
throw new InvalidOperationException(FormatInvalidIndexerAssignment("string-prefix-thyme", "InputTagHelper2", "StringDictionaryProperty"));
|
||||
}
|
||||
__InputTagHelper2.StringDictionaryProperty["thyme"] = __InputTagHelper1.StringDictionaryProperty["thyme"];
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(861, 8, true);
|
||||
WriteLiteral("\r\n</div>");
|
||||
Instrumentation.EndContext();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
@addTagHelper "something, nice"
|
||||
|
||||
@{
|
||||
var literate = "or illiterate";
|
||||
var intDictionary = new Dictionary<string, int>
|
||||
{
|
||||
{ "three", 3 },
|
||||
};
|
||||
var stringDictionary = new SortedDictionary<string, string>
|
||||
{
|
||||
{ "name", "value" },
|
||||
};
|
||||
}
|
||||
|
||||
<div class="randomNonTagHelperAttribute">
|
||||
<input type="checkbox" int-dictionary="intDictionary" string-dictionary="stringDictionary"/>
|
||||
<input type="password" int-dictionary="intDictionary" int-prefix-garlic="37" int-prefix-grabber="42" />
|
||||
<input type="radio"
|
||||
int-prefix-grabber="42" int-prefix-salt="37" int-prefix-pepper="98" int-prefix-salt="8"
|
||||
string-prefix-grabber="string" string-prefix-paprika="another string"
|
||||
string-prefix-cumin="literate @literate?"/>
|
||||
<input int-prefix-value="37" string-prefix-thyme="string" />
|
||||
</div>
|
||||
Loading…
Reference in New Issue