React to aspnet/Razor#89 fix
- use `IDictionary<string, TValue>` support in `<a/>` and `<form/>` tag helpers - make `RouteValues` dictionaries `IDictionary<string, string>` for ease of use - remove `TagHelperOutputExtensions.FindPrefixedAttributes()` - set all `GeneratedTagHelperContext` properties - add error for tag helper dictionary properties where `TValue` is `ModelExpression` - add new `RazorPage.InvalidTagHelperIndexerAssignment()` method and resource tests - use new `isIndexer` argument when creating `TagHelperAttributeDescriptor` - arrange `AnchorTagHelper` and `FormTagHelper` correctly - also expect `routeValues != null` in calls to `IHtmlGenerator` nits: - get rid of some `foo` and `bar` gunk in tests - remove unused variable to cleanup a test compilation warning
This commit is contained in:
parent
337bbad51d
commit
517c013882
|
|
@ -72,6 +72,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
ExecutionContextAddTagHelperAttributeMethodName =
|
ExecutionContextAddTagHelperAttributeMethodName =
|
||||||
nameof(TagHelperExecutionContext.AddTagHelperAttribute),
|
nameof(TagHelperExecutionContext.AddTagHelperAttribute),
|
||||||
ExecutionContextAddHtmlAttributeMethodName = nameof(TagHelperExecutionContext.AddHtmlAttribute),
|
ExecutionContextAddHtmlAttributeMethodName = nameof(TagHelperExecutionContext.AddHtmlAttribute),
|
||||||
|
ExecutionContextAddMinimizedHtmlAttributeMethodName =
|
||||||
|
nameof(TagHelperExecutionContext.AddMinimizedHtmlAttribute),
|
||||||
ExecutionContextOutputPropertyName = nameof(TagHelperExecutionContext.Output),
|
ExecutionContextOutputPropertyName = nameof(TagHelperExecutionContext.Output),
|
||||||
|
|
||||||
RunnerTypeName = typeof(TagHelperRunner).FullName,
|
RunnerTypeName = typeof(TagHelperRunner).FullName,
|
||||||
|
|
@ -85,6 +87,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
// Can't use nameof because RazorPage is not accessible here.
|
// Can't use nameof because RazorPage is not accessible here.
|
||||||
CreateTagHelperMethodName = "CreateTagHelper",
|
CreateTagHelperMethodName = "CreateTagHelper",
|
||||||
|
FormatInvalidIndexerAssignmentMethodName = "InvalidTagHelperIndexerAssignment",
|
||||||
StartTagHelperWritingScopeMethodName = "StartTagHelperWritingScope",
|
StartTagHelperWritingScopeMethodName = "StartTagHelperWritingScope",
|
||||||
EndTagHelperWritingScopeMethodName = "EndTagHelperWritingScope",
|
EndTagHelperWritingScopeMethodName = "EndTagHelperWritingScope",
|
||||||
|
|
||||||
|
|
@ -211,7 +214,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
sourceFileName = _pathNormalizer.NormalizePath(sourceFileName);
|
sourceFileName = _pathNormalizer.NormalizePath(sourceFileName);
|
||||||
|
|
||||||
var inheritedCodeTrees = ChunkInheritanceUtility.GetInheritedCodeTrees(sourceFileName);
|
var inheritedCodeTrees = ChunkInheritanceUtility.GetInheritedCodeTrees(sourceFileName);
|
||||||
return new MvcRazorParser(razorParser, inheritedCodeTrees, DefaultInheritedChunks);
|
return new MvcRazorParser(razorParser, inheritedCodeTrees, DefaultInheritedChunks, ModelExpressionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -224,7 +227,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
public override CodeBuilder DecorateCodeBuilder([NotNull] CodeBuilder incomingBuilder,
|
public override CodeBuilder DecorateCodeBuilder([NotNull] CodeBuilder incomingBuilder,
|
||||||
[NotNull] CodeBuilderContext context)
|
[NotNull] CodeBuilderContext context)
|
||||||
{
|
{
|
||||||
// Need the normalized path to resolve inherited chunks only. Full paths are needed for generated Razor
|
// Need the normalized path to resolve inherited chunks only. Full paths are needed for generated Razor
|
||||||
// files checksum and line pragmas to enable DesignTime debugging.
|
// files checksum and line pragmas to enable DesignTime debugging.
|
||||||
var normalizedPath = _pathNormalizer.NormalizePath(context.SourceFile);
|
var normalizedPath = _pathNormalizer.NormalizePath(context.SourceFile);
|
||||||
var inheritedChunks = ChunkInheritanceUtility.GetInheritedCodeTrees(normalizedPath);
|
var inheritedChunks = ChunkInheritanceUtility.GetInheritedCodeTrees(normalizedPath);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.Razor.Host;
|
||||||
using Microsoft.AspNet.Razor;
|
using Microsoft.AspNet.Razor;
|
||||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||||
using Microsoft.AspNet.Razor.Parser;
|
using Microsoft.AspNet.Razor.Parser;
|
||||||
|
|
@ -20,6 +22,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
public class MvcRazorParser : RazorParser
|
public class MvcRazorParser : RazorParser
|
||||||
{
|
{
|
||||||
private readonly IEnumerable<TagHelperDirectiveDescriptor> _globalImportDirectiveDescriptors;
|
private readonly IEnumerable<TagHelperDirectiveDescriptor> _globalImportDirectiveDescriptors;
|
||||||
|
private readonly string _modelExpressionTypeName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="MvcRazorParser"/>.
|
/// Initializes a new instance of <see cref="MvcRazorParser"/>.
|
||||||
|
|
@ -32,13 +35,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
public MvcRazorParser(
|
public MvcRazorParser(
|
||||||
[NotNull] RazorParser parser,
|
[NotNull] RazorParser parser,
|
||||||
[NotNull] IReadOnlyList<CodeTree> inheritedCodeTrees,
|
[NotNull] IReadOnlyList<CodeTree> inheritedCodeTrees,
|
||||||
[NotNull] IReadOnlyList<Chunk> defaultInheritedChunks)
|
[NotNull] IReadOnlyList<Chunk> defaultInheritedChunks,
|
||||||
|
[NotNull] string modelExpressionTypeName)
|
||||||
: base(parser)
|
: base(parser)
|
||||||
{
|
{
|
||||||
// Construct tag helper descriptors from @addTagHelper, @removeTagHelper and @tagHelperPrefix chunks
|
// Construct tag helper descriptors from @addTagHelper, @removeTagHelper and @tagHelperPrefix chunks
|
||||||
_globalImportDirectiveDescriptors = GetTagHelperDirectiveDescriptors(
|
_globalImportDirectiveDescriptors = GetTagHelperDirectiveDescriptors(
|
||||||
inheritedCodeTrees,
|
inheritedCodeTrees,
|
||||||
defaultInheritedChunks);
|
defaultInheritedChunks);
|
||||||
|
|
||||||
|
_modelExpressionTypeName = modelExpressionTypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -50,7 +56,27 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
TagHelperDescriptorResolver,
|
TagHelperDescriptorResolver,
|
||||||
_globalImportDirectiveDescriptors,
|
_globalImportDirectiveDescriptors,
|
||||||
errorSink);
|
errorSink);
|
||||||
return visitor.GetDescriptors(documentRoot);
|
|
||||||
|
var descriptors = visitor.GetDescriptors(documentRoot);
|
||||||
|
foreach (var descriptor in descriptors)
|
||||||
|
{
|
||||||
|
foreach (var attributeDescriptor in descriptor.Attributes)
|
||||||
|
{
|
||||||
|
if (attributeDescriptor.IsIndexer &&
|
||||||
|
string.Equals(
|
||||||
|
attributeDescriptor.TypeName,
|
||||||
|
_modelExpressionTypeName,
|
||||||
|
StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
errorSink.OnError(SourceLocation.Undefined, Resources.FormatMvcRazorParser_InvalidPropertyType(
|
||||||
|
descriptor.TypeName,
|
||||||
|
attributeDescriptor.Name,
|
||||||
|
_modelExpressionTypeName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<TagHelperDirectiveDescriptor> GetTagHelperDirectiveDescriptors(
|
private static IEnumerable<TagHelperDirectiveDescriptor> GetTagHelperDirectiveDescriptors(
|
||||||
|
|
@ -69,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var chunksInOrder = defaultInheritedChunks.Concat(chunksFromGlobalImports);
|
var chunksInOrder = defaultInheritedChunks.Concat(chunksFromGlobalImports);
|
||||||
foreach (var chunk in chunksInOrder)
|
foreach (var chunk in chunksInOrder)
|
||||||
{
|
{
|
||||||
// All TagHelperDirectiveDescriptors created here have undefined source locations because the source
|
// All TagHelperDirectiveDescriptors created here have undefined source locations because the source
|
||||||
// that created them is not in the same file.
|
// that created them is not in the same file.
|
||||||
var addTagHelperChunk = chunk as AddTagHelperChunk;
|
var addTagHelperChunk = chunk as AddTagHelperChunk;
|
||||||
if (addTagHelperChunk != null)
|
if (addTagHelperChunk != null)
|
||||||
|
|
@ -154,8 +180,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to see if the provided descriptors contain a @tagHelperPrefix directive. If so, it
|
// We need to see if the provided descriptors contain a @tagHelperPrefix directive. If so, it
|
||||||
// takes precedence and overrides any provided by the inheritedDescriptors. If not we need to add the
|
// takes precedence and overrides any provided by the inheritedDescriptors. If not we need to add the
|
||||||
// inherited @tagHelperPrefix directive back into the merged list.
|
// inherited @tagHelperPrefix directive back into the merged list.
|
||||||
if (prefixDirectiveDescriptor != null &&
|
if (prefixDirectiveDescriptor != null &&
|
||||||
!descriptors.Any(descriptor => descriptor.DirectiveType == TagHelperDirectiveType.TagHelperPrefix))
|
!descriptors.Any(descriptor => descriptor.DirectiveType == TagHelperDirectiveType.TagHelperPrefix))
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,22 @@ namespace Microsoft.AspNet.Mvc.Razor.Host
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("MvcRazorCodeParser_OnlyOneModelStatementIsAllowed"), p0);
|
return string.Format(CultureInfo.CurrentCulture, GetString("MvcRazorCodeParser_OnlyOneModelStatementIsAllowed"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper property '{0}.{1}'. Dictionary values must not be of type '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string MvcRazorParser_InvalidPropertyType
|
||||||
|
{
|
||||||
|
get { return GetString("MvcRazorParser_InvalidPropertyType"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper property '{0}.{1}'. Dictionary values must not be of type '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatMvcRazorParser_InvalidPropertyType(object p0, object p1, object p2)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("MvcRazorParser_InvalidPropertyType"), p0, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -135,4 +135,7 @@
|
||||||
<data name="MvcRazorCodeParser_OnlyOneModelStatementIsAllowed" xml:space="preserve">
|
<data name="MvcRazorCodeParser_OnlyOneModelStatementIsAllowed" xml:space="preserve">
|
||||||
<value>Only one '{0}' statement is allowed in a file.</value>
|
<value>Only one '{0}' statement is allowed in a file.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="MvcRazorParser_InvalidPropertyType" xml:space="preserve">
|
||||||
|
<value>Invalid tag helper property '{0}.{1}'. Dictionary values must not be of type '{2}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -410,6 +410,22 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
return GetString("GeneratedCodeFileName");
|
return GetString("GeneratedCodeFileName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unable to perform '{0}' assignment. Tag helper property '{1}.{2}' must not be null.
|
||||||
|
/// </summary>
|
||||||
|
internal static string RazorPage_InvalidTagHelperIndexerAssignment
|
||||||
|
{
|
||||||
|
get { return GetString("RazorPage_InvalidTagHelperIndexerAssignment"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unable to perform '{0}' assignment. Tag helper property '{1}.{2}' must not be null.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatRazorPage_InvalidTagHelperIndexerAssignment(object p0, object p1, object p2)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("RazorPage_InvalidTagHelperIndexerAssignment"), p0, p1, p2);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,24 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format an error message about using an indexer when the tag helper property is <c>null</c>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attributeName">Name of the HTML attribute associated with the indexer.</param>
|
||||||
|
/// <param name="tagHelperTypeName">Full name of the tag helper <see cref="Type"/>.</param>
|
||||||
|
/// <param name="propertyName">Dictionary property in the tag helper.</param>
|
||||||
|
/// <returns>An error message about using an indexer when the tag helper property is <c>null</c>.</returns>
|
||||||
|
public static string InvalidTagHelperIndexerAssignment(
|
||||||
|
string attributeName,
|
||||||
|
string tagHelperTypeName,
|
||||||
|
string propertyName)
|
||||||
|
{
|
||||||
|
return Resources.FormatRazorPage_InvalidTagHelperIndexerAssignment(
|
||||||
|
attributeName,
|
||||||
|
tagHelperTypeName,
|
||||||
|
propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and activates a <see cref="ITagHelper"/>.
|
/// Creates and activates a <see cref="ITagHelper"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</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>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
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
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
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
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
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
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -192,4 +192,7 @@
|
||||||
<data name="GeneratedCodeFileName" xml:space="preserve">
|
<data name="GeneratedCodeFileName" xml:space="preserve">
|
||||||
<value>Generated Code</value>
|
<value>Generated Code</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RazorPage_InvalidTagHelperIndexerAssignment" xml:space="preserve">
|
||||||
|
<value>Unable to perform '{0}' assignment. Tag helper property '{1}.{2}' must not be null.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -21,7 +21,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
private const string HostAttributeName = "asp-host";
|
private const string HostAttributeName = "asp-host";
|
||||||
private const string ProtocolAttributeName = "asp-protocol";
|
private const string ProtocolAttributeName = "asp-protocol";
|
||||||
private const string RouteAttributeName = "asp-route";
|
private const string RouteAttributeName = "asp-route";
|
||||||
private const string RouteAttributePrefix = "asp-route-";
|
private const string RouteValuesDictionaryName = "asp-all-route-data";
|
||||||
|
private const string RouteValuesPrefix = "asp-route-";
|
||||||
private const string Href = "href";
|
private const string Href = "href";
|
||||||
|
|
||||||
[Activate]
|
[Activate]
|
||||||
|
|
@ -69,6 +70,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
[HtmlAttributeName(RouteAttributeName)]
|
[HtmlAttributeName(RouteAttributeName)]
|
||||||
public string Route { get; set; }
|
public string Route { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional parameters for the route.
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]
|
||||||
|
public IDictionary<string, string> RouteValues { get; set; } =
|
||||||
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
/// <remarks>Does nothing if user provides an <c>href</c> attribute.</remarks>
|
/// <remarks>Does nothing if user provides an <c>href</c> attribute.</remarks>
|
||||||
/// <exception cref="InvalidOperationException">
|
/// <exception cref="InvalidOperationException">
|
||||||
|
|
@ -79,8 +87,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
var routePrefixedAttributes = output.FindPrefixedAttributes(RouteAttributePrefix);
|
|
||||||
|
|
||||||
// If "href" is already set, it means the user is attempting to use a normal anchor.
|
// If "href" is already set, it means the user is attempting to use a normal anchor.
|
||||||
if (output.Attributes.ContainsName(Href))
|
if (output.Attributes.ContainsName(Href))
|
||||||
{
|
{
|
||||||
|
|
@ -90,7 +96,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Protocol != null ||
|
Protocol != null ||
|
||||||
Host != null ||
|
Host != null ||
|
||||||
Fragment != null ||
|
Fragment != null ||
|
||||||
routePrefixedAttributes.Any())
|
RouteValues.Count != 0)
|
||||||
{
|
{
|
||||||
// User specified an href and one of the bound attributes; can't determine the href attribute.
|
// User specified an href and one of the bound attributes; can't determine the href attribute.
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
|
|
@ -102,15 +108,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
ProtocolAttributeName,
|
ProtocolAttributeName,
|
||||||
HostAttributeName,
|
HostAttributeName,
|
||||||
FragmentAttributeName,
|
FragmentAttributeName,
|
||||||
RouteAttributePrefix,
|
RouteValuesPrefix,
|
||||||
Href));
|
Href));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TagBuilder tagBuilder;
|
// Convert from Dictionary<string, string> to Dictionary<string, object>.
|
||||||
var routeValues = GetRouteValues(output, routePrefixedAttributes);
|
var routeValues = RouteValues.ToDictionary(
|
||||||
|
kvp => kvp.Key,
|
||||||
|
kvp => (object)kvp.Value,
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
TagBuilder tagBuilder;
|
||||||
if (Route == null)
|
if (Route == null)
|
||||||
{
|
{
|
||||||
tagBuilder = Generator.GenerateActionLink(linkText: string.Empty,
|
tagBuilder = Generator.GenerateActionLink(linkText: string.Empty,
|
||||||
|
|
@ -150,26 +160,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed.
|
|
||||||
private static Dictionary<string, object> GetRouteValues(
|
|
||||||
TagHelperOutput output,
|
|
||||||
IEnumerable<TagHelperAttribute> routePrefixedAttributes)
|
|
||||||
{
|
|
||||||
Dictionary<string, object> routeValues = null;
|
|
||||||
if (routePrefixedAttributes.Any())
|
|
||||||
{
|
|
||||||
// Prefixed values should be treated as bound attributes, remove them from the output.
|
|
||||||
output.RemoveRange(routePrefixedAttributes);
|
|
||||||
|
|
||||||
// Remove prefix from keys and convert all values to strings. HtmlString and similar classes are not
|
|
||||||
// meaningful to routing.
|
|
||||||
routeValues = routePrefixedAttributes.ToDictionary(
|
|
||||||
attribute => attribute.Name.Substring(RouteAttributePrefix.Length),
|
|
||||||
attribute => (object)attribute.Value.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return routeValues;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
private const string AntiForgeryAttributeName = "asp-anti-forgery";
|
private const string AntiForgeryAttributeName = "asp-anti-forgery";
|
||||||
private const string ControllerAttributeName = "asp-controller";
|
private const string ControllerAttributeName = "asp-controller";
|
||||||
private const string RouteAttributeName = "asp-route";
|
private const string RouteAttributeName = "asp-route";
|
||||||
private const string RouteAttributePrefix = "asp-route-";
|
private const string RouteValuesDictionaryName = "asp-all-route-data";
|
||||||
|
private const string RouteValuesPrefix = "asp-route-";
|
||||||
private const string HtmlActionAttributeName = "action";
|
private const string HtmlActionAttributeName = "action";
|
||||||
|
|
||||||
[Activate]
|
[Activate]
|
||||||
|
|
@ -57,6 +58,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
[HtmlAttributeName(RouteAttributeName)]
|
[HtmlAttributeName(RouteAttributeName)]
|
||||||
public string Route { get; set; }
|
public string Route { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional parameters for the route.
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]
|
||||||
|
public IDictionary<string, string> RouteValues { get; set; } =
|
||||||
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Does nothing if user provides an <c>action</c> attribute and <see cref="AntiForgery"/> is <c>null</c> or
|
/// Does nothing if user provides an <c>action</c> attribute and <see cref="AntiForgery"/> is <c>null</c> or
|
||||||
|
|
@ -69,12 +77,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
var antiForgeryDefault = true;
|
var antiForgeryDefault = true;
|
||||||
var routePrefixedAttributes = output.FindPrefixedAttributes(RouteAttributePrefix);
|
|
||||||
|
|
||||||
// If "action" is already set, it means the user is attempting to use a normal <form>.
|
// If "action" is already set, it means the user is attempting to use a normal <form>.
|
||||||
if (output.Attributes.ContainsName(HtmlActionAttributeName))
|
if (output.Attributes.ContainsName(HtmlActionAttributeName))
|
||||||
{
|
{
|
||||||
if (Action != null || Controller != null || Route != null || routePrefixedAttributes.Any())
|
if (Action != null || Controller != null || Route != null || RouteValues.Count != 0)
|
||||||
{
|
{
|
||||||
// User also specified bound attributes we cannot use.
|
// User also specified bound attributes we cannot use.
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
|
|
@ -84,7 +91,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
ActionAttributeName,
|
ActionAttributeName,
|
||||||
ControllerAttributeName,
|
ControllerAttributeName,
|
||||||
RouteAttributeName,
|
RouteAttributeName,
|
||||||
RouteAttributePrefix));
|
RouteValuesPrefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// User is using the FormTagHelper like a normal <form> tag. Anti-forgery default should be false to
|
// User is using the FormTagHelper like a normal <form> tag. Anti-forgery default should be false to
|
||||||
|
|
@ -93,8 +100,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Convert from Dictionary<string, string> to Dictionary<string, object>.
|
||||||
|
var routeValues = RouteValues.ToDictionary(
|
||||||
|
kvp => kvp.Key,
|
||||||
|
kvp => (object)kvp.Value,
|
||||||
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
TagBuilder tagBuilder;
|
TagBuilder tagBuilder;
|
||||||
var routeValues = GetRouteValues(output, routePrefixedAttributes);
|
|
||||||
if (Route == null)
|
if (Route == null)
|
||||||
{
|
{
|
||||||
tagBuilder = Generator.GenerateForm(
|
tagBuilder = Generator.GenerateForm(
|
||||||
|
|
@ -142,26 +154,5 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed.
|
|
||||||
private static Dictionary<string, object> GetRouteValues(
|
|
||||||
TagHelperOutput output,
|
|
||||||
IEnumerable<TagHelperAttribute> routePrefixedAttributes)
|
|
||||||
{
|
|
||||||
Dictionary<string, object> routeValues = null;
|
|
||||||
if (routePrefixedAttributes.Any())
|
|
||||||
{
|
|
||||||
// Prefixed values should be treated as bound attributes, remove them from the output.
|
|
||||||
output.RemoveRange(routePrefixedAttributes);
|
|
||||||
|
|
||||||
// Remove prefix from keys and convert all values to strings. HtmlString and similar classes are not
|
|
||||||
// meaningful to routing.
|
|
||||||
routeValues = routePrefixedAttributes.ToDictionary(
|
|
||||||
attribute => attribute.Name.Substring(RouteAttributePrefix.Length),
|
|
||||||
attribute => (object)attribute.Value.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return routeValues;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -51,28 +51,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns all attributes from <paramref name="tagHelperOutput"/>'s
|
|
||||||
/// <see cref="TagHelperOutput.Attributes"/> that have the given <paramref name="prefix"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tagHelperOutput">The <see cref="TagHelperOutput"/> this method extends.</param>
|
|
||||||
/// <param name="prefix">A prefix to look for.</param>
|
|
||||||
/// <returns><see cref="KeyValuePair{string, string}"/>s with <see cref="KeyValuePair{string, string}.Key"/>
|
|
||||||
/// starting with the given <paramref name="prefix"/>.</returns>
|
|
||||||
public static IEnumerable<TagHelperAttribute> FindPrefixedAttributes(
|
|
||||||
[NotNull] this TagHelperOutput tagHelperOutput,
|
|
||||||
[NotNull] string prefix)
|
|
||||||
{
|
|
||||||
// TODO: https://github.com/aspnet/Razor/issues/89 - We will not need this method once #89 is completed.
|
|
||||||
|
|
||||||
// We're only interested in HTML attributes that have the desired prefix.
|
|
||||||
var prefixedAttributes = tagHelperOutput.Attributes
|
|
||||||
.Where(attribute => attribute.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return prefixedAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Merges the given <paramref name="tagBuilder"/>'s <see cref="TagBuilder.Attributes"/> into the
|
/// Merges the given <paramref name="tagBuilder"/>'s <see cref="TagBuilder.Attributes"/> into the
|
||||||
/// <paramref name="tagHelperOutput"/>.
|
/// <paramref name="tagHelperOutput"/>.
|
||||||
|
|
|
||||||
|
|
@ -1071,9 +1071,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
|
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// The [DefaultValue] on ValueTypeRequiredWithDefaultValue is ignored by model binding.
|
|
||||||
var expectedValue = 0;
|
|
||||||
|
|
||||||
// Make ValueTypeRequired invalid.
|
// Make ValueTypeRequired invalid.
|
||||||
var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == nameof(Person.ValueTypeRequired));
|
var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == nameof(Person.ValueTypeRequired));
|
||||||
dto.Results[propertyMetadata] = new ModelBindingResult(
|
dto.Results[propertyMetadata] = new ModelBindingResult(
|
||||||
|
|
@ -1294,7 +1291,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.Equal(dob, model.DateOfBirth);
|
Assert.Equal(dob, model.DateOfBirth);
|
||||||
Assert.True(bindingContext.ModelState.IsValid);
|
Assert.True(bindingContext.ModelState.IsValid);
|
||||||
|
|
||||||
// Ensure that we add child nodes for all the nodes which have a result (irrespective of if they
|
// Ensure that we add child nodes for all the nodes which have a result (irrespective of if they
|
||||||
// are bound or not).
|
// are bound or not).
|
||||||
Assert.Equal(2, modelValidationNode.ChildNodes.Count());
|
Assert.Equal(2, modelValidationNode.ChildNodes.Count());
|
||||||
|
|
||||||
|
|
@ -1631,7 +1628,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
bindingContext.ModelState["foo"].Errors[0].Exception.Message);
|
bindingContext.ModelState["foo"].Errors[0].Exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can only really be done by writing an invalid model binder and returning 'isModelSet: true'
|
// This can only really be done by writing an invalid model binder and returning 'isModelSet: true'
|
||||||
// with a null model for a value type.
|
// with a null model for a value type.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SetProperty_SettingNonNullableValueTypeToNull_CapturesException()
|
public void SetProperty_SettingNonNullableValueTypeToNull_CapturesException()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
using Microsoft.AspNet.Razor;
|
using Microsoft.AspNet.Razor;
|
||||||
using Microsoft.AspNet.Razor.Generator.Compiler;
|
using Microsoft.AspNet.Razor.Generator.Compiler;
|
||||||
using Microsoft.AspNet.Razor.Parser;
|
using Microsoft.AspNet.Razor.Parser;
|
||||||
|
|
@ -230,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
public TestableMvcRazorParser(RazorParser parser,
|
public TestableMvcRazorParser(RazorParser parser,
|
||||||
IReadOnlyList<CodeTree> codeTrees,
|
IReadOnlyList<CodeTree> codeTrees,
|
||||||
IReadOnlyList<Chunk> defaultInheritedChunks)
|
IReadOnlyList<Chunk> defaultInheritedChunks)
|
||||||
: base(parser, codeTrees, defaultInheritedChunks)
|
: base(parser, codeTrees, defaultInheritedChunks, typeof(ModelExpression).FullName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
ModelExpressionTypeName = modelExpressionType,
|
ModelExpressionTypeName = modelExpressionType,
|
||||||
CreateModelExpressionMethodName = "SomeMethod"
|
CreateModelExpressionMethodName = "SomeMethod"
|
||||||
});
|
});
|
||||||
var attributeDescriptor = new TagHelperAttributeDescriptor("MyAttribute", "SomeProperty", propertyType);
|
var attributeDescriptor = new TagHelperAttributeDescriptor(
|
||||||
|
name: "MyAttribute",
|
||||||
|
propertyName: "SomeProperty",
|
||||||
|
typeName: propertyType,
|
||||||
|
isIndexer: false);
|
||||||
var writer = new CSharpCodeWriter();
|
var writer = new CSharpCodeWriter();
|
||||||
var generatorContext = new CodeGeneratorContext(host: null,
|
var generatorContext = new CodeGeneratorContext(host: null,
|
||||||
className: string.Empty,
|
className: string.Empty,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||||
using Microsoft.Framework.WebEncoders;
|
|
||||||
using Microsoft.Framework.WebEncoders.Testing;
|
using Microsoft.Framework.WebEncoders.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -28,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
allAttributes: new TagHelperAttributeList
|
allAttributes: new TagHelperAttributeList
|
||||||
{
|
{
|
||||||
{ "id", "myanchor" },
|
{ "id", "myanchor" },
|
||||||
{ "asp-route-foo", "bar" },
|
{ "asp-route-name", "value" },
|
||||||
{ "asp-action", "index" },
|
{ "asp-action", "index" },
|
||||||
{ "asp-controller", "home" },
|
{ "asp-controller", "home" },
|
||||||
{ "asp-fragment", "hello=world" },
|
{ "asp-fragment", "hello=world" },
|
||||||
|
|
@ -48,7 +47,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
attributes: new TagHelperAttributeList
|
attributes: new TagHelperAttributeList
|
||||||
{
|
{
|
||||||
{ "id", "myanchor" },
|
{ "id", "myanchor" },
|
||||||
{ "asp-route-foo", "bar" },
|
|
||||||
});
|
});
|
||||||
output.Content.SetContent("Something");
|
output.Content.SetContent("Something");
|
||||||
|
|
||||||
|
|
@ -68,6 +66,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Generator = htmlGenerator,
|
Generator = htmlGenerator,
|
||||||
Host = "contoso.com",
|
Host = "contoso.com",
|
||||||
Protocol = "http",
|
Protocol = "http",
|
||||||
|
RouteValues =
|
||||||
|
{
|
||||||
|
{ "name", "value" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -106,7 +108,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||||
generator
|
generator
|
||||||
.Setup(mock => mock.GenerateRouteLink(
|
.Setup(mock => mock.GenerateRouteLink(
|
||||||
string.Empty, "Default", "http", "contoso.com", "hello=world", null, null))
|
string.Empty,
|
||||||
|
"Default",
|
||||||
|
"http",
|
||||||
|
"contoso.com",
|
||||||
|
"hello=world",
|
||||||
|
It.IsAny<IDictionary<string, object>>(),
|
||||||
|
null))
|
||||||
.Returns(new TagBuilder("a", new CommonTestEncoder()))
|
.Returns(new TagBuilder("a", new CommonTestEncoder()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var anchorTagHelper = new AnchorTagHelper
|
var anchorTagHelper = new AnchorTagHelper
|
||||||
|
|
@ -149,7 +157,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var generator = new Mock<IHtmlGenerator>();
|
var generator = new Mock<IHtmlGenerator>();
|
||||||
generator
|
generator
|
||||||
.Setup(mock => mock.GenerateActionLink(
|
.Setup(mock => mock.GenerateActionLink(
|
||||||
string.Empty, "Index", "Home", "http", "contoso.com", "hello=world", null, null))
|
string.Empty,
|
||||||
|
"Index",
|
||||||
|
"Home",
|
||||||
|
"http",
|
||||||
|
"contoso.com",
|
||||||
|
"hello=world",
|
||||||
|
It.IsAny<IDictionary<string, object>>(),
|
||||||
|
null))
|
||||||
.Returns(new TagBuilder("a", new CommonTestEncoder()))
|
.Returns(new TagBuilder("a", new CommonTestEncoder()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var anchorTagHelper = new AnchorTagHelper
|
var anchorTagHelper = new AnchorTagHelper
|
||||||
|
|
@ -190,7 +205,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
});
|
});
|
||||||
if (propertyName == "asp-route-")
|
if (propertyName == "asp-route-")
|
||||||
{
|
{
|
||||||
output.Attributes.Add("asp-route-foo", "bar");
|
anchorTagHelper.RouteValues.Add("name", "value");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
allAttributes: new TagHelperAttributeList
|
allAttributes: new TagHelperAttributeList
|
||||||
{
|
{
|
||||||
{ "id", "myform" },
|
{ "id", "myform" },
|
||||||
{ "asp-route-foo", "bar" },
|
{ "asp-route-name", "value" },
|
||||||
{ "asp-action", "index" },
|
{ "asp-action", "index" },
|
||||||
{ "asp-controller", "home" },
|
{ "asp-controller", "home" },
|
||||||
{ "method", "post" },
|
{ "method", "post" },
|
||||||
|
|
@ -48,7 +48,6 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
attributes: new TagHelperAttributeList
|
attributes: new TagHelperAttributeList
|
||||||
{
|
{
|
||||||
{ "id", "myform" },
|
{ "id", "myform" },
|
||||||
{ "asp-route-foo", "bar" },
|
|
||||||
});
|
});
|
||||||
output.PostContent.SetContent("Something");
|
output.PostContent.SetContent("Something");
|
||||||
var urlHelper = new Mock<IUrlHelper>();
|
var urlHelper = new Mock<IUrlHelper>();
|
||||||
|
|
@ -68,6 +67,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Controller = "home",
|
Controller = "home",
|
||||||
Generator = htmlGenerator,
|
Generator = htmlGenerator,
|
||||||
ViewContext = viewContext,
|
ViewContext = viewContext,
|
||||||
|
RouteValues =
|
||||||
|
{
|
||||||
|
{ "name", "value" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -145,7 +148,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ProcessAsync_BindsRouteValuesFromTagHelperOutput()
|
public async Task ProcessAsync_BindsRouteValues()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var testViewContext = CreateViewContext();
|
var testViewContext = CreateViewContext();
|
||||||
|
|
@ -163,11 +166,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
var expectedAttribute = new TagHelperAttribute("asp-ROUTEE-NotRoute", "something");
|
var expectedAttribute = new TagHelperAttribute("asp-ROUTEE-NotRoute", "something");
|
||||||
var output = new TagHelperOutput(
|
var output = new TagHelperOutput(
|
||||||
"form",
|
"form",
|
||||||
attributes: new TagHelperAttributeList()
|
attributes: new TagHelperAttributeList());
|
||||||
{
|
|
||||||
{ "asp-route-val", "hello" },
|
|
||||||
{ "asp-roUte--Foo", "bar" }
|
|
||||||
});
|
|
||||||
output.Attributes.Add(expectedAttribute);
|
output.Attributes.Add(expectedAttribute);
|
||||||
|
|
||||||
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||||
|
|
@ -190,8 +189,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Assert.Equal(2, routeValueDictionary.Count);
|
Assert.Equal(2, routeValueDictionary.Count);
|
||||||
var routeValue = Assert.Single(routeValueDictionary, attr => attr.Key.Equals("val"));
|
var routeValue = Assert.Single(routeValueDictionary, attr => attr.Key.Equals("val"));
|
||||||
Assert.Equal("hello", routeValue.Value);
|
Assert.Equal("hello", routeValue.Value);
|
||||||
routeValue = Assert.Single(routeValueDictionary, attr => attr.Key.Equals("-Foo"));
|
routeValue = Assert.Single(routeValueDictionary, attr => attr.Key.Equals("-Name"));
|
||||||
Assert.Equal("bar", routeValue.Value);
|
Assert.Equal("Value", routeValue.Value);
|
||||||
})
|
})
|
||||||
.Returns(new TagBuilder("form", new CommonTestEncoder()))
|
.Returns(new TagBuilder("form", new CommonTestEncoder()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
@ -201,6 +200,11 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
AntiForgery = false,
|
AntiForgery = false,
|
||||||
Generator = generator.Object,
|
Generator = generator.Object,
|
||||||
ViewContext = testViewContext,
|
ViewContext = testViewContext,
|
||||||
|
RouteValues =
|
||||||
|
{
|
||||||
|
{ "val", "hello" },
|
||||||
|
{ "-Name", "Value" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
|
|
@ -237,7 +241,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
attributes: new TagHelperAttributeList());
|
attributes: new TagHelperAttributeList());
|
||||||
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||||
generator
|
generator
|
||||||
.Setup(mock => mock.GenerateForm(viewContext, "Index", "Home", null, null, null))
|
.Setup(mock => mock.GenerateForm(
|
||||||
|
viewContext,
|
||||||
|
"Index",
|
||||||
|
"Home",
|
||||||
|
It.IsAny<IDictionary<string, object>>(),
|
||||||
|
null,
|
||||||
|
null))
|
||||||
.Returns(new TagBuilder("form", new CommonTestEncoder()))
|
.Returns(new TagBuilder("form", new CommonTestEncoder()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var formTagHelper = new FormTagHelper
|
var formTagHelper = new FormTagHelper
|
||||||
|
|
@ -280,16 +290,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
});
|
});
|
||||||
var output = new TagHelperOutput(
|
var output = new TagHelperOutput(
|
||||||
"form",
|
"form",
|
||||||
attributes: new TagHelperAttributeList
|
attributes: new TagHelperAttributeList());
|
||||||
{
|
|
||||||
{ "asp-route-foo", "bar" }
|
|
||||||
});
|
|
||||||
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
var generator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||||
generator
|
generator
|
||||||
.Setup(mock => mock.GenerateRouteForm(
|
.Setup(mock => mock.GenerateRouteForm(
|
||||||
viewContext,
|
viewContext,
|
||||||
"Default",
|
"Default",
|
||||||
It.Is<Dictionary<string, object>>(m => string.Equals(m["foo"], "bar")),
|
It.Is<Dictionary<string, object>>(m => string.Equals(m["name"], "value")),
|
||||||
null,
|
null,
|
||||||
null))
|
null))
|
||||||
.Returns(new TagBuilder("form", new CommonTestEncoder()))
|
.Returns(new TagBuilder("form", new CommonTestEncoder()))
|
||||||
|
|
@ -300,6 +307,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Route = "Default",
|
Route = "Default",
|
||||||
Generator = generator.Object,
|
Generator = generator.Object,
|
||||||
ViewContext = viewContext,
|
ViewContext = viewContext,
|
||||||
|
RouteValues =
|
||||||
|
{
|
||||||
|
{ "name", "value" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
|
|
@ -384,7 +395,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
});
|
});
|
||||||
if (propertyName == "asp-route-")
|
if (propertyName == "asp-route-")
|
||||||
{
|
{
|
||||||
tagHelperOutput.Attributes.Add("asp-route-foo", "bar");
|
formTagHelper.RouteValues.Add("name", "value");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
});
|
});
|
||||||
var expectedAttribute = new TagHelperAttribute("type", "btn");
|
var expectedAttribute = new TagHelperAttribute("type", "btn");
|
||||||
tagHelperOutput.Attributes.Add(expectedAttribute);
|
tagHelperOutput.Attributes.Add(expectedAttribute);
|
||||||
var attributes = tagHelperOutput.FindPrefixedAttributes("route-");
|
|
||||||
|
var attributes = tagHelperOutput.Attributes
|
||||||
|
.Where(item => item.Name.StartsWith("route-", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
tagHelperOutput.RemoveRange(attributes);
|
tagHelperOutput.RemoveRange(attributes);
|
||||||
|
|
@ -264,19 +267,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
"p",
|
"p",
|
||||||
attributes: new TagHelperAttributeList()
|
attributes: new TagHelperAttributeList()
|
||||||
{
|
{
|
||||||
{ "routeHello", "World" },
|
{ "route-Hello", "World" },
|
||||||
{ "Routee-I", "Am" }
|
{ "Route-I", "Am" }
|
||||||
});
|
});
|
||||||
|
var expectedAttribute = new TagHelperAttribute("type", "btn");
|
||||||
|
tagHelperOutput.Attributes.Add(expectedAttribute);
|
||||||
|
|
||||||
|
var attributes = tagHelperOutput.Attributes
|
||||||
|
.Where(item => item.Name.StartsWith("route-", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var attributes = tagHelperOutput.FindPrefixedAttributes("route-");
|
tagHelperOutput.RemoveRange(attributes);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(attributes);
|
var attribute = Assert.Single(tagHelperOutput.Attributes);
|
||||||
var attribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("routeHello"));
|
Assert.Equal(expectedAttribute, attribute);
|
||||||
Assert.Equal(attribute.Value, "World");
|
|
||||||
attribute = Assert.Single(tagHelperOutput.Attributes, attr => attr.Name.Equals("Routee-I"));
|
|
||||||
Assert.Equal(attribute.Value, "Am");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData MultipleAttributeSameNameData
|
public static TheoryData MultipleAttributeSameNameData
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue