Add Items bag to TagHelperContext.

- Add a an Item properties on TagHelperExecutionContext to propagate to the TagHelperContext.
- Updated TagHelperScopeManager to create CopyOnWriteDictionary item bags for created TagHelperExecutionContexts.

#238
This commit is contained in:
N. Taylor Mullen 2015-02-13 15:31:23 -08:00
parent 23e6264715
commit b95e73e2b0
10 changed files with 76 additions and 36 deletions

View File

@ -1,12 +0,0 @@
// 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
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
internal sealed class NotNullAttribute : Attribute
{
}
}

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
@ -18,15 +19,19 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// Instantiates a new <see cref="TagHelperContext"/>. /// Instantiates a new <see cref="TagHelperContext"/>.
/// </summary> /// </summary>
/// <param name="allAttributes">Every attribute associated with the current HTML element.</param> /// <param name="allAttributes">Every attribute associated with the current HTML element.</param>
/// <param name="items">Collection of items used to communicate with other <see cref="ITagHelper"/>s.</param>
/// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" /> /// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" />
/// applies to.</param> /// applies to.</param>
/// <param name="getChildContentAsync">A delegate used to execute and retrieve the rendered child content /// <param name="getChildContentAsync">A delegate used to execute and retrieve the rendered child content
/// asynchronously.</param> /// asynchronously.</param>
public TagHelperContext([NotNull] IDictionary<string, object> allAttributes, public TagHelperContext(
[NotNull] string uniqueId, [NotNull] IDictionary<string, object> allAttributes,
[NotNull] Func<Task<string>> getChildContentAsync) [NotNull] IDictionary<object, object> items,
[NotNull] string uniqueId,
[NotNull] Func<Task<string>> getChildContentAsync)
{ {
AllAttributes = allAttributes; AllAttributes = allAttributes;
Items = items;
UniqueId = uniqueId; UniqueId = uniqueId;
_getChildContentAsync = getChildContentAsync; _getChildContentAsync = getChildContentAsync;
} }
@ -36,6 +41,15 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// </summary> /// </summary>
public IDictionary<string, object> AllAttributes { get; } public IDictionary<string, object> AllAttributes { get; }
/// <summary>
/// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
/// </summary>
/// <remarks>
/// This <see cref="IDictionary{object, object}"/> is copy-on-write in order to ensure items added to this
/// collection are visible only to other <see cref="ITagHelper"/>s targeting child elements.
/// </remarks>
public IDictionary<object, object> Items { get; }
/// <summary> /// <summary>
/// An identifier unique to the HTML element this context is for. /// An identifier unique to the HTML element this context is for.
/// </summary> /// </summary>

View File

@ -9,6 +9,7 @@ using System.Text.RegularExpressions;
using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {

View File

@ -2,9 +2,11 @@
// 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;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
@ -25,6 +27,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
internal TagHelperExecutionContext(string tagName, bool selfClosing) internal TagHelperExecutionContext(string tagName, bool selfClosing)
: this(tagName, : this(tagName,
selfClosing, selfClosing,
items: new Dictionary<object, object>(),
uniqueId: string.Empty, uniqueId: string.Empty,
executeChildContentAsync: async () => await Task.FromResult(result: true), executeChildContentAsync: async () => await Task.FromResult(result: true),
startWritingScope: () => { }, startWritingScope: () => { },
@ -37,18 +40,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// </summary> /// </summary>
/// <param name="tagName">The HTML tag name in the Razor source.</param> /// <param name="tagName">The HTML tag name in the Razor source.</param>
/// <param name="selfClosing"> /// <param name="selfClosing">
/// <see cref="bool"/> indicating whether or not the tag in the Razor source was self-closing. /// <see cref="bool"/> indicating whether or not the tag in the Razor source was self-closing.</param>
/// </param> /// <param name="items">The collection of items used to communicate with other
/// <see cref="ITagHelper"/>s</param>
/// <param name="uniqueId">An identifier unique to the HTML element this context is for.</param> /// <param name="uniqueId">An identifier unique to the HTML element this context is for.</param>
/// <param name="executeChildContentAsync">A delegate used to execute the child content asynchronously.</param> /// <param name="executeChildContentAsync">A delegate used to execute the child content asynchronously.</param>
/// <param name="startWritingScope">A delegate used to start a writing scope in a Razor page.</param> /// <param name="startWritingScope">A delegate used to start a writing scope in a Razor page.</param>
/// <param name="endWritingScope">A delegate used to end a writing scope in a Razor page.</param> /// <param name="endWritingScope">A delegate used to end a writing scope in a Razor page.</param>
public TagHelperExecutionContext([NotNull] string tagName, public TagHelperExecutionContext(
bool selfClosing, [NotNull] string tagName,
[NotNull] string uniqueId, bool selfClosing,
[NotNull] Func<Task> executeChildContentAsync, [NotNull] IDictionary<object, object> items,
[NotNull] Action startWritingScope, [NotNull] string uniqueId,
[NotNull] Func<TextWriter> endWritingScope) [NotNull] Func<Task> executeChildContentAsync,
[NotNull] Action startWritingScope,
[NotNull] Func<TextWriter> endWritingScope)
{ {
_tagHelpers = new List<ITagHelper>(); _tagHelpers = new List<ITagHelper>();
_executeChildContentAsync = executeChildContentAsync; _executeChildContentAsync = executeChildContentAsync;
@ -59,6 +65,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
AllAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); AllAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
HTMLAttributes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); HTMLAttributes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
TagName = tagName; TagName = tagName;
Items = items;
UniqueId = uniqueId; UniqueId = uniqueId;
} }
@ -78,6 +85,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
} }
} }
/// <summary>
/// Gets the collection of items used to communicate with other <see cref="ITagHelper"/>s.
/// </summary>
public IDictionary<object, object> Items { get; }
/// <summary> /// <summary>
/// HTML attributes. /// HTML attributes.
/// </summary> /// </summary>

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Net; using System.Net;
using System.Text; using System.Text;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {

View File

@ -3,6 +3,7 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
@ -22,6 +23,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
var tagHelperContext = new TagHelperContext( var tagHelperContext = new TagHelperContext(
executionContext.AllAttributes, executionContext.AllAttributes,
executionContext.Items,
executionContext.UniqueId, executionContext.UniqueId,
executionContext.GetChildContentAsync); executionContext.GetChildContentAsync);
var tagHelperOutput = new TagHelperOutput(executionContext.TagName, executionContext.HTMLAttributes) var tagHelperOutput = new TagHelperOutput(executionContext.TagName, executionContext.HTMLAttributes)

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {
@ -35,19 +36,36 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
/// <param name="startWritingScope">A delegate used to start a writing scope in a Razor page.</param> /// <param name="startWritingScope">A delegate used to start a writing scope in a Razor page.</param>
/// <param name="endWritingScope">A delegate used to end a writing scope in a Razor page.</param> /// <param name="endWritingScope">A delegate used to end a writing scope in a Razor page.</param>
/// <returns>A <see cref="TagHelperExecutionContext"/> to use.</returns> /// <returns>A <see cref="TagHelperExecutionContext"/> to use.</returns>
public TagHelperExecutionContext Begin([NotNull] string tagName, public TagHelperExecutionContext Begin(
bool selfClosing, [NotNull] string tagName,
[NotNull] string uniqueId, bool selfClosing,
[NotNull] Func<Task> executeChildContentAsync, [NotNull] string uniqueId,
[NotNull] Action startWritingScope, [NotNull] Func<Task> executeChildContentAsync,
[NotNull] Func<TextWriter> endWritingScope) [NotNull] Action startWritingScope,
[NotNull] Func<TextWriter> endWritingScope)
{ {
var executionContext = new TagHelperExecutionContext(tagName, IDictionary<object, object> items;
selfClosing,
uniqueId, // If we're not wrapped by another TagHelper, then there will not be a parentExecutionContext.
executeChildContentAsync, if (_executionScopes.Count > 0)
startWritingScope, {
endWritingScope); items = new CopyOnWriteDictionary<object, object>(
_executionScopes.Peek().Items,
comparer: EqualityComparer<object>.Default);
}
else
{
items = new Dictionary<object, object>();
}
var executionContext = new TagHelperExecutionContext(
tagName,
selfClosing,
items,
uniqueId,
executeChildContentAsync,
startWritingScope,
endWritingScope);
_executionScopes.Push(executionContext); _executionScopes.Push(executionContext);

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Text; using Microsoft.AspNet.Razor.Text;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
{ {

View File

@ -2,7 +2,9 @@
"description": "Runtime components for rendering Razor pages.", "description": "Runtime components for rendering Razor pages.",
"version": "4.0.0-*", "version": "4.0.0-*",
"dependencies": { "dependencies": {
"Microsoft.AspNet.Razor": "4.0.0-*" "Microsoft.AspNet.Razor": "4.0.0-*",
"Microsoft.Framework.CopyOnWriteDictionary.Internal": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }
}, },
"frameworks": { "frameworks": {
"net45": { }, "net45": { },