Add ability to execute child content more than once.
- Added a boolean overload that specifies whether the user wants to retrieve cached content. - Added tests to validate `TagHelperExecutionContext` `GetChildContentAsync` and that `TagHelperContext` passes the appropriate values through. #459
This commit is contained in:
parent
6a285ce70a
commit
a8fd85db1e
|
|
@ -14,22 +14,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
public class TagHelperContext
|
||||
{
|
||||
private readonly Func<Task<TagHelperContent>> _getChildContentAsync;
|
||||
private readonly Func<bool, Task<TagHelperContent>> _getChildContentAsync;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="TagHelperContext"/>.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
/// <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>
|
||||
public TagHelperContext(
|
||||
[NotNull] IEnumerable<IReadOnlyTagHelperAttribute> allAttributes,
|
||||
[NotNull] IDictionary<object, object> items,
|
||||
[NotNull] string uniqueId,
|
||||
[NotNull] Func<Task<TagHelperContent>> getChildContentAsync)
|
||||
[NotNull] Func<bool, Task<TagHelperContent>> getChildContentAsync)
|
||||
{
|
||||
AllAttributes = new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
|
||||
allAttributes.Select(attribute => new TagHelperAttribute(attribute.Name, attribute.Value)));
|
||||
|
|
@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// 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
|
||||
/// 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; }
|
||||
|
|
@ -61,9 +61,21 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// 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>
|
||||
/// <remarks>This method is memoized.</remarks>
|
||||
public Task<TagHelperContent> GetChildContentAsync()
|
||||
{
|
||||
return _getChildContentAsync();
|
||||
return GetChildContentAsync(useCachedResult: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A delegate used to execute and retrieve the rendered child content asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="useCachedResult">If <c>true</c> multiple calls to this method will not cause re-execution
|
||||
/// of child content; cached content will be returned.</param>
|
||||
/// <returns>A <see cref="Task"/> that when executed returns content rendered by children.</returns>
|
||||
public Task<TagHelperContent> GetChildContentAsync(bool useCachedResult)
|
||||
{
|
||||
return _getChildContentAsync(useCachedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -191,9 +191,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// Child content is only executed once. Successive calls to this method or successive executions of the
|
||||
/// returned <see cref="Task{TagHelperContent}"/> return a cached result.
|
||||
/// </remarks>
|
||||
public async Task<TagHelperContent> GetChildContentAsync()
|
||||
public async Task<TagHelperContent> GetChildContentAsync(bool useCachedResult)
|
||||
{
|
||||
if (_childContent == null)
|
||||
if (!useCachedResult || _childContent == null)
|
||||
{
|
||||
_startTagHelperWritingScope();
|
||||
await _executeChildContentAsync();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,30 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
{
|
||||
public class TagHelperContextTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task GetChildContentAsync_PassesUseCachedResultAsExpected(bool expectedUseCachedResultValue)
|
||||
{
|
||||
// Arrange
|
||||
bool? useCachedResultValue = null;
|
||||
var context = new TagHelperContext(
|
||||
allAttributes: Enumerable.Empty<IReadOnlyTagHelperAttribute>(),
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: string.Empty,
|
||||
getChildContentAsync: useCachedResult =>
|
||||
{
|
||||
useCachedResultValue = useCachedResult;
|
||||
return Task.FromResult<TagHelperContent>(new DefaultTagHelperContent());
|
||||
});
|
||||
|
||||
// Act
|
||||
await context.GetChildContentAsync(expectedUseCachedResultValue);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedUseCachedResultValue, useCachedResultValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_SetsProperties_AsExpected()
|
||||
{
|
||||
|
|
@ -25,7 +49,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
allAttributes: Enumerable.Empty<IReadOnlyTagHelperAttribute>(),
|
||||
items: expectedItems,
|
||||
uniqueId: string.Empty,
|
||||
getChildContentAsync: () => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
|
||||
getChildContentAsync: useCachedResult =>
|
||||
Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.Items);
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
endTagHelperWritingScope: () => defaultTagHelperContent);
|
||||
|
||||
// Act
|
||||
var content1 = await executionContext.GetChildContentAsync();
|
||||
var content2 = await executionContext.GetChildContentAsync();
|
||||
var content1 = await executionContext.GetChildContentAsync(useCachedResult: true);
|
||||
var content2 = await executionContext.GetChildContentAsync(useCachedResult: true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedContent, content1.GetContent());
|
||||
|
|
@ -83,7 +83,36 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetChildContentAsync_ReturnsNewObjectEveryTimeItIsCalled()
|
||||
public async Task GetChildContentAsync_CanExecuteChildrenMoreThanOnce()
|
||||
{
|
||||
// Arrange
|
||||
var executionCount = 0;
|
||||
var executionContext = new TagHelperExecutionContext(
|
||||
"p",
|
||||
selfClosing: false,
|
||||
items: null,
|
||||
uniqueId: string.Empty,
|
||||
executeChildContentAsync: () =>
|
||||
{
|
||||
executionCount++;
|
||||
|
||||
return Task.FromResult(result: true);
|
||||
},
|
||||
startTagHelperWritingScope: () => { },
|
||||
endTagHelperWritingScope: () => new DefaultTagHelperContent());
|
||||
|
||||
// Act
|
||||
await executionContext.GetChildContentAsync(useCachedResult: false);
|
||||
await executionContext.GetChildContentAsync(useCachedResult: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, executionCount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task GetChildContentAsync_ReturnsNewObjectEveryTimeItIsCalled(bool useCachedResult)
|
||||
{
|
||||
// Arrange
|
||||
var defaultTagHelperContent = new DefaultTagHelperContent();
|
||||
|
|
@ -97,14 +126,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
endTagHelperWritingScope: () => defaultTagHelperContent);
|
||||
|
||||
// Act
|
||||
var content1 = await executionContext.GetChildContentAsync();
|
||||
var content1 = await executionContext.GetChildContentAsync(useCachedResult);
|
||||
content1.Append("Hello");
|
||||
var content2 = await executionContext.GetChildContentAsync();
|
||||
var content2 = await executionContext.GetChildContentAsync(useCachedResult);
|
||||
content2.Append("World!");
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(content1, content2);
|
||||
Assert.Empty((await executionContext.GetChildContentAsync()).GetContent());
|
||||
Assert.Empty((await executionContext.GetChildContentAsync(useCachedResult)).GetContent());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Reference in New Issue