Introduce IComponent.SetParameters, moving parameter-setting and rerendering logic into component base class
This commit is contained in:
parent
37217db73a
commit
861154764c
|
|
@ -24,6 +24,10 @@ namespace HostedInAspNet.Client
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
{
|
||||||
builder.OpenElement(0, "h1");
|
builder.OpenElement(0, "h1");
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Blazor.Components
|
||||||
/// Optional base class for Blazor components. Alternatively, Blazor components may
|
/// Optional base class for Blazor components. Alternatively, Blazor components may
|
||||||
/// implement <see cref="IComponent"/> directly.
|
/// implement <see cref="IComponent"/> directly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BlazorComponent : IComponent, IHandlePropertiesChanged
|
public abstract class BlazorComponent : IComponent
|
||||||
{
|
{
|
||||||
private RenderHandle _renderHandle;
|
private RenderHandle _renderHandle;
|
||||||
|
|
||||||
|
|
@ -28,6 +28,16 @@ namespace Microsoft.AspNetCore.Blazor.Components
|
||||||
_renderHandle = renderHandle;
|
_renderHandle = renderHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IComponent.SetParameters(ParameterCollection parameters)
|
||||||
|
{
|
||||||
|
parameters.AssignToProperties(this);
|
||||||
|
|
||||||
|
// TODO: If we know conclusively that the parameters have not changed since last
|
||||||
|
// time (because they are all primitives and equal to the existing property values)
|
||||||
|
// then don't re-render. Can put an "out bool" parameter on AssignToProperties.
|
||||||
|
_renderHandle.Render();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void BuildRenderTree(RenderTreeBuilder builder)
|
public virtual void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
{
|
||||||
|
|
@ -50,11 +60,6 @@ namespace Microsoft.AspNetCore.Blazor.Components
|
||||||
public virtual Task ExecuteAsync()
|
public virtual Task ExecuteAsync()
|
||||||
=> throw new NotImplementedException($"Blazor components do not implement {nameof(ExecuteAsync)}.");
|
=> throw new NotImplementedException($"Blazor components do not implement {nameof(ExecuteAsync)}.");
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual void OnPropertiesChanged()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles click events by invoking <paramref name="handler"/>.
|
/// Handles click events by invoking <paramref name="handler"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,12 @@ namespace Microsoft.AspNetCore.Blazor.Components
|
||||||
/// <param name="renderHandle">A <see cref="RenderHandle"/> that allows the component to be rendered.</param>
|
/// <param name="renderHandle">A <see cref="RenderHandle"/> that allows the component to be rendered.</param>
|
||||||
void Init(RenderHandle renderHandle);
|
void Init(RenderHandle renderHandle);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets parameters supplied by the component's parent in the render tree.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameters">The parameters.</param>
|
||||||
|
void SetParameters(ParameterCollection parameters);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds a <see cref="RenderTree"/> representing the current state of the component.
|
/// Builds a <see cref="RenderTree"/> representing the current state of the component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Blazor.Components
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface implemented by components that receive notifications when their
|
|
||||||
/// properties are changed by their parent component.
|
|
||||||
/// </summary>
|
|
||||||
public interface IHandlePropertiesChanged
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Notifies the component that its properties have changed.
|
|
||||||
/// </summary>
|
|
||||||
void OnPropertiesChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Blazor.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for the <see cref="ParameterCollection"/> type.
|
||||||
|
/// </summary>
|
||||||
|
public static class ParameterCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Iterates through the <see cref="ParameterCollection"/>, assigning each parameter
|
||||||
|
/// to a property of the same name on <paramref name="target"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameterCollection">The <see cref="ParameterCollection"/>.</param>
|
||||||
|
/// <param name="target">An object that has a public writable property matching each parameter's name and type.</param>
|
||||||
|
public static void AssignToProperties(
|
||||||
|
this ParameterCollection parameterCollection,
|
||||||
|
object target)
|
||||||
|
{
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var parameter in parameterCollection)
|
||||||
|
{
|
||||||
|
AssignToProperty(target, parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AssignToProperty(object target, Parameter parameter)
|
||||||
|
{
|
||||||
|
// TODO: Don't just use naive reflection like this. Possible ways to make it faster:
|
||||||
|
// (a) Create and cache a property-assigning open delegate for each (target type,
|
||||||
|
// property name) pair, e.g., using propertyInfo.GetSetMethod().CreateDelegate(...)
|
||||||
|
// That's much faster than caching the PropertyInfo, at least on JIT-enabled platforms.
|
||||||
|
// (b) Or possibly just code-gen an IComponent.SetParameters implementation for each
|
||||||
|
// Razor component. However that might not work well with code-behind inheritance,
|
||||||
|
// because the code-behind wouldn't be able to override it.
|
||||||
|
|
||||||
|
var propertyInfo = GetPropertyInfo(target.GetType(), parameter.Name);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
propertyInfo.SetValue(target, parameter.Value);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Unable to set property '{parameter.Name}' on object of " +
|
||||||
|
$"type '{target.GetType().FullName}'. The error was: {ex.Message}", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PropertyInfo GetPropertyInfo(Type targetType, string propertyName)
|
||||||
|
{
|
||||||
|
var property = targetType.GetProperty(propertyName);
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Object of type '{targetType.FullName}' does not have a property " +
|
||||||
|
$"matching the name '{propertyName}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -144,133 +144,23 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
int oldComponentIndex,
|
int oldComponentIndex,
|
||||||
int newComponentIndex)
|
int newComponentIndex)
|
||||||
{
|
{
|
||||||
// The algorithm here is the same as in AppendDiffEntriesForRange, except that
|
|
||||||
// here we don't optimise for loops - we assume that both sequences are forward-only.
|
|
||||||
// That's because this is true for all currently supported scenarios, and it means
|
|
||||||
// fewer steps here.
|
|
||||||
|
|
||||||
var oldTree = diffContext.OldTree;
|
var oldTree = diffContext.OldTree;
|
||||||
var newTree = diffContext.NewTree;
|
var newTree = diffContext.NewTree;
|
||||||
ref var oldComponentFrame = ref oldTree[oldComponentIndex];
|
ref var oldComponentFrame = ref oldTree[oldComponentIndex];
|
||||||
ref var newComponentFrame = ref newTree[newComponentIndex];
|
ref var newComponentFrame = ref newTree[newComponentIndex];
|
||||||
var componentId = oldComponentFrame.ComponentId;
|
var componentId = oldComponentFrame.ComponentId;
|
||||||
var componentInstance = oldComponentFrame.Component;
|
var componentInstance = oldComponentFrame.Component;
|
||||||
var hasSetAnyProperty = false;
|
|
||||||
|
|
||||||
// Preserve the actual componentInstance
|
// Preserve the actual componentInstance
|
||||||
newComponentFrame = newComponentFrame.WithComponentInstance(componentId, componentInstance);
|
newComponentFrame = newComponentFrame.WithComponentInstance(componentId, componentInstance);
|
||||||
|
|
||||||
// Now locate any added/changed/removed properties
|
// Supply latest parameters. They might not have changed, but it's up to the
|
||||||
var oldStartIndex = oldComponentIndex + 1;
|
// recipient to decide what "changed" means. Currently we only supply the new
|
||||||
var newStartIndex = newComponentIndex + 1;
|
// set of parameters and assume the recipient has enough info to do whatever
|
||||||
var oldEndIndexExcl = oldComponentIndex + oldComponentFrame.ComponentSubtreeLength;
|
// comparisons it wants with the old values. Later we could choose to pass the
|
||||||
var newEndIndexExcl = newComponentIndex + newComponentFrame.ComponentSubtreeLength;
|
// old parameter values if we wanted.
|
||||||
var hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
var newParameters = new ParameterCollection(newTree, newComponentIndex);
|
||||||
var hasMoreNew = newEndIndexExcl > newStartIndex;
|
componentInstance.SetParameters(newParameters);
|
||||||
while (hasMoreOld || hasMoreNew)
|
|
||||||
{
|
|
||||||
var oldSeq = hasMoreOld ? oldTree[oldStartIndex].Sequence : int.MaxValue;
|
|
||||||
var newSeq = hasMoreNew ? newTree[newStartIndex].Sequence : int.MaxValue;
|
|
||||||
|
|
||||||
if (oldSeq == newSeq)
|
|
||||||
{
|
|
||||||
ref var oldFrame = ref oldTree[oldStartIndex];
|
|
||||||
ref var newFrame = ref newTree[newStartIndex];
|
|
||||||
var oldName = oldFrame.AttributeName;
|
|
||||||
var newName = newFrame.AttributeName;
|
|
||||||
var newPropertyValue = newFrame.AttributeValue;
|
|
||||||
if (string.Equals(oldName, newName, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
// Using Equals to account for string comparisons, nulls, etc.
|
|
||||||
var oldPropertyValue = oldFrame.AttributeValue;
|
|
||||||
if (!Equals(oldPropertyValue, newPropertyValue))
|
|
||||||
{
|
|
||||||
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
|
||||||
hasSetAnyProperty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Since this code path is never reachable for Razor components (because you
|
|
||||||
// can't have two different attribute names from the same source sequence), we
|
|
||||||
// could consider removing the 'name equality' check entirely for perf
|
|
||||||
SetChildComponentProperty(componentInstance, newName, newPropertyValue);
|
|
||||||
RemoveChildComponentProperty(componentInstance, oldName);
|
|
||||||
hasSetAnyProperty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldStartIndex++;
|
|
||||||
newStartIndex++;
|
|
||||||
hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
|
||||||
hasMoreNew = newEndIndexExcl > newStartIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Both sequences are proceeding through the same loop block, so do a simple
|
|
||||||
// preordered merge join (picking from whichever side brings us closer to being
|
|
||||||
// back in sync)
|
|
||||||
var treatAsInsert = newSeq < oldSeq;
|
|
||||||
|
|
||||||
if (treatAsInsert)
|
|
||||||
{
|
|
||||||
ref var newFrame = ref newTree[newStartIndex];
|
|
||||||
SetChildComponentProperty(componentInstance, newFrame.AttributeName, newFrame.AttributeValue);
|
|
||||||
hasSetAnyProperty = true;
|
|
||||||
newStartIndex++;
|
|
||||||
hasMoreNew = newEndIndexExcl > newStartIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ref var oldFrame = ref oldTree[oldStartIndex];
|
|
||||||
RemoveChildComponentProperty(componentInstance, oldFrame.AttributeName);
|
|
||||||
hasSetAnyProperty = true;
|
|
||||||
oldStartIndex++;
|
|
||||||
hasMoreOld = oldEndIndexExcl > oldStartIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasSetAnyProperty)
|
|
||||||
{
|
|
||||||
diffContext.BatchBuilder.ComponentRenderQueue.Enqueue(newComponentFrame.ComponentId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RemoveChildComponentProperty(IComponent component, string componentPropertyName)
|
|
||||||
{
|
|
||||||
var propertyInfo = GetChildComponentPropertyInfo(component.GetType(), componentPropertyName);
|
|
||||||
var defaultValue = propertyInfo.PropertyType.IsValueType
|
|
||||||
? Activator.CreateInstance(propertyInfo.PropertyType)
|
|
||||||
: null;
|
|
||||||
SetChildComponentProperty(component, componentPropertyName, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetChildComponentProperty(IComponent component, string componentPropertyName, object newPropertyValue)
|
|
||||||
{
|
|
||||||
var propertyInfo = GetChildComponentPropertyInfo(component.GetType(), componentPropertyName);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
propertyInfo.SetValue(component, newPropertyValue);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
$"Unable to set property '{componentPropertyName}' on component of " +
|
|
||||||
$"type '{component.GetType().FullName}'. The error was: {ex.Message}", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PropertyInfo GetChildComponentPropertyInfo(Type componentType, string componentPropertyName)
|
|
||||||
{
|
|
||||||
var property = componentType.GetProperty(componentPropertyName);
|
|
||||||
if (property == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
$"Component of type '{componentType.FullName}' does not have a property " +
|
|
||||||
$"matching the name '{componentPropertyName}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return property;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int NextSiblingIndex(RenderTreeFrame frame, int frameIndex)
|
private static int NextSiblingIndex(RenderTreeFrame frame, int frameIndex)
|
||||||
|
|
@ -541,18 +431,9 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
diffContext.Renderer.InstantiateChildComponentOnFrame(ref frame);
|
diffContext.Renderer.InstantiateChildComponentOnFrame(ref frame);
|
||||||
var childComponentInstance = frame.Component;
|
var childComponentInstance = frame.Component;
|
||||||
|
|
||||||
// All descendants of a component are its properties
|
// Set initial parameters
|
||||||
var componentDescendantsEndIndexExcl = frameIndex + frame.ComponentSubtreeLength;
|
var initialParameters = new ParameterCollection(frames, frameIndex);
|
||||||
for (var attributeFrameIndex = frameIndex + 1; attributeFrameIndex < componentDescendantsEndIndexExcl; attributeFrameIndex++)
|
childComponentInstance.SetParameters(initialParameters);
|
||||||
{
|
|
||||||
ref var attributeFrame = ref frames[attributeFrameIndex];
|
|
||||||
SetChildComponentProperty(
|
|
||||||
childComponentInstance,
|
|
||||||
attributeFrame.AttributeName,
|
|
||||||
attributeFrame.AttributeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
diffContext.BatchBuilder.ComponentRenderQueue.Enqueue(frame.ComponentId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializeNewAttributeFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
|
private static void InitializeNewAttributeFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,6 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
|
||||||
|
|
||||||
public void RenderIntoBatch(RenderBatchBuilder batchBuilder)
|
public void RenderIntoBatch(RenderBatchBuilder batchBuilder)
|
||||||
{
|
{
|
||||||
if (_component is IHandlePropertiesChanged notifyableComponent)
|
|
||||||
{
|
|
||||||
notifyableComponent.OnPropertiesChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap the old and new tree builders
|
// Swap the old and new tree builders
|
||||||
(_renderTreeBuilderCurrent, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, _renderTreeBuilderCurrent);
|
(_renderTreeBuilderCurrent, _renderTreeBuilderPrevious) = (_renderTreeBuilderPrevious, _renderTreeBuilderCurrent);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -482,6 +482,10 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
{
|
||||||
builder.AddText(0, $"Hello from {nameof(TestComponent)}");
|
builder.AddText(0, $"Hello from {nameof(TestComponent)}");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Blazor.Components;
|
||||||
|
using Microsoft.AspNetCore.Blazor.RenderTree;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
|
{
|
||||||
|
public abstract class AutoRenderComponent : IComponent
|
||||||
|
{
|
||||||
|
private RenderHandle _renderHandle;
|
||||||
|
|
||||||
|
public abstract void BuildRenderTree(RenderTreeBuilder builder);
|
||||||
|
|
||||||
|
public void Init(RenderHandle renderHandle)
|
||||||
|
{
|
||||||
|
_renderHandle = renderHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
{
|
||||||
|
parameters.AssignToProperties(this);
|
||||||
|
_renderHandle.Render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -107,6 +107,9 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
public void Init(RenderHandle renderHandle)
|
public void Init(RenderHandle renderHandle)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,9 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
{
|
{
|
||||||
public void Init(RenderHandle renderHandle) { }
|
public void Init(RenderHandle renderHandle) { }
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
=> throw new NotImplementedException();
|
=> throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
appendAction(newTree);
|
appendAction(newTree);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var (result, referenceFrames) = GetSingleUpdatedComponent();
|
var (result, referenceFrames) = GetSingleUpdatedComponent(initializeFromFrames: true);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(result.Edits);
|
Assert.Empty(result.Edits);
|
||||||
|
|
@ -51,7 +51,11 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
builder.AddAttribute(1, "My attribute", "My value");
|
builder.AddAttribute(1, "My attribute", "My value");
|
||||||
builder.CloseElement();
|
builder.CloseElement();
|
||||||
},
|
},
|
||||||
builder => builder.OpenComponent<FakeComponent>(0)
|
builder =>
|
||||||
|
{
|
||||||
|
builder.OpenComponent<FakeComponent>(0);
|
||||||
|
builder.CloseComponent();
|
||||||
|
}
|
||||||
}.Select(x => new object[] { x });
|
}.Select(x => new object[] { x });
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -319,7 +323,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
// Arrange
|
// Arrange
|
||||||
oldTree.OpenComponent<FakeComponent>(123);
|
oldTree.OpenComponent<FakeComponent>(123);
|
||||||
oldTree.CloseComponent();
|
oldTree.CloseComponent();
|
||||||
GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree); // Assign initial IDs
|
GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree, false); // Assign initial IDs
|
||||||
newTree.OpenComponent<FakeComponent2>(123);
|
newTree.OpenComponent<FakeComponent2>(123);
|
||||||
newTree.CloseComponent();
|
newTree.CloseComponent();
|
||||||
var batchBuilder = new RenderBatchBuilder();
|
var batchBuilder = new RenderBatchBuilder();
|
||||||
|
|
@ -327,11 +331,8 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
// Act
|
// Act
|
||||||
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, oldTree.GetFrames(), newTree.GetFrames());
|
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, oldTree.GetFrames(), newTree.GetFrames());
|
||||||
|
|
||||||
// Assert: We're going to dispose the old component, and render the new one
|
// Assert: We're going to dispose the old component and render the new one
|
||||||
Assert.Equal(new[] { 0 }, batchBuilder.ComponentDisposalQueue);
|
Assert.Equal(new[] { 0 }, batchBuilder.ComponentDisposalQueue);
|
||||||
Assert.Equal(new[] { 1 }, batchBuilder.ComponentRenderQueue);
|
|
||||||
|
|
||||||
// Assert: Got correct info in diff
|
|
||||||
Assert.Collection(diff.Edits,
|
Assert.Collection(diff.Edits,
|
||||||
entry => AssertEdit(entry, RenderTreeEditType.RemoveFrame, 0),
|
entry => AssertEdit(entry, RenderTreeEditType.RemoveFrame, 0),
|
||||||
entry =>
|
entry =>
|
||||||
|
|
@ -616,7 +617,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SetsKnownPropertiesOnChildComponents()
|
public void SetsParametersOnChildComponents()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var testObject = new object();
|
var testObject = new object();
|
||||||
|
|
@ -640,41 +641,6 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
Assert.Same(testObject, componentInstance.ObjectProperty);
|
Assert.Same(testObject, componentInstance.ObjectProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ThrowsIfAssigningUnknownPropertiesToChildComponents()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var testObject = new object();
|
|
||||||
newTree.OpenComponent<FakeComponent>(0);
|
|
||||||
newTree.AddAttribute(1, "SomeUnknownProperty", 123);
|
|
||||||
newTree.CloseComponent();
|
|
||||||
|
|
||||||
// Act/Assert
|
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
|
||||||
{
|
|
||||||
RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, oldTree.GetFrames(), newTree.GetFrames());
|
|
||||||
});
|
|
||||||
Assert.Equal($"Component of type '{typeof(FakeComponent).FullName}' does not have a property matching the name 'SomeUnknownProperty'.", ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ThrowsIfAssigningReadOnlyPropertiesToChildComponents()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var testObject = new object();
|
|
||||||
newTree.OpenComponent<FakeComponent>(0);
|
|
||||||
newTree.AddAttribute(1, nameof(FakeComponent.ReadonlyProperty), 123);
|
|
||||||
newTree.CloseComponent();
|
|
||||||
|
|
||||||
// Act/Assert
|
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
|
||||||
{
|
|
||||||
RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, oldTree.GetFrames(), newTree.GetFrames());
|
|
||||||
});
|
|
||||||
Assert.StartsWith($"Unable to set property '{nameof(FakeComponent.ReadonlyProperty)}' on " +
|
|
||||||
$"component of type '{typeof(FakeComponent).FullName}'.", ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RetainsChildComponentsForExistingFrames()
|
public void RetainsChildComponentsForExistingFrames()
|
||||||
{
|
{
|
||||||
|
|
@ -712,7 +678,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdatesChangedPropertiesOnRetainedChildComponents()
|
public void SetsUpdatedParametersOnChildComponents()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var objectWillNotChange = new object();
|
var objectWillNotChange = new object();
|
||||||
|
|
@ -727,7 +693,6 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
|
|
||||||
RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, new RenderTreeBuilder(renderer).GetFrames(), oldTree.GetFrames());
|
RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, new RenderTreeBuilder(renderer).GetFrames(), oldTree.GetFrames());
|
||||||
var originalComponentInstance = (FakeComponent)oldTree.GetFrames().Array[0].Component;
|
var originalComponentInstance = (FakeComponent)oldTree.GetFrames().Array[0].Component;
|
||||||
originalComponentInstance.ObjectProperty = null; // So we can see it doesn't get reassigned
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var renderBatch = GetRenderedBatch();
|
var renderBatch = GetRenderedBatch();
|
||||||
|
|
@ -737,7 +702,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
Assert.Equal(1, renderBatch.UpdatedComponents.Count); // Because the diff builder only queues child component renders; it doesn't actually perfom them itself
|
Assert.Equal(1, renderBatch.UpdatedComponents.Count); // Because the diff builder only queues child component renders; it doesn't actually perfom them itself
|
||||||
Assert.Same(originalComponentInstance, newComponentInstance);
|
Assert.Same(originalComponentInstance, newComponentInstance);
|
||||||
Assert.Equal("String did change", newComponentInstance.StringProperty);
|
Assert.Equal("String did change", newComponentInstance.StringProperty);
|
||||||
Assert.Null(newComponentInstance.ObjectProperty); // To observe that the property wasn't even written, we nulled it out on the original
|
Assert.Same(objectWillNotChange, newComponentInstance.ObjectProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -764,19 +729,26 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
Assert.Equal(new[] { 0, 1 }, batchBuilder.ComponentDisposalQueue);
|
Assert.Equal(new[] { 0, 1 }, batchBuilder.ComponentDisposalQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private (RenderTreeDiff, RenderTreeFrame[]) GetSingleUpdatedComponent()
|
private (RenderTreeDiff, RenderTreeFrame[]) GetSingleUpdatedComponent(bool initializeFromFrames = false)
|
||||||
{
|
{
|
||||||
var batch = GetRenderedBatch();
|
var batch = GetRenderedBatch(initializeFromFrames);
|
||||||
var diffsInBatch = batch.UpdatedComponents;
|
var diffsInBatch = batch.UpdatedComponents;
|
||||||
Assert.Equal(1, diffsInBatch.Count);
|
Assert.Equal(1, diffsInBatch.Count);
|
||||||
return (diffsInBatch.Array[0], batch.ReferenceFrames.ToArray());
|
return (diffsInBatch.Array[0], batch.ReferenceFrames.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private RenderBatch GetRenderedBatch()
|
private RenderBatch GetRenderedBatch(bool initializeFromFrames = false)
|
||||||
=> GetRenderedBatch(oldTree, newTree);
|
=> GetRenderedBatch(oldTree, newTree, initializeFromFrames);
|
||||||
|
|
||||||
private RenderBatch GetRenderedBatch(RenderTreeBuilder from, RenderTreeBuilder to)
|
private RenderBatch GetRenderedBatch(RenderTreeBuilder from, RenderTreeBuilder to, bool initializeFromFrames)
|
||||||
{
|
{
|
||||||
|
if (initializeFromFrames)
|
||||||
|
{
|
||||||
|
var emptyFrames = new RenderTreeBuilder(renderer).GetFrames();
|
||||||
|
var oldFrames = from.GetFrames();
|
||||||
|
RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, emptyFrames, oldFrames);
|
||||||
|
}
|
||||||
|
|
||||||
var batchBuilder = new RenderBatchBuilder();
|
var batchBuilder = new RenderBatchBuilder();
|
||||||
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, from.GetFrames(), to.GetFrames());
|
var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, from.GetFrames(), to.GetFrames());
|
||||||
batchBuilder.UpdatedComponentDiffs.Append(diff);
|
batchBuilder.UpdatedComponentDiffs.Append(diff);
|
||||||
|
|
@ -799,12 +771,22 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
private string PrivateProperty { get; set; }
|
private string PrivateProperty { get; set; }
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle) { }
|
public void Init(RenderHandle renderHandle) { }
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
{
|
||||||
|
parameters.AssignToProperties(this);
|
||||||
|
}
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder) { }
|
public void BuildRenderTree(RenderTreeBuilder builder) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FakeComponent2 : IComponent
|
private class FakeComponent2 : IComponent
|
||||||
{
|
{
|
||||||
public void Init(RenderHandle renderHandle) { }
|
public void Init(RenderHandle renderHandle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
{
|
||||||
|
|
@ -819,6 +801,8 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle) { }
|
public void Init(RenderHandle renderHandle) { }
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters) { }
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder) { }
|
public void BuildRenderTree(RenderTreeBuilder builder) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -826,10 +810,11 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
{
|
{
|
||||||
public void Init(RenderHandle renderHandle) { }
|
public void Init(RenderHandle renderHandle) { }
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters) { }
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder) { }
|
public void BuildRenderTree(RenderTreeBuilder builder) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void AssertEdit(
|
private static void AssertEdit(
|
||||||
RenderTreeEdit edit,
|
RenderTreeEdit edit,
|
||||||
RenderTreeEditType type,
|
RenderTreeEditType type,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Blazor.Test
|
namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
{
|
{
|
||||||
public class RendererTest
|
public partial class RendererTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanRenderTopLevelComponents()
|
public void CanRenderTopLevelComponents()
|
||||||
|
|
@ -310,7 +310,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
Assert.Equal(0, edit.ReferenceFrameIndex);
|
Assert.Equal(0, edit.ReferenceFrameIndex);
|
||||||
});
|
});
|
||||||
AssertFrame.Text(batch.ReferenceFrames[0], "Modified message");
|
AssertFrame.Text(batch.ReferenceFrames[0], "Modified message");
|
||||||
Assert.False(batch.DiffsByComponentId.ContainsKey(nestedComponentFrame.ComponentId));
|
Assert.Empty(batch.DiffsByComponentId[nestedComponentFrame.ComponentId].Single().Edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -388,66 +388,6 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
AssertFrame.Text(renderer.Batches[1].ReferenceFrames[0], "second");
|
AssertFrame.Text(renderer.Batches[1].ReferenceFrames[0], "second");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void NotifiesIHandlePropertiesChangedBeforeFirstRender()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var renderer = new TestRenderer();
|
|
||||||
var component = new HandlePropertiesChangedComponent();
|
|
||||||
var rootComponentId = renderer.AssignComponentId(component);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
renderer.RenderNewBatch(rootComponentId);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
AssertFrame.Text(renderer.Batches.Single().ReferenceFrames[0], "Notifications: 1", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void NotifiesIHandlePropertiesChangedWhenChanged()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var renderer = new TestRenderer();
|
|
||||||
var component = new ConditionalParentComponent<HandlePropertiesChangedComponent>
|
|
||||||
{
|
|
||||||
IncludeChild = true,
|
|
||||||
ChildParameters = new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{ nameof(HandlePropertiesChangedComponent.IntProperty), 123 }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var rootComponentId = renderer.AssignComponentId(component);
|
|
||||||
|
|
||||||
// Act/Assert 0: Initial render
|
|
||||||
renderer.RenderNewBatch(rootComponentId);
|
|
||||||
var batch1 = renderer.Batches.Single();
|
|
||||||
var childComponentFrame = batch1
|
|
||||||
.ReferenceFrames.Where(frame => frame.FrameType == RenderTreeFrameType.Component).Single();
|
|
||||||
var childComponentId = childComponentFrame.ComponentId;
|
|
||||||
var diffForChildComponent0 = batch1.DiffsByComponentId[childComponentId].Single();
|
|
||||||
var childComponentInstance = (HandlePropertiesChangedComponent)childComponentFrame.Component;
|
|
||||||
Assert.Equal(1, childComponentInstance.NotificationsCount);
|
|
||||||
Assert.Collection(diffForChildComponent0.Edits,
|
|
||||||
edit =>
|
|
||||||
{
|
|
||||||
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
|
|
||||||
AssertFrame.Text(batch1.ReferenceFrames[edit.ReferenceFrameIndex], "Notifications: 1", 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act/Assert 1: If properties didn't change, we don't notify
|
|
||||||
renderer.RenderNewBatch(rootComponentId);
|
|
||||||
Assert.Equal(1, childComponentInstance.NotificationsCount);
|
|
||||||
|
|
||||||
// Act/Assert 2: If properties did change, we do notify
|
|
||||||
component.ChildParameters[nameof(HandlePropertiesChangedComponent.IntProperty)] = 456;
|
|
||||||
renderer.RenderNewBatch(rootComponentId);
|
|
||||||
Assert.Equal(2, childComponentInstance.NotificationsCount);
|
|
||||||
var batch3 = renderer.Batches.Skip(2).Single();
|
|
||||||
var diffForChildComponent2 = batch3.DiffsByComponentId[childComponentId].Single();
|
|
||||||
Assert.Equal(2, childComponentInstance.NotificationsCount);
|
|
||||||
AssertFrame.Text(batch3.ReferenceFrames[0], "Notifications: 2", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RenderBatchIncludesListOfDisposedComponents()
|
public void RenderBatchIncludesListOfDisposedComponents()
|
||||||
{
|
{
|
||||||
|
|
@ -708,7 +648,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var batch = renderer.Batches.Single();
|
var batch = renderer.Batches.Single();
|
||||||
Assert.Equal(3, batch.DiffsInOrder.Count);
|
Assert.Equal(4, batch.DiffsInOrder.Count);
|
||||||
|
|
||||||
// First is the parent component's initial render
|
// First is the parent component's initial render
|
||||||
var diff1 = batch.DiffsInOrder[0];
|
var diff1 = batch.DiffsInOrder[0];
|
||||||
|
|
@ -743,6 +683,11 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
Assert.Equal(RenderTreeEditType.UpdateText, diff3edit.Type);
|
Assert.Equal(RenderTreeEditType.UpdateText, diff3edit.Type);
|
||||||
AssertFrame.Text(batch.ReferenceFrames[diff3edit.ReferenceFrameIndex],
|
AssertFrame.Text(batch.ReferenceFrames[diff3edit.ReferenceFrameIndex],
|
||||||
"Parent render count: 2");
|
"Parent render count: 2");
|
||||||
|
|
||||||
|
// Fourth is child's rerender due to parent rendering
|
||||||
|
var diff4 = batch.DiffsInOrder[3];
|
||||||
|
Assert.NotEqual(parentComponentId, diff4.ComponentId);
|
||||||
|
Assert.Empty(diff4.Edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NoOpRenderer : Renderer
|
private class NoOpRenderer : Renderer
|
||||||
|
|
@ -750,9 +695,6 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
public new int AssignComponentId(IComponent component)
|
public new int AssignComponentId(IComponent component)
|
||||||
=> base.AssignComponentId(component);
|
=> base.AssignComponentId(component);
|
||||||
|
|
||||||
public new void RenderNewBatch(int componentId)
|
|
||||||
=> base.RenderNewBatch(componentId);
|
|
||||||
|
|
||||||
protected override void UpdateDisplay(RenderBatch renderBatch)
|
protected override void UpdateDisplay(RenderBatch renderBatch)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -832,6 +774,9 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
_renderHandle = renderHandle;
|
_renderHandle = renderHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetParameters(ParameterCollection parameters)
|
||||||
|
=> _renderHandle.Render();
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
=> _renderAction(builder);
|
=> _renderAction(builder);
|
||||||
|
|
||||||
|
|
@ -839,15 +784,11 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
=> _renderHandle.Render();
|
=> _renderHandle.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MessageComponent : IComponent
|
private class MessageComponent : AutoRenderComponent
|
||||||
{
|
{
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle)
|
public override void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.AddText(0, Message);
|
builder.AddText(0, Message);
|
||||||
}
|
}
|
||||||
|
|
@ -859,25 +800,24 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
public string StringProperty { get; set; }
|
public string StringProperty { get; set; }
|
||||||
public object ObjectProperty { get; set; }
|
public object ObjectProperty { get; set; }
|
||||||
|
|
||||||
|
public void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle)
|
public void Init(RenderHandle renderHandle)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
public void SetParameters(ParameterCollection parameters)
|
||||||
{
|
=> parameters.AssignToProperties(this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EventComponent : IComponent
|
private class EventComponent : AutoRenderComponent, IComponent
|
||||||
{
|
{
|
||||||
public UIEventHandler Handler { get; set; }
|
public UIEventHandler Handler { get; set; }
|
||||||
public bool SkipElement { get; set; }
|
public bool SkipElement { get; set; }
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle)
|
public override void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.OpenElement(0, "grandparent");
|
builder.OpenElement(0, "grandparent");
|
||||||
if (!SkipElement)
|
if (!SkipElement)
|
||||||
|
|
@ -895,16 +835,12 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ConditionalParentComponent<T> : IComponent where T : IComponent
|
private class ConditionalParentComponent<T> : AutoRenderComponent where T : IComponent
|
||||||
{
|
{
|
||||||
public bool IncludeChild { get; set; }
|
public bool IncludeChild { get; set; }
|
||||||
public IDictionary<string, object> ChildParameters { get; set; }
|
public IDictionary<string, object> ChildParameters { get; set; }
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle)
|
public override void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
|
||||||
{
|
{
|
||||||
builder.AddText(0, "Parent here");
|
builder.AddText(0, "Parent here");
|
||||||
|
|
||||||
|
|
@ -923,36 +859,13 @@ namespace Microsoft.AspNetCore.Blazor.Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HandlePropertiesChangedComponent : IComponent, IHandlePropertiesChanged
|
|
||||||
{
|
|
||||||
public int NotificationsCount { get; private set; }
|
|
||||||
|
|
||||||
public int IntProperty { get; set; }
|
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
|
||||||
{
|
|
||||||
builder.AddText(0, $"Notifications: {NotificationsCount}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPropertiesChanged()
|
|
||||||
{
|
|
||||||
NotificationsCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ReRendersParentComponent : IComponent
|
private class ReRendersParentComponent : AutoRenderComponent
|
||||||
{
|
{
|
||||||
public TestComponent Parent { get; set; }
|
public TestComponent Parent { get; set; }
|
||||||
private bool _isFirstTime = true;
|
private bool _isFirstTime = true;
|
||||||
|
|
||||||
public void Init(RenderHandle renderHandle) { }
|
public override void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
|
|
||||||
public void BuildRenderTree(RenderTreeBuilder builder)
|
|
||||||
{
|
{
|
||||||
if (_isFirstTime) // Don't want an infinite loop
|
if (_isFirstTime) // Don't want an infinite loop
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,9 @@
|
||||||
|
|
||||||
@functions {
|
@functions {
|
||||||
public int SuppliedValue { get; set; }
|
public int SuppliedValue { get; set; }
|
||||||
private int ComputedValue { get; set; }
|
|
||||||
|
|
||||||
public override void OnPropertiesChanged()
|
// TODO: Instead of computing this in the getter, override OnParametersChanged or whatever
|
||||||
{
|
// it ends up being called when that's implemented on BlazorComponent
|
||||||
ComputedValue = SuppliedValue * 2;
|
private int ComputedValue
|
||||||
}
|
=> SuppliedValue * 2;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue