Give attribute nodes a single object-valued value so they can be used for component properties too.

This commit is contained in:
Steve Sanderson 2018-01-26 09:40:48 -08:00
parent 9dcb1c4fc4
commit 1b4fa4781a
7 changed files with 21 additions and 27 deletions

View File

@ -1,6 +1,6 @@
import { System_String, System_Array, Pointer } from '../Platform/Platform'; import { System_String, System_Array, Pointer } from '../Platform/Platform';
import { platform } from '../Environment'; import { platform } from '../Environment';
const renderTreeNodeStructLength = 44; const renderTreeNodeStructLength = 40;
// To minimise GC pressure, instead of instantiating a JS object to represent each tree node, // To minimise GC pressure, instead of instantiating a JS object to represent each tree node,
// we work in terms of pointers to the structs on the .NET heap, and use static functions that // we work in terms of pointers to the structs on the .NET heap, and use static functions that
@ -18,9 +18,8 @@ export const renderTreeNode = {
textContent: (node: RenderTreeNodePointer) => platform.readStringField(node, 16), textContent: (node: RenderTreeNodePointer) => platform.readStringField(node, 16),
attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 20), attributeName: (node: RenderTreeNodePointer) => platform.readStringField(node, 20),
attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 24), attributeValue: (node: RenderTreeNodePointer) => platform.readStringField(node, 24),
attributeEventHandlerValue: (node: RenderTreeNodePointer) => platform.readObjectField(node, 28), componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 32),
componentId: (node: RenderTreeNodePointer) => platform.readInt32Field(node, 36), component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 36),
component: (node: RenderTreeNodePointer) => platform.readObjectField(node, 40),
}; };
export enum NodeType { export enum NodeType {

View File

@ -271,10 +271,11 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
var newName = newTree[newNodeIndex].AttributeName; var newName = newTree[newNodeIndex].AttributeName;
if (string.Equals(oldName, newName, StringComparison.Ordinal)) if (string.Equals(oldName, newName, StringComparison.Ordinal))
{ {
var changed = // Using Equals to account for string comparisons, nulls, etc.
!string.Equals(oldTree[oldNodeIndex].AttributeValue, newTree[newNodeIndex].AttributeValue, StringComparison.Ordinal) var valueChanged = !Equals(
|| oldTree[oldNodeIndex].AttributeEventHandlerValue != newTree[newNodeIndex].AttributeEventHandlerValue; oldTree[oldNodeIndex].AttributeValue,
if (changed) newTree[newNodeIndex].AttributeValue);
if (valueChanged)
{ {
Append(RenderTreeEdit.SetAttribute(siblingIndex, newNodeIndex)); Append(RenderTreeEdit.SetAttribute(siblingIndex, newNodeIndex));
} }

View File

@ -56,13 +56,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
/// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Attribute"/>, /// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Attribute"/>,
/// gets the attribute value. Otherwise, the value is <see langword="null"/>. /// gets the attribute value. Otherwise, the value is <see langword="null"/>.
/// </summary> /// </summary>
public string AttributeValue { get; private set; } public object AttributeValue { get; private set; }
/// <summary>
/// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Attribute"/>,
/// gets the attribute's event handle, if any. Otherwise, the value is <see langword="null"/>.
/// </summary>
public UIEventHandler AttributeEventHandlerValue { get; private set; }
/// <summary> /// <summary>
/// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Component"/>, /// If the <see cref="NodeType"/> property equals <see cref="RenderTreeNodeType.Component"/>,
@ -109,7 +103,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
Sequence = sequence, Sequence = sequence,
NodeType = RenderTreeNodeType.Attribute, NodeType = RenderTreeNodeType.Attribute,
AttributeName = name, AttributeName = name,
AttributeEventHandlerValue = value AttributeValue = value
}; };
internal static RenderTreeNode ChildComponent<T>(int sequence) where T: IComponent => new RenderTreeNode internal static RenderTreeNode ChildComponent<T>(int sequence) where T: IComponent => new RenderTreeNode

View File

@ -67,10 +67,10 @@ namespace Microsoft.AspNetCore.Blazor.Rendering
} }
var nodes = _renderTreeBuilderCurrent.GetNodes(); var nodes = _renderTreeBuilderCurrent.GetNodes();
var eventHandler = nodes.Array[renderTreeIndex].AttributeEventHandlerValue; var eventHandler = nodes.Array[renderTreeIndex].AttributeValue as UIEventHandler;
if (eventHandler == null) if (eventHandler == null)
{ {
throw new ArgumentException($"The render tree node at index {renderTreeIndex} has a null value for {nameof(RenderTreeNode.AttributeEventHandlerValue)}."); throw new ArgumentException($"The render tree node at index {renderTreeIndex} does not specify a {nameof(UIEventHandler)}.");
} }
eventHandler.Invoke(eventArgs); eventHandler.Invoke(eventArgs);

View File

@ -254,9 +254,9 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
node => node =>
{ {
Assert.Equal(RenderTreeNodeType.Attribute, node.NodeType); Assert.Equal(RenderTreeNodeType.Attribute, node.NodeType);
Assert.NotNull(node.AttributeEventHandlerValue); Assert.NotNull(node.AttributeValue);
node.AttributeEventHandlerValue(null); ((UIEventHandler)node.AttributeValue)(null);
Assert.True((bool)handlerWasCalledProperty.GetValue(component)); Assert.True((bool)handlerWasCalledProperty.GetValue(component));
}); });
} }
@ -279,9 +279,9 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
node => node =>
{ {
Assert.Equal(RenderTreeNodeType.Attribute, node.NodeType); Assert.Equal(RenderTreeNodeType.Attribute, node.NodeType);
Assert.NotNull(node.AttributeEventHandlerValue); Assert.NotNull(node.AttributeValue);
node.AttributeEventHandlerValue(null); ((UIEventHandler)node.AttributeValue)(null);
Assert.True((bool)didInvokeCodeProperty.GetValue(component)); Assert.True((bool)didInvokeCodeProperty.GetValue(component));
}); });
} }
@ -325,9 +325,9 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
node => node =>
{ {
Assert.Equal(RenderTreeNodeType.Attribute, node.NodeType); Assert.Equal(RenderTreeNodeType.Attribute, node.NodeType);
Assert.NotNull(node.AttributeEventHandlerValue); Assert.NotNull(node.AttributeValue);
node.AttributeEventHandlerValue(null); ((UIEventHandler)node.AttributeValue)(null);
Assert.True((bool)didInvokeCodeProperty.GetValue(component)); Assert.True((bool)didInvokeCodeProperty.GetValue(component));
}); });
} }

View File

@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
var (eventHandlerNodeIndex, _) = FirstWithIndex( var (eventHandlerNodeIndex, _) = FirstWithIndex(
renderer.RenderTreesByComponentId[componentId], renderer.RenderTreesByComponentId[componentId],
node => node.AttributeEventHandlerValue != null); node => node.AttributeValue != null);
// Assert: Event not yet fired // Assert: Event not yet fired
Assert.Null(receivedArgs); Assert.Null(receivedArgs);
@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Blazor.Test
// Find nested component's event handler ndoe // Find nested component's event handler ndoe
var (eventHandlerNodeIndex, _) = FirstWithIndex( var (eventHandlerNodeIndex, _) = FirstWithIndex(
renderer.RenderTreesByComponentId[nestedComponentId], renderer.RenderTreesByComponentId[nestedComponentId],
node => node.AttributeEventHandlerValue != null); node => node.AttributeValue != null);
// Assert: Event not yet fired // Assert: Event not yet fired
Assert.Null(receivedArgs); Assert.Null(receivedArgs);

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Blazor.Test.Shared
public static void Attribute(RenderTreeNode node, string attributeName, UIEventHandler attributeEventHandlerValue) public static void Attribute(RenderTreeNode node, string attributeName, UIEventHandler attributeEventHandlerValue)
{ {
AssertNode.Attribute(node, attributeName); AssertNode.Attribute(node, attributeName);
Assert.Equal(attributeEventHandlerValue, node.AttributeEventHandlerValue); Assert.Equal(attributeEventHandlerValue, node.AttributeValue);
} }
public static void Component<T>(RenderTreeNode node) where T : IComponent public static void Component<T>(RenderTreeNode node) where T : IComponent