In ComponentState, track parent ComponentState. Be explicit that Renderer only lets you attach root components.

This commit is contained in:
Steve Sanderson 2018-10-05 14:20:22 +01:00
parent c05b98f9cb
commit fa2b61773a
9 changed files with 126 additions and 48 deletions

View File

@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Rendering
public void AddComponent(Type componentType, string domElementSelector) public void AddComponent(Type componentType, string domElementSelector)
{ {
var component = InstantiateComponent(componentType); var component = InstantiateComponent(componentType);
var componentId = AssignComponentId(component); var componentId = AssignRootComponentId(component);
// The only reason we're calling this synchronously is so that, if it throws, // The only reason we're calling this synchronously is so that, if it throws,
// we get the exception back *before* attempting the first UpdateDisplay // we get the exception back *before* attempting the first UpdateDisplay

View File

@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Rendering
public void AddComponent(Type componentType, string domElementSelector) public void AddComponent(Type componentType, string domElementSelector)
{ {
var component = InstantiateComponent(componentType); var component = InstantiateComponent(componentType);
var componentId = AssignComponentId(component); var componentId = AssignRootComponentId(component);
var attachComponentTask = _jsRuntime.InvokeAsync<object>( var attachComponentTask = _jsRuntime.InvokeAsync<object>(
"Blazor._internal.attachRootComponentToElement", "Blazor._internal.attachRootComponentToElement",

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
var editsBuffer = batchBuilder.EditsBuffer; var editsBuffer = batchBuilder.EditsBuffer;
var editsBufferStartLength = editsBuffer.Count; var editsBufferStartLength = editsBuffer.Count;
var diffContext = new DiffContext(renderer, batchBuilder, oldTree.Array, newTree.Array); var diffContext = new DiffContext(renderer, batchBuilder, componentId, oldTree.Array, newTree.Array);
AppendDiffEntriesForRange(ref diffContext, 0, oldTree.Count, 0, newTree.Count); AppendDiffEntriesForRange(ref diffContext, 0, oldTree.Count, 0, newTree.Count);
var editsSegment = editsBuffer.ToSegment(editsBufferStartLength, editsBuffer.Count); var editsSegment = editsBuffer.ToSegment(editsBufferStartLength, editsBuffer.Count);
@ -638,7 +638,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
throw new InvalidOperationException($"Child component already exists during {nameof(InitializeNewComponentFrame)}"); throw new InvalidOperationException($"Child component already exists during {nameof(InitializeNewComponentFrame)}");
} }
diffContext.Renderer.InstantiateChildComponentOnFrame(ref frame); var parentComponentId = diffContext.ComponentId;
diffContext.Renderer.InstantiateChildComponentOnFrame(ref frame, parentComponentId);
var childComponentInstance = frame.Component; var childComponentInstance = frame.Component;
// Set initial parameters // Set initial parameters
@ -718,16 +719,19 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
public readonly ArrayBuilder<RenderTreeEdit> Edits; public readonly ArrayBuilder<RenderTreeEdit> Edits;
public readonly ArrayBuilder<RenderTreeFrame> ReferenceFrames; public readonly ArrayBuilder<RenderTreeFrame> ReferenceFrames;
public readonly Dictionary<string, int> AttributeDiffSet; public readonly Dictionary<string, int> AttributeDiffSet;
public readonly int ComponentId;
public int SiblingIndex; public int SiblingIndex;
public DiffContext( public DiffContext(
Renderer renderer, Renderer renderer,
RenderBatchBuilder batchBuilder, RenderBatchBuilder batchBuilder,
int componentId,
RenderTreeFrame[] oldTree, RenderTreeFrame[] oldTree,
RenderTreeFrame[] newTree) RenderTreeFrame[] newTree)
{ {
Renderer = renderer; Renderer = renderer;
BatchBuilder = batchBuilder; BatchBuilder = batchBuilder;
ComponentId = componentId;
OldTree = oldTree; OldTree = oldTree;
NewTree = newTree; NewTree = newTree;
Edits = batchBuilder.EditsBuffer; Edits = batchBuilder.EditsBuffer;

View File

@ -1,4 +1,4 @@
// 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;
@ -15,6 +15,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
internal class ComponentState internal class ComponentState
{ {
private readonly int _componentId; // TODO: Change the type to 'long' when the Mono runtime has more complete support for passing longs in .NET->JS calls private readonly int _componentId; // TODO: Change the type to 'long' when the Mono runtime has more complete support for passing longs in .NET->JS calls
private readonly ComponentState _parentComponentState;
private readonly IComponent _component; private readonly IComponent _component;
private readonly Renderer _renderer; private readonly Renderer _renderer;
private RenderTreeBuilder _renderTreeBuilderCurrent; private RenderTreeBuilder _renderTreeBuilderCurrent;
@ -27,9 +28,11 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
/// <param name="renderer">The <see cref="Renderer"/> with which the new instance should be associated.</param> /// <param name="renderer">The <see cref="Renderer"/> with which the new instance should be associated.</param>
/// <param name="componentId">The externally visible identifier for the <see cref="IComponent"/>. The identifier must be unique in the context of the <see cref="Renderer"/>.</param> /// <param name="componentId">The externally visible identifier for the <see cref="IComponent"/>. The identifier must be unique in the context of the <see cref="Renderer"/>.</param>
/// <param name="component">The <see cref="IComponent"/> whose state is being tracked.</param> /// <param name="component">The <see cref="IComponent"/> whose state is being tracked.</param>
public ComponentState(Renderer renderer, int componentId, IComponent component) /// <param name="parentComponentState">The <see cref="ComponentState"/> for the parent component, or null if this is a root component.</param>
public ComponentState(Renderer renderer, int componentId, IComponent component, ComponentState parentComponentState)
{ {
_componentId = componentId; _componentId = componentId;
_parentComponentState = parentComponentState;
_component = component ?? throw new ArgumentNullException(nameof(component)); _component = component ?? throw new ArgumentNullException(nameof(component));
_renderer = renderer ?? throw new ArgumentNullException(nameof(renderer)); _renderer = renderer ?? throw new ArgumentNullException(nameof(renderer));
_renderTreeBuilderCurrent = new RenderTreeBuilder(renderer); _renderTreeBuilderCurrent = new RenderTreeBuilder(renderer);
@ -89,5 +92,10 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
public void NotifyRenderCompleted() public void NotifyRenderCompleted()
=> (_component as IHandleAfterRender)?.OnAfterRender(); => (_component as IHandleAfterRender)?.OnAfterRender();
// TODO: Remove this once we can remove TemporaryGetParentComponentIdForTest
// from Renderer.cs and corresponding unit test.
public int? TemporaryParentComponentIdForTests
=> _parentComponentState?._componentId;
} }
} }

View File

@ -48,10 +48,14 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
/// </summary> /// </summary>
/// <param name="component">The component.</param> /// <param name="component">The component.</param>
/// <returns>The component's assigned identifier.</returns> /// <returns>The component's assigned identifier.</returns>
protected int AssignComponentId(IComponent component) protected int AssignRootComponentId(IComponent component)
=> AssignComponentId(component, -1);
private int AssignComponentId(IComponent component, int parentComponentId)
{ {
var componentId = _nextComponentId++; var componentId = _nextComponentId++;
var componentState = new ComponentState(this, componentId, component); var parentComponentState = GetOptionalComponentState(parentComponentId);
var componentState = new ComponentState(this, componentId, component, parentComponentState);
_componentStateById.Add(componentId, componentState); _componentStateById.Add(componentId, componentState);
component.Init(new RenderHandle(this, componentId)); component.Init(new RenderHandle(this, componentId));
return componentId; return componentId;
@ -92,7 +96,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
} }
} }
internal void InstantiateChildComponentOnFrame(ref RenderTreeFrame frame) internal void InstantiateChildComponentOnFrame(ref RenderTreeFrame frame, int parentComponentId)
{ {
if (frame.FrameType != RenderTreeFrameType.Component) if (frame.FrameType != RenderTreeFrameType.Component)
{ {
@ -105,7 +109,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
} }
var newComponent = InstantiateComponent(frame.ComponentType); var newComponent = InstantiateComponent(frame.ComponentType);
var newComponentId = AssignComponentId(newComponent); var newComponentId = AssignComponentId(newComponent, parentComponentId);
frame = frame.WithComponentInstance(newComponentId, newComponent); frame = frame.WithComponentInstance(newComponentId, newComponent);
} }
@ -140,6 +144,15 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
} }
} }
/// <summary>
/// This only needs to exist until there's some other unit-testable functionality
/// that makes use of walking the ancestor hierarchy.
/// </summary>
/// <param name="componentId">The component ID.</param>
/// <returns>The parent component's ID, or null if the component was at the root.</returns>
internal int? TemporaryGetParentComponentIdForTest(int componentId)
=> GetRequiredComponentState(componentId).TemporaryParentComponentIdForTests;
private ComponentState GetRequiredComponentState(int componentId) private ComponentState GetRequiredComponentState(int componentId)
=> _componentStateById.TryGetValue(componentId, out var componentState) => _componentStateById.TryGetValue(componentId, out var componentState)
? componentState ? componentState

View File

@ -1,4 +1,4 @@
// 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;
@ -368,7 +368,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
public RenderTreeFrame[] LatestBatchReferenceFrames { get; private set; } public RenderTreeFrame[] LatestBatchReferenceFrames { get; private set; }
public void AttachComponent(IComponent component) public void AttachComponent(IComponent component)
=> AssignComponentId(component); => AssignRootComponentId(component);
protected override void UpdateDisplay(in RenderBatch renderBatch) protected override void UpdateDisplay(in RenderBatch renderBatch)
{ {

View File

@ -1,4 +1,4 @@
// 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 Microsoft.AspNetCore.Blazor.Components; using Microsoft.AspNetCore.Blazor.Components;
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
_renderer = new TestRenderer(); _renderer = new TestRenderer();
_layoutDisplayComponent = new LayoutDisplay(); _layoutDisplayComponent = new LayoutDisplay();
_layoutDisplayComponentId = _renderer.AssignComponentId(_layoutDisplayComponent); _layoutDisplayComponentId = _renderer.AssignRootComponentId(_layoutDisplayComponent);
} }
[Fact] [Fact]

View File

@ -1,4 +1,4 @@
// 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;
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
}); });
// Act // Act
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
// Assert // Assert
@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
}); });
// Act/Assert // Act/Assert
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var batch = renderer.Batches.Single(); var batch = renderer.Batches.Single();
var componentFrame = batch.ReferenceFrames var componentFrame = batch.ReferenceFrames
@ -83,13 +83,46 @@ namespace Microsoft.AspNetCore.Blazor.Test
}); });
} }
[Fact]
public void CanWalkTheAncestorHierarchy()
{
// TODO: Instead of testing this directly, once there's some other functionaly
// that relies on the ancestor hierarchy (e.g., deep params), test that instead
// and remove the otherwise unnecessary TemporaryGetParentComponentIdForTest API.
// Arrange
var renderer = new TestRenderer();
var rootComponent = new TestComponent(builder =>
{
builder.AddContent(0, "Hello");
builder.OpenComponent<AncestryComponent>(0);
builder.AddAttribute(1, nameof(AncestryComponent.NumDescendants), 2);
builder.CloseComponent();
});
// Act
var componentId = renderer.AssignRootComponentId(rootComponent);
rootComponent.TriggerRender();
// Assert
var batch = renderer.Batches.Single();
var componentIds = batch.ReferenceFrames
.Where(frame => frame.FrameType == RenderTreeFrameType.Component)
.Select(f => f.ComponentId);
Assert.Equal(new[] { 1, 2, 3 }, componentIds);
Assert.Equal(2, renderer.TemporaryGetParentComponentIdForTest(3));
Assert.Equal(1, renderer.TemporaryGetParentComponentIdForTest(2));
Assert.Equal(0, renderer.TemporaryGetParentComponentIdForTest(1));
Assert.Null(renderer.TemporaryGetParentComponentIdForTest(0));
}
[Fact] [Fact]
public void CanReRenderTopLevelComponents() public void CanReRenderTopLevelComponents()
{ {
// Arrange // Arrange
var renderer = new TestRenderer(); var renderer = new TestRenderer();
var component = new MessageComponent { Message = "Initial message" }; var component = new MessageComponent { Message = "Initial message" };
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
// Act/Assert: first render // Act/Assert: first render
component.TriggerRender(); component.TriggerRender();
@ -127,7 +160,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.OpenComponent<MessageComponent>(0); builder.OpenComponent<MessageComponent>(0);
builder.CloseComponent(); builder.CloseComponent();
}); });
var parentComponentId = renderer.AssignComponentId(parentComponent); var parentComponentId = renderer.AssignRootComponentId(parentComponent);
parentComponent.TriggerRender(); parentComponent.TriggerRender();
var nestedComponentFrame = renderer.Batches.Single() var nestedComponentFrame = renderer.Batches.Single()
.ReferenceFrames .ReferenceFrames
@ -173,7 +206,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
OnTest = args => { receivedArgs = args; } OnTest = args => { receivedArgs = args; }
}; };
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var eventHandlerId = renderer.Batches.Single() var eventHandlerId = renderer.Batches.Single()
@ -201,7 +234,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
OnClick = args => { receivedArgs = args; } OnClick = args => { receivedArgs = args; }
}; };
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var eventHandlerId = renderer.Batches.Single() var eventHandlerId = renderer.Batches.Single()
@ -229,7 +262,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
OnClickAction = () => { receivedArgs = new object(); } OnClickAction = () => { receivedArgs = new object(); }
}; };
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var eventHandlerId = renderer.Batches.Single() var eventHandlerId = renderer.Batches.Single()
@ -258,7 +291,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.OpenComponent<EventComponent>(0); builder.OpenComponent<EventComponent>(0);
builder.CloseComponent(); builder.CloseComponent();
}); });
var parentComponentId = renderer.AssignComponentId(parentComponent); var parentComponentId = renderer.AssignRootComponentId(parentComponent);
parentComponent.TriggerRender(); parentComponent.TriggerRender();
// Arrange: Render nested component // Arrange: Render nested component
@ -298,7 +331,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.CloseElement(); builder.CloseElement();
}); });
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var eventHandlerId = renderer.Batches.Single() var eventHandlerId = renderer.Batches.Single()
@ -336,9 +369,9 @@ namespace Microsoft.AspNetCore.Blazor.Test
var renderer1 = new TestRenderer(); var renderer1 = new TestRenderer();
var renderer2 = new TestRenderer(); var renderer2 = new TestRenderer();
var component = new MultiRendererComponent(); var component = new MultiRendererComponent();
var renderer1ComponentId = renderer1.AssignComponentId(component); var renderer1ComponentId = renderer1.AssignRootComponentId(component);
renderer2.AssignComponentId(new TestComponent(null)); // Just so they don't get the same IDs renderer2.AssignRootComponentId(new TestComponent(null)); // Just so they don't get the same IDs
var renderer2ComponentId = renderer2.AssignComponentId(component); var renderer2ComponentId = renderer2.AssignRootComponentId(component);
// Act/Assert // Act/Assert
component.TriggerRender(); component.TriggerRender();
@ -376,7 +409,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.CloseComponent(); builder.CloseComponent();
}); });
var rootComponentId = renderer.AssignComponentId(component); var rootComponentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var nestedComponentFrame = renderer.Batches.Single() var nestedComponentFrame = renderer.Batches.Single()
@ -417,7 +450,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.CloseComponent(); builder.CloseComponent();
}); });
var rootComponentId = renderer.AssignComponentId(component); var rootComponentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var originalComponentFrame = renderer.Batches.Single() var originalComponentFrame = renderer.Batches.Single()
@ -453,7 +486,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.CloseComponent(); builder.CloseComponent();
}); });
var rootComponentId = renderer.AssignComponentId(component); var rootComponentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var childComponentId = renderer.Batches.Single() var childComponentId = renderer.Batches.Single()
@ -495,7 +528,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.CloseComponent(); builder.CloseComponent();
}); });
var rootComponentId = renderer.AssignComponentId(component); var rootComponentId = renderer.AssignRootComponentId(component);
// Act/Assert 1: First render, capturing child component IDs // Act/Assert 1: First render, capturing child component IDs
component.TriggerRender(); component.TriggerRender();
@ -534,7 +567,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
var eventCount = 0; var eventCount = 0;
Action<UIEventArgs> origEventHandler = args => { eventCount++; }; Action<UIEventArgs> origEventHandler = args => { eventCount++; };
var component = new EventComponent { OnTest = origEventHandler }; var component = new EventComponent { OnTest = origEventHandler };
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var origEventHandlerId = renderer.Batches.Single() var origEventHandlerId = renderer.Batches.Single()
.ReferenceFrames .ReferenceFrames
@ -571,7 +604,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
var eventCount = 0; var eventCount = 0;
Action<UIEventArgs> origEventHandler = args => { eventCount++; }; Action<UIEventArgs> origEventHandler = args => { eventCount++; };
var component = new EventComponent { OnTest = origEventHandler }; var component = new EventComponent { OnTest = origEventHandler };
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var origEventHandlerId = renderer.Batches.Single() var origEventHandlerId = renderer.Batches.Single()
.ReferenceFrames .ReferenceFrames
@ -611,7 +644,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ nameof(EventComponent.OnTest), origEventHandler } { nameof(EventComponent.OnTest), origEventHandler }
} }
}; };
var rootComponentId = renderer.AssignComponentId(component); var rootComponentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var batch = renderer.Batches.Single(); var batch = renderer.Batches.Single();
var rootComponentDiff = batch.DiffsByComponentId[rootComponentId].Single(); var rootComponentDiff = batch.DiffsByComponentId[rootComponentId].Single();
@ -653,7 +686,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
var eventCount = 0; var eventCount = 0;
Action<UIEventArgs> origEventHandler = args => { eventCount++; }; Action<UIEventArgs> origEventHandler = args => { eventCount++; };
var component = new EventComponent { OnTest = origEventHandler }; var component = new EventComponent { OnTest = origEventHandler };
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var origEventHandlerId = renderer.Batches.Single() var origEventHandlerId = renderer.Batches.Single()
.ReferenceFrames .ReferenceFrames
@ -699,7 +732,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
}); });
builder.CloseComponent(); builder.CloseComponent();
}); });
var rootComponentId = renderer.AssignComponentId(rootComponent); var rootComponentId = renderer.AssignRootComponentId(rootComponent);
rootComponent.TriggerRender(); rootComponent.TriggerRender();
var origBatchReferenceFrames = renderer.Batches.Single().ReferenceFrames; var origBatchReferenceFrames = renderer.Batches.Single().ReferenceFrames;
var childComponentFrame = origBatchReferenceFrames var childComponentFrame = origBatchReferenceFrames
@ -781,7 +814,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
builder.AddContent(0, $"Render count: {++renderCount}"); builder.AddContent(0, $"Render count: {++renderCount}");
}); });
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
// Act/Assert: Can trigger initial render // Act/Assert: Can trigger initial render
Assert.Equal(0, renderCount); Assert.Equal(0, renderCount);
@ -817,7 +850,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.CloseComponent(); builder.CloseComponent();
builder.AddContent(2, $"Parent render count: {++parentRenderCount}"); builder.AddContent(2, $"Parent render count: {++parentRenderCount}");
}); });
var parentComponentId = renderer.AssignComponentId(parent); var parentComponentId = renderer.AssignRootComponentId(parent);
// Act // Act
parent.TriggerRender(); parent.TriggerRender();
@ -891,7 +924,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
} }
}); });
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var childComponentId = renderer.Batches.Single() var childComponentId = renderer.Batches.Single()
.ReferenceFrames .ReferenceFrames
@ -928,7 +961,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
// Arrange: Rendered with textbox enabled // Arrange: Rendered with textbox enabled
var renderer = new TestRenderer(); var renderer = new TestRenderer();
var component = new BindPlusConditionalAttributeComponent(); var component = new BindPlusConditionalAttributeComponent();
var componentId = renderer.AssignComponentId(component); var componentId = renderer.AssignRootComponentId(component);
component.TriggerRender(); component.TriggerRender();
var checkboxChangeEventHandlerId = renderer.Batches.Single() var checkboxChangeEventHandlerId = renderer.Batches.Single()
.ReferenceFrames .ReferenceFrames
@ -971,7 +1004,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
builder.CloseElement(); builder.CloseElement();
}); });
var renderer = new TestRenderer(); var renderer = new TestRenderer();
renderer.AssignComponentId(component); renderer.AssignRootComponentId(component);
// Act: Update the attribute value on the parent // Act: Update the attribute value on the parent
component.TriggerRender(); component.TriggerRender();
@ -1001,7 +1034,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
OnUpdateDisplay = _ => onAfterRenderCallCountLog.Add(component.OnAfterRenderCallCount) OnUpdateDisplay = _ => onAfterRenderCallCountLog.Add(component.OnAfterRenderCallCount)
}; };
renderer.AssignComponentId(component); renderer.AssignRootComponentId(component);
// Act // Act
component.TriggerRender(); component.TriggerRender();
@ -1042,7 +1075,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
} }
}); });
var renderer = new TestRenderer(); var renderer = new TestRenderer();
var parentComponentId = renderer.AssignComponentId(parentComponent); var parentComponentId = renderer.AssignRootComponentId(parentComponent);
// Act: First render // Act: First render
parentComponent.TriggerRender(); parentComponent.TriggerRender();
@ -1077,8 +1110,8 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
} }
public new int AssignComponentId(IComponent component) public new int AssignRootComponentId(IComponent component)
=> base.AssignComponentId(component); => base.AssignRootComponentId(component);
protected override void UpdateDisplay(in RenderBatch renderBatch) protected override void UpdateDisplay(in RenderBatch renderBatch)
{ {
@ -1330,5 +1363,25 @@ namespace Microsoft.AspNetCore.Blazor.Test
{ {
} }
} }
private class AncestryComponent : AutoRenderComponent
{
[Parameter] public int NumDescendants { get; private set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
// Recursively renders more of the same until NumDescendants == 0
if (NumDescendants > 0)
{
builder.OpenComponent<AncestryComponent>(0);
builder.AddAttribute(1, nameof(NumDescendants), NumDescendants - 1);
builder.CloseComponent();
}
else
{
builder.AddContent(1, "I'm the final descendant");
}
}
}
} }
} }

View File

@ -1,4 +1,4 @@
// 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;
@ -24,8 +24,8 @@ namespace Microsoft.AspNetCore.Blazor.Test.Helpers
public List<CapturedBatch> Batches { get; } public List<CapturedBatch> Batches { get; }
= new List<CapturedBatch>(); = new List<CapturedBatch>();
public new int AssignComponentId(IComponent component) public new int AssignRootComponentId(IComponent component)
=> base.AssignComponentId(component); => base.AssignRootComponentId(component);
public new void DispatchEvent(int componentId, int eventHandlerId, UIEventArgs args) public new void DispatchEvent(int componentId, int eventHandlerId, UIEventArgs args)
=> base.DispatchEvent(componentId, eventHandlerId, args); => base.DispatchEvent(componentId, eventHandlerId, args);