Add TagHelperOutput.Attributes and TagHelperContext.AllAttributes replacement.
- Added a TagHelperAttributes object that's used to hold 1=>many attributes. Is used for TagHelperOutput.Attributes. - Added a ReadOnlyTagHelperAttributes object that holds 1=>many IReadOnlyTagHelperAttributes. Is used for TagHelperContext.AllAttributes. - Added a TagHelperAttribute object which is used to represent attributes. - Added a IReadOnlyTagHelperAttribute which is used to represent attributes which cannot be modified. #279
This commit is contained in:
parent
de95f67400
commit
7c604d2b11
|
|
@ -218,6 +218,38 @@ namespace Microsoft.AspNet.Razor.Runtime
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidBoundAttributeName"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot add a '{0}' with a null '{1}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperAttributeList_CannotAddWithNullName
|
||||
{
|
||||
get { return GetString("TagHelperAttributeList_CannotAddWithNullName"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot add a '{0}' with a null '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperAttributeList_CannotAddWithNullName(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddWithNullName"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperAttributeList_CannotAddAttribute
|
||||
{
|
||||
get { return GetString("TagHelperAttributeList_CannotAddAttribute"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperAttributeList_CannotAddAttribute(object p0, object p1, object p2, object p3)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddAttribute"), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -156,4 +156,10 @@
|
|||
<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>
|
||||
<data name="TagHelperAttributeList_CannotAddWithNullName" xml:space="preserve">
|
||||
<value>Cannot add a '{0}' with a null '{1}'.</value>
|
||||
</data>
|
||||
<data name="TagHelperAttributeList_CannotAddAttribute" xml:space="preserve">
|
||||
<value>Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A read-only HTML tag helper attribute.
|
||||
/// </summary>
|
||||
public interface IReadOnlyTagHelperAttribute : IEquatable<IReadOnlyTagHelperAttribute>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the attribute.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the attribute.
|
||||
/// </summary>
|
||||
object Value { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A read-only collection of <typeparamref name="TAttribute"/>s.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute">
|
||||
/// The type of <see cref="IReadOnlyTagHelperAttribute"/>s in the collection.
|
||||
/// </typeparam>
|
||||
public class ReadOnlyTagHelperAttributeList<TAttribute> : IReadOnlyList<TAttribute>
|
||||
where TAttribute : IReadOnlyTagHelperAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList{TAttribute}"/> with an empty
|
||||
/// collection.
|
||||
/// </summary>
|
||||
protected ReadOnlyTagHelperAttributeList()
|
||||
{
|
||||
Attributes = new List<TAttribute>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="ReadOnlyTagHelperAttributeList{TAttribute}"/> with the specified
|
||||
/// <paramref name="attributes"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The collection to wrap.</param>
|
||||
public ReadOnlyTagHelperAttributeList([NotNull] IEnumerable<TAttribute> attributes)
|
||||
{
|
||||
Attributes = new List<TAttribute>(attributes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The underlying collection of <typeparamref name="TAttribute"/>s.
|
||||
/// </summary>
|
||||
/// <remarks>Intended for use in a non-read-only subclass. Changes to this <see cref="List{TAttribute}"/> will
|
||||
/// affect all getters that <see cref="ReadOnlyTagHelperAttributeList{TAttribute}"/> provides.</remarks>
|
||||
protected List<TAttribute> Attributes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TAttribute this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Attributes[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
|
||||
/// matching <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the <typeparamref name="TAttribute"/> to get.
|
||||
/// </param>
|
||||
/// <returns>The first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
|
||||
/// matching <paramref name="name"/>.
|
||||
/// </returns>
|
||||
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
|
||||
public TAttribute this[[NotNull] string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Attributes.FirstOrDefault(attribute => NameEquals(name, attribute));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return Attributes.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a <typeparamref name="TAttribute"/> matching <paramref name="item"/> exists in the
|
||||
/// collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The <typeparamref name="TAttribute"/> to locate.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if an <typeparamref name="TAttribute"/> matching <paramref name="item"/> exists in the
|
||||
/// collection; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <paramref name="item"/>s <see cref="IReadOnlyTagHelperAttribute.Name"/> is compared case-insensitively.
|
||||
/// </remarks>
|
||||
public bool Contains([NotNull] TAttribute item)
|
||||
{
|
||||
return Attributes.Contains(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a <typeparamref name="TAttribute"/> with the same
|
||||
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the
|
||||
/// <typeparamref name="TAttribute"/> to get.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if a <typeparamref name="TAttribute"/> with the same
|
||||
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
|
||||
public bool ContainsName([NotNull] string name)
|
||||
{
|
||||
return Attributes.Any(attribute => NameEquals(name, attribute));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a <typeparamref name="TAttribute"/> matching <paramref name="item"/> in the collection and
|
||||
/// returns the zero-based index of the first occurrence.
|
||||
/// </summary>
|
||||
/// <param name="item">The <typeparamref name="TAttribute"/> to locate.</param>
|
||||
/// <returns>The zero-based index of the first occurrence of a <typeparamref name="TAttribute"/> matching
|
||||
/// <paramref name="item"/> in the collection, if found; otherwise, –1.</returns>
|
||||
/// <remarks>
|
||||
/// <paramref name="item"/>s <see cref="IReadOnlyTagHelperAttribute.Name"/> is compared case-insensitively.
|
||||
/// </remarks>
|
||||
public int IndexOf([NotNull] TAttribute item)
|
||||
{
|
||||
return Attributes.IndexOf(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the first <typeparamref name="TAttribute"/> with <see cref="IReadOnlyTagHelperAttribute.Name"/>
|
||||
/// matching <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the
|
||||
/// <typeparamref name="TAttribute"/> to get.</param>
|
||||
/// <param name="attribute">When this method returns, the first <typeparamref name="TAttribute"/> with
|
||||
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> matching <paramref name="name"/>, if found; otherwise,
|
||||
/// <c>null</c>.</param>
|
||||
/// <returns><c>true</c> if a <typeparamref name="TAttribute"/> with the same
|
||||
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.</returns>
|
||||
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
|
||||
public bool TryGetAttribute([NotNull] string name, out TAttribute attribute)
|
||||
{
|
||||
attribute = Attributes.FirstOrDefault(attr => NameEquals(name, attr));
|
||||
|
||||
return attribute != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves <typeparamref name="TAttribute"/>s in the collection with
|
||||
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> matching <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="IReadOnlyTagHelperAttribute.Name"/> of the
|
||||
/// <typeparamref name="TAttribute"/>s to get.</param>
|
||||
/// <param name="attributes">When this method returns, the <typeparamref name="TAttribute"/>s with
|
||||
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> matching <paramref name="name"/>, if at least one is
|
||||
/// found; otherwise, <c>null</c>.</param>
|
||||
/// <returns><c>true</c> if at least one <typeparamref name="TAttribute"/> with the same
|
||||
/// <see cref="IReadOnlyTagHelperAttribute.Name"/> exists in the collection; otherwise, <c>false</c>.</returns>
|
||||
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
|
||||
public bool TryGetAttributes([NotNull] string name, out IEnumerable<TAttribute> attributes)
|
||||
{
|
||||
attributes = Attributes.Where(attribute => NameEquals(name, attribute));
|
||||
|
||||
return attributes.Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<TAttribute> GetEnumerator()
|
||||
{
|
||||
return Attributes.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <paramref name="attribute"/> has the same name as <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The value to compare against <paramref name="attribute"/>s
|
||||
/// <see cref="TagHelperAttribute.Name"/>.</param>
|
||||
/// <param name="attribute">The attribute to compare against.</param>
|
||||
/// <returns><c>true</c> if <paramref name="name"/> case-insensitively matches <paramref name="attribute"/>s
|
||||
/// <see cref="TagHelperAttribute.Name"/>.</returns>
|
||||
protected static bool NameEquals(string name, [NotNull] TAttribute attribute)
|
||||
{
|
||||
return string.Equals(name, attribute.Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Internal.Web.Utils;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// An HTML tag helper attribute.
|
||||
/// </summary>
|
||||
public class TagHelperAttribute : IReadOnlyTagHelperAttribute
|
||||
{
|
||||
private static readonly int TypeHashCode = typeof(TagHelperAttribute).GetHashCode();
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="TagHelperAttribute"/>.
|
||||
/// </summary>
|
||||
public TagHelperAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="TagHelperAttribute"/> with the specified <paramref name="name"/>
|
||||
/// and <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="Name"/> of the attribute.</param>
|
||||
/// <param name="value">The <see cref="Value"/> of the attribute.</param>
|
||||
public TagHelperAttribute(string name, object value)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the attribute.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the attribute.
|
||||
/// </summary>
|
||||
public object Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified <paramref name="value"/> into a <see cref="TagHelperAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The <see cref="Value"/> of the created <see cref="TagHelperAttribute"/>.</param>
|
||||
/// <remarks>Created <see cref="TagHelperAttribute"/>s <see cref="Name"/> is set to <c>null</c>.</remarks>
|
||||
public static implicit operator TagHelperAttribute(string value)
|
||||
{
|
||||
return new TagHelperAttribute
|
||||
{
|
||||
Value = value
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks><see cref="Name"/> is compared case-insensitively.</remarks>
|
||||
public bool Equals(IReadOnlyTagHelperAttribute other)
|
||||
{
|
||||
return
|
||||
other != null &&
|
||||
string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase) &&
|
||||
Equals(Value, other.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj as IReadOnlyTagHelperAttribute;
|
||||
|
||||
return Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return TypeHashCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of <see cref="TagHelperAttribute"/>s.
|
||||
/// </summary>
|
||||
public class TagHelperAttributeList : ReadOnlyTagHelperAttributeList<TagHelperAttribute>, IList<TagHelperAttribute>
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="TagHelperAttributeList"/> with an empty collection.
|
||||
/// </summary>
|
||||
public TagHelperAttributeList()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="TagHelperAttributeList"/> with the specified
|
||||
/// <paramref name="attributes"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The collection to wrap.</param>
|
||||
public TagHelperAttributeList([NotNull] IEnumerable<TagHelperAttribute> attributes)
|
||||
: base(attributes)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <paramref name="value"/>'s <see cref="TagHelperAttribute.Name"/> must not be <c>null</c>.
|
||||
/// </remarks>
|
||||
public new TagHelperAttribute this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return base[index];
|
||||
}
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
if (value.Name == null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatTagHelperAttributeList_CannotAddWithNullName(
|
||||
typeof(TagHelperAttribute).FullName,
|
||||
nameof(TagHelperAttribute.Name)),
|
||||
nameof(value));
|
||||
}
|
||||
|
||||
Attributes[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/> matching
|
||||
/// <paramref name="name"/>. When setting, replaces the first matching
|
||||
/// <see cref="TagHelperAttribute"/> with the specified <paramref name="value"/> and removes any additional
|
||||
/// matching <see cref="TagHelperAttribute"/>s. If a matching <see cref="TagHelperAttribute"/> is not found,
|
||||
/// adds the specified <paramref name="value"/> to the end of the collection.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The <see cref="TagHelperAttribute.Name"/> of the <see cref="TagHelperAttribute"/> to get or set.
|
||||
/// </param>
|
||||
/// <returns>The first <see cref="TagHelperAttribute"/> with <see cref="TagHelperAttribute.Name"/> matching
|
||||
/// <paramref name="name"/>.
|
||||
/// </returns>
|
||||
/// <remarks><paramref name="name"/> is compared case-insensitively. When setting,
|
||||
/// <see cref="TagHelperAttribute"/>s <see cref="TagHelperAttribute.Name"/> must be <c>null</c> or
|
||||
/// case-insensitively match the specified <paramref name="name"/>.</remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var attributes = new TagHelperAttributeList();
|
||||
///
|
||||
/// // Will "value" be converted to a TagHelperAttribute with a null Name
|
||||
/// attributes["name"] = "value";
|
||||
///
|
||||
/// // TagHelperAttribute.Name must match the specified name.
|
||||
/// attributes["name"] = new TagHelperAttribute("name", "value");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public new TagHelperAttribute this[[NotNull] string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return base[name];
|
||||
}
|
||||
[param: NotNull]
|
||||
set
|
||||
{
|
||||
// Name will be null if user attempts to set the attribute via an implicit conversion:
|
||||
// output.Attributes["someName"] = "someValue"
|
||||
if (value.Name == null)
|
||||
{
|
||||
value.Name = name;
|
||||
}
|
||||
else if (!NameEquals(name, value))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatTagHelperAttributeList_CannotAddAttribute(
|
||||
nameof(TagHelperAttribute),
|
||||
nameof(TagHelperAttribute.Name),
|
||||
value.Name,
|
||||
name),
|
||||
nameof(name));
|
||||
}
|
||||
|
||||
var attributeReplaced = false;
|
||||
|
||||
for (var i = 0; i < Attributes.Count; i++)
|
||||
{
|
||||
if (NameEquals(name, Attributes[i]))
|
||||
{
|
||||
// We replace the first attribute with the provided value, remove all the rest.
|
||||
if (!attributeReplaced)
|
||||
{
|
||||
// We replace the first attribute we find with the same name.
|
||||
Attributes[i] = value;
|
||||
attributeReplaced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Attributes.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't replace an attribute value we should add value to the end of the collection.
|
||||
if (!attributeReplaced)
|
||||
{
|
||||
Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
bool ICollection<TagHelperAttribute>.IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="TagHelperAttribute"/> to the end of the collection with the specified
|
||||
/// <paramref name="name"/> and <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="TagHelperAttribute.Name"/> of the attribute to add.</param>
|
||||
/// <param name="value">The <see cref="TagHelperAttribute.Value"/> of the attribute to add.</param>
|
||||
public void Add([NotNull] string name, object value)
|
||||
{
|
||||
Attributes.Add(new TagHelperAttribute(name, value));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <paramref name="attribute"/>'s <see cref="TagHelperAttribute.Name"/> must not be <c>null</c>.
|
||||
/// </remarks>
|
||||
public void Add([NotNull] TagHelperAttribute attribute)
|
||||
{
|
||||
if (attribute.Name == null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatTagHelperAttributeList_CannotAddWithNullName(
|
||||
typeof(TagHelperAttribute).FullName,
|
||||
nameof(TagHelperAttribute.Name)),
|
||||
nameof(attribute));
|
||||
}
|
||||
|
||||
Attributes.Add(attribute);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <paramref name="attribute"/>'s <see cref="TagHelperAttribute.Name"/> must not be <c>null</c>.
|
||||
/// </remarks>
|
||||
public void Insert(int index, [NotNull] TagHelperAttribute attribute)
|
||||
{
|
||||
if (attribute.Name == null)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatTagHelperAttributeList_CannotAddWithNullName(
|
||||
typeof(TagHelperAttribute).FullName,
|
||||
nameof(TagHelperAttribute.Name)),
|
||||
nameof(attribute));
|
||||
}
|
||||
|
||||
Attributes.Insert(index, attribute);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo([NotNull] TagHelperAttribute[] array, int index)
|
||||
{
|
||||
Attributes.CopyTo(array, index);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// <paramref name="attribute"/>s <see cref="TagHelperAttribute.Name"/> is compared case-insensitively.
|
||||
/// </remarks>
|
||||
public bool Remove([NotNull] TagHelperAttribute attribute)
|
||||
{
|
||||
return Attributes.Remove(attribute);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
Attributes.RemoveAt(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all <see cref="TagHelperAttribute"/>s with <see cref="TagHelperAttribute.Name"/> matching
|
||||
/// <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">
|
||||
/// The <see cref="TagHelperAttribute.Name"/> of <see cref="TagHelperAttribute"/>s to remove.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if at least 1 <see cref="TagHelperAttribute"/> was removed; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
/// <remarks><paramref name="name"/> is compared case-insensitively.</remarks>
|
||||
public bool RemoveAll([NotNull] string name)
|
||||
{
|
||||
return Attributes.RemoveAll(attribute => NameEquals(name, attribute)) > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
Attributes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
|
|
@ -25,12 +26,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <param name="getChildContentAsync">A delegate used to execute and retrieve the rendered child content
|
||||
/// asynchronously.</param>
|
||||
public TagHelperContext(
|
||||
[NotNull] IDictionary<string, object> allAttributes,
|
||||
[NotNull] IEnumerable<IReadOnlyTagHelperAttribute> allAttributes,
|
||||
[NotNull] IDictionary<object, object> items,
|
||||
[NotNull] string uniqueId,
|
||||
[NotNull] Func<Task<TagHelperContent>> getChildContentAsync)
|
||||
{
|
||||
AllAttributes = allAttributes;
|
||||
AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
|
||||
allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value)));
|
||||
Items = items;
|
||||
UniqueId = uniqueId;
|
||||
_getChildContentAsync = getChildContentAsync;
|
||||
|
|
@ -39,7 +41,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <summary>
|
||||
/// Every attribute associated with the current HTML element.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AllAttributes { get; }
|
||||
public ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute> AllAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
_endTagHelperWritingScope = endTagHelperWritingScope;
|
||||
|
||||
SelfClosing = selfClosing;
|
||||
AllAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
HTMLAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
HTMLAttributes = new TagHelperAttributeList();
|
||||
AllAttributes = new TagHelperAttributeList();
|
||||
TagName = tagName;
|
||||
Items = items;
|
||||
UniqueId = uniqueId;
|
||||
|
|
@ -91,12 +91,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <summary>
|
||||
/// HTML attributes.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> HTMLAttributes { get; }
|
||||
public TagHelperAttributeList HTMLAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/> bound attributes and HTML attributes.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AllAttributes { get; }
|
||||
public TagHelperAttributeList AllAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An identifier unique to the HTML element this context is for.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
// Internal for testing
|
||||
internal TagHelperOutput(string tagName)
|
||||
: this(tagName, new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase))
|
||||
: this(tagName, new TagHelperAttributeList())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -25,10 +25,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <param name="attributes">The HTML attributes.</param>
|
||||
public TagHelperOutput(
|
||||
string tagName,
|
||||
[NotNull] IDictionary<string, object> attributes)
|
||||
[NotNull] TagHelperAttributeList attributes)
|
||||
{
|
||||
TagName = tagName;
|
||||
Attributes = new Dictionary<string, object>(attributes, StringComparer.OrdinalIgnoreCase);
|
||||
Attributes = new TagHelperAttributeList(attributes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -94,7 +94,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// a <c>Microsoft.AspNet.Mvc.Rendering.HtmlString</c> instance. MVC converts most other types to a
|
||||
/// <see cref="string"/>, then HTML encodes the result.
|
||||
/// </remarks>
|
||||
public IDictionary<string, object> Attributes { get; }
|
||||
public TagHelperAttributeList Attributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes <see cref="TagHelperOutput"/> to generate nothing.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Microsoft.Internal.Web.Utils
|
||||
{
|
||||
internal class HashCodeCombiner
|
||||
public class HashCodeCombiner
|
||||
{
|
||||
private long _combinedHash64 = 0x1505L;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
{
|
||||
TagName = source.TagName;
|
||||
Descriptors = source.Descriptors;
|
||||
Attributes = new Dictionary<string, SyntaxTreeNode>(source.Attributes);
|
||||
Attributes = new List<KeyValuePair<string, SyntaxTreeNode>>(source.Attributes);
|
||||
_start = source.Start;
|
||||
SelfClosing = source.SelfClosing;
|
||||
SourceStartTag = source.SourceStartTag;
|
||||
|
|
@ -36,9 +36,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
|
||||
source.Reset();
|
||||
|
||||
foreach (var attributeChildren in Attributes.Values)
|
||||
foreach (var attributeChildren in Attributes)
|
||||
{
|
||||
attributeChildren.Parent = this;
|
||||
attributeChildren.Value.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
/// <summary>
|
||||
/// The HTML attributes.
|
||||
/// </summary>
|
||||
public IDictionary<string, SyntaxTreeNode> Attributes { get; private set; }
|
||||
public IList<KeyValuePair<string, SyntaxTreeNode>> Attributes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SourceLocation Start
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
{
|
||||
TagName = original.TagName;
|
||||
Descriptors = original.Descriptors;
|
||||
Attributes = new Dictionary<string, SyntaxTreeNode>(original.Attributes);
|
||||
Attributes = new List<KeyValuePair<string, SyntaxTreeNode>>(original.Attributes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -39,26 +39,28 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
/// <param name="attributes">Attributes of the <see cref="TagHelperBlock"/>.</param>
|
||||
/// <param name="descriptors">The <see cref="TagHelperDescriptor"/>s associated with the current HTML
|
||||
/// tag.</param>
|
||||
public TagHelperBlockBuilder(string tagName,
|
||||
bool selfClosing,
|
||||
SourceLocation start,
|
||||
IDictionary<string, SyntaxTreeNode> attributes,
|
||||
IEnumerable<TagHelperDescriptor> descriptors)
|
||||
public TagHelperBlockBuilder(
|
||||
string tagName,
|
||||
bool selfClosing,
|
||||
SourceLocation start,
|
||||
IList<KeyValuePair<string, SyntaxTreeNode>> attributes,
|
||||
IEnumerable<TagHelperDescriptor> descriptors)
|
||||
{
|
||||
TagName = tagName;
|
||||
SelfClosing = selfClosing;
|
||||
Start = start;
|
||||
Descriptors = descriptors;
|
||||
Attributes = new Dictionary<string, SyntaxTreeNode>(attributes);
|
||||
Attributes = new List<KeyValuePair<string, SyntaxTreeNode>>(attributes);
|
||||
Type = BlockType.Tag;
|
||||
CodeGenerator = new TagHelperCodeGenerator(descriptors);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal TagHelperBlockBuilder(string tagName,
|
||||
bool selfClosing,
|
||||
IDictionary<string, SyntaxTreeNode> attributes,
|
||||
IEnumerable<SyntaxTreeNode> children)
|
||||
internal TagHelperBlockBuilder(
|
||||
string tagName,
|
||||
bool selfClosing,
|
||||
IList<KeyValuePair<string, SyntaxTreeNode>> attributes,
|
||||
IEnumerable<SyntaxTreeNode> children)
|
||||
{
|
||||
TagName = tagName;
|
||||
SelfClosing = selfClosing;
|
||||
|
|
@ -98,7 +100,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers
|
|||
/// <summary>
|
||||
/// The HTML attributes.
|
||||
/// </summary>
|
||||
public IDictionary<string, SyntaxTreeNode> Attributes { get; private set; }
|
||||
public IList<KeyValuePair<string, SyntaxTreeNode>> Attributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The HTML tag name.
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
return new TagHelperBlockBuilder(tagName, selfClosing, start, attributes, descriptors);
|
||||
}
|
||||
|
||||
private static IDictionary<string, SyntaxTreeNode> GetTagAttributes(
|
||||
private static IList<KeyValuePair<string, SyntaxTreeNode>> GetTagAttributes(
|
||||
string tagName,
|
||||
bool validStructure,
|
||||
Block tagBlock,
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var attributes = new Dictionary<string, SyntaxTreeNode>(StringComparer.OrdinalIgnoreCase);
|
||||
var attributes = new List<KeyValuePair<string, SyntaxTreeNode>>();
|
||||
|
||||
// Build a dictionary so we can easily lookup expected attribute value lookups
|
||||
IReadOnlyDictionary<string, string> attributeValueTypes =
|
||||
|
|
@ -88,7 +88,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
attribute.Key.Length);
|
||||
}
|
||||
|
||||
attributes[attribute.Key] = attribute.Value;
|
||||
attributes.Add(new KeyValuePair<string, SyntaxTreeNode>(attribute.Key, attribute.Value));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,13 +137,13 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
|
||||
if (afterEquals)
|
||||
{
|
||||
// We've captured all leading whitespace, the attribute name, and an equals with an optional
|
||||
// We've captured all leading whitespace, the attribute name, and an equals with an optional
|
||||
// quote/double quote. We're now at: " asp-for='|...'" or " asp-for=|..."
|
||||
// The goal here is to capture all symbols until the end of the attribute. Note this will not
|
||||
// The goal here is to capture all symbols until the end of the attribute. Note this will not
|
||||
// consume an ending quote due to the symbolOffset.
|
||||
|
||||
// When symbols are accepted into SpanBuilders, their locations get altered to be offset by the
|
||||
// parent which is why we need to mark our start location prior to adding the symbol.
|
||||
// When symbols are accepted into SpanBuilders, their locations get altered to be offset by the
|
||||
// parent which is why we need to mark our start location prior to adding the symbol.
|
||||
// This is needed to know the location of the attribute value start within the document.
|
||||
if (!capturedAttributeValueStart)
|
||||
{
|
||||
|
|
@ -206,10 +206,10 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
}
|
||||
else if (symbol.Type == HtmlSymbolType.WhiteSpace)
|
||||
{
|
||||
// We're at the start of the attribute, this branch may be hit on the first iterations of
|
||||
// We're at the start of the attribute, this branch may be hit on the first iterations of
|
||||
// the loop since the parser separates attributes with their spaces included as symbols.
|
||||
// We're at: "| asp-for='...'" or "| asp-for=..."
|
||||
// Note: This will not be hit even for situations like asp-for ="..." because the core Razor
|
||||
// Note: This will not be hit even for situations like asp-for ="..." because the core Razor
|
||||
// parser currently does not know how to handle attributes in that format. This will be addressed
|
||||
// by https://github.com/aspnet/Razor/issues/123.
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
{
|
||||
errorSink.OnError(
|
||||
span.Start,
|
||||
RazorResources.TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed,
|
||||
RazorResources.TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed,
|
||||
span.Content.Length);
|
||||
}
|
||||
|
||||
|
|
@ -375,7 +375,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
else if (isDynamic && childSpan.CodeGenerator == SpanCodeGenerator.Null)
|
||||
{
|
||||
// Usually the dynamic code generator handles rendering the null code generators underneath
|
||||
// it. This doesn't make sense in terms of tag helpers though, we need to change null code
|
||||
// it. This doesn't make sense in terms of tag helpers though, we need to change null code
|
||||
// generators to markup code generators.
|
||||
|
||||
newCodeGenerator = new MarkupCodeGenerator();
|
||||
|
|
|
|||
|
|
@ -1369,17 +1369,17 @@ namespace Microsoft.AspNet.Razor
|
|||
/// <summary>
|
||||
/// TagHelper attributes must be welformed.
|
||||
/// </summary>
|
||||
internal static string TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed
|
||||
internal static string TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed
|
||||
{
|
||||
get { return GetString("TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed"); }
|
||||
get { return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TagHelper attributes must be welformed.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperBlockRewriter_TagHelperAttributesMustBeWelformed()
|
||||
internal static string FormatTagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed()
|
||||
{
|
||||
return GetString("TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed");
|
||||
return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ 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_TagHelperAttributesMustBeWelformed" xml:space="preserve">
|
||||
<data name="TagHelperBlockRewriter_TagHelperAttributeListMustBeWelformed" xml:space="preserve">
|
||||
<value>TagHelper attributes must be welformed.</value>
|
||||
</data>
|
||||
<data name="TagHelpers_AttributeExpressionRequired" xml:space="preserve">
|
||||
|
|
|
|||
Loading…
Reference in New Issue