From 9dcb1c4fc4ae3f162c72649f909aa014ad03ff16 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 26 Jan 2018 08:47:53 -0800 Subject: [PATCH] Begin on setting properties on child components --- .../RenderTree/RenderTreeDiffComputer.cs | 3 +- .../Rendering/Renderer.cs | 54 +++++++++++++------ .../RenderTreeDiffComputerTest.cs | 39 ++++++++++++++ 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs index 7b7786bdfd..90f7ee6424 100644 --- a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs +++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Blazor.Components; using Microsoft.AspNetCore.Blazor.Rendering; namespace Microsoft.AspNetCore.Blazor.RenderTree @@ -339,7 +338,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree throw new InvalidOperationException($"Child component already exists during {nameof(InstantiateChildComponents)}"); } - _renderer.InstantiateChildComponent(ref nodes[i]); + _renderer.InstantiateChildComponent(nodes, i); } } } diff --git a/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs b/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs index 70886ffae2..98372bcf48 100644 --- a/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs +++ b/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs @@ -42,23 +42,6 @@ namespace Microsoft.AspNetCore.Blazor.Rendering } } - internal void InstantiateChildComponent(ref RenderTreeNode node) - { - if (node.NodeType != RenderTreeNodeType.Component) - { - throw new ArgumentException($"The node's {nameof(RenderTreeNode.NodeType)} property must equal {RenderTreeNodeType.Component}", nameof(node)); - } - - if (node.Component != null) - { - throw new ArgumentException($"The node already has a non-null component instance", nameof(node)); - } - - var newComponent = (IComponent)Activator.CreateInstance(node.ComponentType); - var newComponentId = AssignComponentId(newComponent); - node.SetChildComponentInstance(newComponentId, newComponent); - } - /// /// Updates the visible UI to display the supplied /// at the location corresponding to the . @@ -84,9 +67,46 @@ namespace Microsoft.AspNetCore.Blazor.Rendering protected void DispatchEvent(int componentId, int renderTreeIndex, UIEventArgs eventArgs) => GetRequiredComponentState(componentId).DispatchEvent(renderTreeIndex, eventArgs); + internal void InstantiateChildComponent(RenderTreeNode[] nodes, int componentNodeIndex) + { + ref var node = ref nodes[componentNodeIndex]; + if (node.NodeType != RenderTreeNodeType.Component) + { + throw new ArgumentException($"The node's {nameof(RenderTreeNode.NodeType)} property must equal {RenderTreeNodeType.Component}", nameof(node)); + } + + if (node.Component != null) + { + throw new ArgumentException($"The node already has a non-null component instance", nameof(node)); + } + + var newComponent = (IComponent)Activator.CreateInstance(node.ComponentType); + var newComponentId = AssignComponentId(newComponent); + node.SetChildComponentInstance(newComponentId, newComponent); + SetPropertiesOnComponent(nodes, componentNodeIndex); + } + private ComponentState GetRequiredComponentState(int componentId) => _componentStateById.TryGetValue(componentId, out var componentState) ? componentState : throw new ArgumentException($"The renderer does not have a component with ID {componentId}."); + + private void SetPropertiesOnComponent(RenderTreeNode[] nodes, int componentNodeIndex) + { + ref var componentNode = ref nodes[componentNodeIndex]; + var component = componentNode.Component; + var descendantsEndIndex = componentNode.ElementDescendantsEndIndex; + for (var attributeNodeIndex = componentNodeIndex + 1; attributeNodeIndex <= descendantsEndIndex; attributeNodeIndex++) + { + SetPropertyOnComponent(component, nodes[attributeNodeIndex]); + } + } + + private void SetPropertyOnComponent(IComponent component, in RenderTreeNode attributeNode) + { + // TODO: Cache the reflection + var property = component.GetType().GetProperty(attributeNode.AttributeName); + property.SetValue(component, attributeNode.AttributeValue); + } } } diff --git a/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs b/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs index f712fe5f9e..0dd403a544 100644 --- a/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs @@ -674,6 +674,39 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0)); } + [Fact] + public void SetsKnownPropertiesOnChildComponents() + { + // Arrange + var renderer = new FakeRenderer(); + var oldTree = new RenderTreeBuilder(renderer); + var newTree = new RenderTreeBuilder(renderer); + var diff = new RenderTreeDiffComputer(renderer); + var testObject = new object(); + newTree.OpenComponentElement(0); + //newTree.AddAttribute(1, nameof(FakeComponent.IntProperty), 123); + newTree.AddAttribute(2, nameof(FakeComponent.StringProperty), "some string"); + //newTree.AddAttribute(3, nameof(FakeComponent.ObjectProperty), testObject); + newTree.CloseElement(); + + // Act + var result = diff.ApplyNewRenderTreeVersion(oldTree.GetNodes(), newTree.GetNodes()); + var componentInstance = newTree.GetNodes().First().Component as FakeComponent; + + // Assert + AssertEdit(result.Edits.Single(), RenderTreeEditType.PrependNode, 0); + Assert.NotNull(componentInstance); + //Assert.Equal(123, componentInstance.IntProperty); + Assert.Equal("some string", componentInstance.StringProperty); + //Assert.Same(testObject, componentInstance.ObjectProperty); + } + + [Fact] + public void RejectsUnknownPropertiesOnChildComponents() + { + + } + [Fact] public void RetainsChildComponentsForExistingNodes() { @@ -722,6 +755,12 @@ namespace Microsoft.AspNetCore.Blazor.Test private class FakeComponent : IComponent { + public int IntProperty { get; set; } + public string StringProperty { get; set; } + public object ObjectProperty { get; set; } + public string ReadonlyProperty { get; private set; } + private string PrivateProperty { get; set; } + public void BuildRenderTree(RenderTreeBuilder builder) => throw new NotImplementedException(); }