Re-design TagHelperOutput and runtime dependencies to allow all content modes.
- Added PreContent, PostContent and ContentSet properties to TagHelperOutput. - Added GeneratePreContent, GeneratePostContent and SupressOutput methods to TagHelperOutput. - Added multile ExecuteChildContentAsync and GetChildContentAsync to the rendering phase, ultimately only exposing GetChildContentAsync to a TagHelper author. - Added more knowledge of StartWritingScope and EndWritingScope to the TagHelper runtime components. This is to enable the runtime components to utilize the RazorPage's infrastructure to render a delegate to a writer and retrieve its value to ultimately expose it to the user. #221
This commit is contained in:
parent
c38761f504
commit
1ef8c088d1
|
|
@ -1,7 +1,9 @@
|
|||
// 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 System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
|
|
@ -10,25 +12,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
public class TagHelperContext
|
||||
{
|
||||
private readonly Func<Task<string>> _getChildContentAsync;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="TagHelperContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="allAttributes">Every attribute associated with the current HTML element.</param>
|
||||
/// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" /> applies to.</param>
|
||||
public TagHelperContext([NotNull] IDictionary<string, object> allAttributes, [NotNull] string uniqueId)
|
||||
/// <param name="uniqueId">The unique identifier for the source element this <see cref="TagHelperContext" />
|
||||
/// applies to.</param>
|
||||
/// <param name="getChildContentAsync">A delegate used to execute and retrieve the rendered child content
|
||||
/// asynchronously.</param>
|
||||
public TagHelperContext([NotNull] IDictionary<string, object> allAttributes,
|
||||
[NotNull] string uniqueId,
|
||||
[NotNull] Func<Task<string>> getChildContentAsync)
|
||||
{
|
||||
AllAttributes = allAttributes;
|
||||
UniqueId = uniqueId;
|
||||
_getChildContentAsync = getChildContentAsync;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Every attribute associated with the current HTML element.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AllAttributes { get; private set; }
|
||||
public IDictionary<string, object> AllAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An identifier unique to the HTML element this context is for.
|
||||
/// </summary>
|
||||
public string UniqueId { get; private set; }
|
||||
public string UniqueId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A delegate used to execute and retrieve the rendered child content asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> that when executed returns content rendered by children.</returns>
|
||||
public Task<string> GetChildContentAsync()
|
||||
{
|
||||
return _getChildContentAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
|
|
@ -12,43 +14,61 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
public class TagHelperExecutionContext
|
||||
{
|
||||
private readonly List<ITagHelper> _tagHelpers;
|
||||
private readonly Func<Task> _executeChildContentAsync;
|
||||
private readonly Action _startWritingScope;
|
||||
private readonly Func<TextWriter> _endWritingScope;
|
||||
private string _childContent;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="TagHelperExecutionContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="tagName">The HTML tag name in the Razor source.</param>
|
||||
public TagHelperExecutionContext([NotNull] string tagName, [NotNull] string uniqueId)
|
||||
/// <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="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>
|
||||
public TagHelperExecutionContext([NotNull] string tagName,
|
||||
[NotNull] string uniqueId,
|
||||
[NotNull] Func<Task> executeChildContentAsync,
|
||||
[NotNull] Action startWritingScope,
|
||||
[NotNull] Func<TextWriter> endWritingScope)
|
||||
{
|
||||
_tagHelpers = new List<ITagHelper>();
|
||||
_executeChildContentAsync = executeChildContentAsync;
|
||||
_startWritingScope = startWritingScope;
|
||||
_endWritingScope = endWritingScope;
|
||||
|
||||
AllAttributes = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
HTMLAttributes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
_tagHelpers = new List<ITagHelper>();
|
||||
TagName = tagName;
|
||||
UniqueId = uniqueId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal for testing purposes only.
|
||||
/// Indicates if <see cref="GetChildContentAsync"/> has been called.
|
||||
/// </summary>
|
||||
internal TagHelperExecutionContext([NotNull] string tagName)
|
||||
: this(tagName, string.Empty)
|
||||
public bool ChildContentRetrieved
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
return _childContent != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTML attributes.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> HTMLAttributes { get; private set; }
|
||||
public IDictionary<string, string> HTMLAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/> bound attributes and HTML attributes.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> AllAttributes { get; private set; }
|
||||
public IDictionary<string, object> AllAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An identifier unique to the HTML element this context is for.
|
||||
/// </summary>
|
||||
public string UniqueId { get; private set; }
|
||||
public string UniqueId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ITagHelper"/>s that should be run.
|
||||
|
|
@ -64,7 +84,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <summary>
|
||||
/// The HTML tag name in the Razor source.
|
||||
/// </summary>
|
||||
public string TagName { get; private set; }
|
||||
public string TagName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ITagHelper"/>s' output.
|
||||
|
|
@ -100,5 +120,34 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
AllAttributes.Add(name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the child content asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> which on completion executes all child content.</returns>
|
||||
public Task ExecuteChildContentAsync()
|
||||
{
|
||||
return _executeChildContentAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute and retrieve the rendered child content asynchronously.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="Task"/> that on completion returns the rendered child content.</returns>
|
||||
/// <remarks>
|
||||
/// Child content is only executed once. Successive calls to this method or successive executions of the
|
||||
/// returned <see cref="Task{string}"/> return a cached result.
|
||||
/// </remarks>
|
||||
public async Task<string> GetChildContentAsync()
|
||||
{
|
||||
if (_childContent == null)
|
||||
{
|
||||
_startWritingScope();
|
||||
await _executeChildContentAsync();
|
||||
_childContent = _endWritingScope().ToString();
|
||||
}
|
||||
|
||||
return _childContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
public class TagHelperOutput
|
||||
{
|
||||
private string _content;
|
||||
private string _tagName;
|
||||
private bool _contentSet;
|
||||
|
||||
// Internal for testing
|
||||
internal TagHelperOutput(string tagName)
|
||||
|
|
@ -35,37 +35,34 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
/// <param name="tagName">The HTML element's tag name.</param>
|
||||
/// <param name="attributes">The HTML attributes.</param>
|
||||
/// <param name="content">The HTML element's content.</param>
|
||||
public TagHelperOutput(string tagName,
|
||||
[NotNull] IDictionary<string, string> attributes,
|
||||
string content)
|
||||
public TagHelperOutput(string tagName, [NotNull] IDictionary<string, string> attributes)
|
||||
{
|
||||
TagName = tagName;
|
||||
Content = content;
|
||||
Attributes = new Dictionary<string, string>(attributes, StringComparer.OrdinalIgnoreCase);
|
||||
PreContent = string.Empty;
|
||||
_content = string.Empty;
|
||||
PostContent = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The HTML element's tag name.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A whitespace value results in no start or end tag being rendered.
|
||||
/// A whitespace or <c>null</c> value results in no start or end tag being rendered.
|
||||
/// </remarks>
|
||||
public string TagName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tagName;
|
||||
}
|
||||
set
|
||||
{
|
||||
_tagName = value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
public string TagName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The HTML element's content.
|
||||
/// The HTML element's pre content.
|
||||
/// </summary>
|
||||
/// <remarks>Value is prepended to the <see cref="ITagHelper"/>'s final output.</remarks>
|
||||
public string PreContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The HTML element's main content.
|
||||
/// </summary>
|
||||
/// <remarks>Value occurs in the <see cref="ITagHelper"/>'s final output after <see cref="PreContent"/> and
|
||||
/// before <see cref="PostContent"/></remarks>
|
||||
public string Content
|
||||
{
|
||||
get
|
||||
|
|
@ -74,7 +71,25 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
}
|
||||
set
|
||||
{
|
||||
_content = value ?? string.Empty;
|
||||
_contentSet = true;
|
||||
_content = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The HTML element's post content.
|
||||
/// </summary>
|
||||
/// <remarks>Value is appended to the <see cref="ITagHelper"/>'s final output.</remarks>
|
||||
public string PostContent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <c>true</c> if <see cref="Content"/> has been set, <c>false</c> otherwise.
|
||||
/// </summary>
|
||||
public bool ContentSet
|
||||
{
|
||||
get
|
||||
{
|
||||
return _contentSet;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +101,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <summary>
|
||||
/// The HTML element's attributes.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Attributes { get; private set; }
|
||||
public IDictionary<string, string> Attributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="TagHelperOutput"/>'s start tag.
|
||||
|
|
@ -126,6 +141,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="TagHelperOutput"/>'s <see cref="PreContent"/>.
|
||||
/// </summary>
|
||||
/// <returns><c>string.Empty</c> if <see cref="SelfClosing"/> is <c>true</c>. <see cref="PreContent"/>
|
||||
/// otherwise.
|
||||
/// </returns>
|
||||
public string GeneratePreContent()
|
||||
{
|
||||
if (SelfClosing)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return PreContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="TagHelperOutput"/>'s body.
|
||||
/// </summary>
|
||||
|
|
@ -141,6 +172,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
return Content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="TagHelperOutput"/>'s <see cref="PostContent"/>.
|
||||
/// </summary>
|
||||
/// <returns><c>string.Empty</c> if <see cref="SelfClosing"/> is <c>true</c>. <see cref="PostContent"/>
|
||||
/// otherwise.
|
||||
/// </returns>
|
||||
public string GeneratePostContent()
|
||||
{
|
||||
if (SelfClosing)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return PostContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="TagHelperOutput"/>'s end tag.
|
||||
/// </summary>
|
||||
|
|
@ -155,5 +202,20 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
return string.Format(CultureInfo.InvariantCulture, "</{0}>", TagName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes <see cref="TagHelperOutput"/> to generate nothing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sets <see cref="TagName"/>, <see cref="PreContent"/>, <see cref="Content"/>, and <see cref="PostContent"/>
|
||||
/// to <c>null</c> to suppress output.
|
||||
/// </remarks>
|
||||
public void SuppressOutput()
|
||||
{
|
||||
TagName = null;
|
||||
PreContent = null;
|
||||
Content = null;
|
||||
PostContent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
// 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.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
|
|
@ -14,33 +13,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// <summary>
|
||||
/// Calls the <see cref="ITagHelper.ProcessAsync"/> method on <see cref="ITagHelper"/>s.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information associated with running <see cref="ITagHelper"/>s.</param>
|
||||
/// <param name="executionContext">Contains information associated with running <see cref="ITagHelper"/>s.
|
||||
/// </param>
|
||||
/// <returns>Resulting <see cref="TagHelperOutput"/> from processing all of the
|
||||
/// <paramref name="context"/>'s <see cref="ITagHelper"/>s.</returns>
|
||||
public async Task<TagHelperOutput> RunAsync([NotNull] TagHelperExecutionContext context)
|
||||
/// <paramref name="executionContext"/>'s <see cref="ITagHelper"/>s.</returns>
|
||||
public async Task<TagHelperOutput> RunAsync([NotNull] TagHelperExecutionContext executionContext)
|
||||
{
|
||||
return await RunAsyncCore(context, string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the <see cref="ITagHelper.ProcessAsync"/> method on <see cref="ITagHelper"/>s.
|
||||
/// </summary>
|
||||
/// <param name="context">Contains information associated with running <see cref="ITagHelper"/>s.</param>
|
||||
/// <param name="bufferedBody">Contains the buffered content of the current HTML tag.</param>
|
||||
/// <returns>Resulting <see cref="TagHelperOutput"/> from processing all of the
|
||||
/// <paramref name="context"/>'s <see cref="ITagHelper"/>s.</returns>
|
||||
public async Task<TagHelperOutput> RunAsync([NotNull] TagHelperExecutionContext context,
|
||||
[NotNull] TextWriter bufferedBody)
|
||||
{
|
||||
return await RunAsyncCore(context, bufferedBody.ToString());
|
||||
}
|
||||
|
||||
private async Task<TagHelperOutput> RunAsyncCore(TagHelperExecutionContext executionContext, string outputContent)
|
||||
{
|
||||
var tagHelperContext = new TagHelperContext(executionContext.AllAttributes, executionContext.UniqueId);
|
||||
var tagHelperOutput = new TagHelperOutput(executionContext.TagName,
|
||||
executionContext.HTMLAttributes,
|
||||
outputContent);
|
||||
var tagHelperContext = new TagHelperContext(
|
||||
executionContext.AllAttributes,
|
||||
executionContext.UniqueId,
|
||||
executionContext.GetChildContentAsync);
|
||||
var tagHelperOutput = new TagHelperOutput(executionContext.TagName, executionContext.HTMLAttributes);
|
||||
|
||||
foreach (var tagHelper in executionContext.TagHelpers)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
|
|
@ -26,10 +28,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
/// <param name="tagName">The HTML tag name that the scope is associated with.</param>
|
||||
/// <param name="uniqueId">An identifier unique to the HTML element this scope is for.</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="endWritingScope">A delegate used to end a writing scope in a Razor page.</param>
|
||||
/// <returns>A <see cref="TagHelperExecutionContext"/> to use.</returns>
|
||||
public TagHelperExecutionContext Begin(string tagName, string uniqueId)
|
||||
public TagHelperExecutionContext Begin([NotNull] string tagName,
|
||||
[NotNull] string uniqueId,
|
||||
[NotNull] Func<Task> executeChildContentAsync,
|
||||
[NotNull] Action startWritingScope,
|
||||
[NotNull] Func<TextWriter> endWritingScope)
|
||||
{
|
||||
var executionContext = new TagHelperExecutionContext(tagName, uniqueId);
|
||||
var executionContext = new TagHelperExecutionContext(tagName,
|
||||
uniqueId,
|
||||
executeChildContentAsync,
|
||||
startWritingScope,
|
||||
endWritingScope);
|
||||
|
||||
_executionScopes.Push(executionContext);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,13 @@ namespace Microsoft.AspNet.Razor.Generator
|
|||
RunnerRunAsyncMethodName = "RunAsync";
|
||||
ScopeManagerBeginMethodName = "Begin";
|
||||
ScopeManagerEndMethodName = "End";
|
||||
OutputContentSetPropertyName = "ContentSet";
|
||||
OutputGenerateStartTagMethodName = "GenerateStartTag";
|
||||
OutputGeneratePreContentMethodName = "GeneratePreContent";
|
||||
OutputGenerateContentMethodName = "GenerateContent";
|
||||
OutputGeneratePostContentMethodName = "GeneratePostContent";
|
||||
OutputGenerateEndTagMethodName = "GenerateEndTag";
|
||||
ExecutionContextExecuteChildContentAsyncMethodName = "ExecuteChildContentAsync";
|
||||
ExecutionContextAddMethodName = "Add";
|
||||
ExecutionContextAddTagHelperAttributeMethodName = "AddTagHelperAttribute";
|
||||
ExecutionContextAddHtmlAttributeMethodName = "AddHtmlAttribute";
|
||||
|
|
@ -51,21 +55,42 @@ namespace Microsoft.AspNet.Razor.Generator
|
|||
/// </summary>
|
||||
public string ScopeManagerEndMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the property used to determine if a tag helper output's content was set.
|
||||
/// </summary>
|
||||
public string OutputContentSetPropertyName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method used to generate a tag helper output's start tag.
|
||||
/// </summary>
|
||||
public string OutputGenerateStartTagMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method used to generate a tag helper output's pre content.
|
||||
/// </summary>
|
||||
public string OutputGeneratePreContentMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method used to generate a tag helper output's content.
|
||||
/// </summary>
|
||||
public string OutputGenerateContentMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method used to generate a tag helper output's post-content.
|
||||
/// </summary>
|
||||
public string OutputGeneratePostContentMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the method used to generate a tag helper output's end tag.
|
||||
/// </summary>
|
||||
public string OutputGenerateEndTagMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the <see cref="ExecutionContextTypeName"/> method used to get a
|
||||
/// <see cref="System.Threading.Tasks.Task"/> that executes tag helper child content.
|
||||
/// </summary>
|
||||
public string ExecutionContextExecuteChildContentAsyncMethodName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the <see cref="ExecutionContextTypeName"/> method used to add tag helper attributes.
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue