diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/BrowserRenderer.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/BrowserRenderer.ts index 09168332aa..1087f182db 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/BrowserRenderer.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/BrowserRenderer.ts @@ -229,7 +229,7 @@ function removeAttributeFromDOM(parent: Element, childIndex: number, attributeNa element.removeAttribute(attributeName); } -function raiseEvent(browserRendererId: number, componentId: number, renderTreeFrameIndex: number, eventInfoType: EventInfoType, eventInfo: any) { +function raiseEvent(browserRendererId: number, componentId: number, referenceTreeFrameIndex: number, eventInfoType: EventInfoType, eventInfo: any) { if (!raiseEventMethod) { raiseEventMethod = platform.findMethod( 'Microsoft.AspNetCore.Blazor.Browser', 'Microsoft.AspNetCore.Blazor.Browser.Rendering', 'BrowserRendererEventDispatcher', 'DispatchEvent' @@ -239,7 +239,7 @@ function raiseEvent(browserRendererId: number, componentId: number, renderTreeFr const eventDescriptor = { BrowserRendererId: browserRendererId, ComponentId: componentId, - RenderTreeFrameIndex: renderTreeFrameIndex, + ReferenceTreeFrameIndex: referenceTreeFrameIndex, EventArgsType: eventInfoType }; diff --git a/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs b/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs index 49d5091672..947af8a3e4 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs +++ b/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs @@ -32,8 +32,8 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Rendering _browserRendererId = BrowserRendererRegistry.Add(this); } - internal void DispatchBrowserEvent(int componentId, int renderTreeIndex, UIEventArgs eventArgs) - => DispatchEvent(componentId, renderTreeIndex, eventArgs); + internal void DispatchBrowserEvent(int componentId, int referenceTreeIndex, UIEventArgs eventArgs) + => DispatchEvent(componentId, referenceTreeIndex, eventArgs); internal void RenderNewBatchInternal(int componentId) => RenderNewBatch(componentId); diff --git a/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRendererEventDispatcher.cs b/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRendererEventDispatcher.cs index a82582a3a8..51cab8fa7c 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRendererEventDispatcher.cs +++ b/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRendererEventDispatcher.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Rendering var browserRenderer = BrowserRendererRegistry.Find(eventDescriptor.BrowserRendererId); browserRenderer.DispatchBrowserEvent( eventDescriptor.ComponentId, - eventDescriptor.RenderTreeFrameIndex, + eventDescriptor.ReferenceTreeFrameIndex, eventArgs); } @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Rendering { public int BrowserRendererId { get; set; } public int ComponentId { get; set; } - public int RenderTreeFrameIndex { get; set; } + public int ReferenceTreeFrameIndex { get; set; } public string EventArgsType { get; set; } } } diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/ArrayBuilder.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/ArrayBuilder.cs index d11c136fc6..1a68fcdf5a 100644 --- a/src/Microsoft.AspNetCore.Blazor/RenderTree/ArrayBuilder.cs +++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/ArrayBuilder.cs @@ -51,15 +51,40 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree /// Appends a new item, automatically resizing the underlying array if necessary. /// /// The item to append. + /// The index of the appended item. [MethodImpl(MethodImplOptions.AggressiveInlining)] // Just like System.Collections.Generic.List - public void Append(in T item) + public int Append(in T item) { if (_itemsInUse == _items.Length) { SetCapacity(_itemsInUse * 2, preserveContents: true); } - _items[_itemsInUse++] = item; + var indexOfAppendedItem = _itemsInUse++; + _items[indexOfAppendedItem] = item; + return indexOfAppendedItem; + } + + internal int Append(T[] source, int startIndex, int length) + { + // Expand storage if needed. Using same doubling approach as would + // be used if you inserted the items one-by-one. + var requiredCapacity = _itemsInUse + length; + if (_items.Length < requiredCapacity) + { + var candidateCapacity = _itemsInUse * 2; + while (candidateCapacity < requiredCapacity) + { + candidateCapacity *= 2; + } + + SetCapacity(candidateCapacity, preserveContents: true); + } + + Array.Copy(source, startIndex, _items, _itemsInUse, length); + var startIndexOfAppendedItems = _itemsInUse; + _itemsInUse += length; + return startIndexOfAppendedItems; } /// @@ -94,9 +119,9 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree { SetCapacity((previousItemsInUse + _items.Length) / 2, preserveContents: false); } - else + else if (previousItemsInUse > 0) { - Array.Clear(_items, 0, _itemsInUse); // Release to GC + Array.Clear(_items, 0, previousItemsInUse); // Release to GC } } diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiff.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiff.cs index 7da223a010..d8ff75ec59 100644 --- a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiff.cs +++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiff.cs @@ -1,13 +1,10 @@ // 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; - namespace Microsoft.AspNetCore.Blazor.RenderTree { /// - /// Describes changes to a component's render tree between successive renders, - /// as well as the resulting state. + /// Describes changes to a component's render tree between successive renders. /// public readonly struct RenderTreeDiff { @@ -22,19 +19,20 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree public ArrayRange Edits { get; } /// - /// Gets the latest render tree. That is, the result of applying the - /// to the previous state. + /// Gets render frames that may be referenced by entries in . + /// For example, edit entries of type + /// will point to an entry in this array to specify the subtree to be prepended. /// - public ArrayRange CurrentState { get; } + public ArrayRange ReferenceFrames { get; } internal RenderTreeDiff( int componentId, ArrayRange entries, - ArrayRange referenceTree) + ArrayRange referenceFrames) { ComponentId = componentId; Edits = entries; - CurrentState = referenceTree; + ReferenceFrames = referenceFrames; } } } diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs index a432cabd3e..138462758f 100644 --- a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs +++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffComputer.cs @@ -12,6 +12,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree { private readonly Renderer _renderer; private readonly ArrayBuilder _entries = new ArrayBuilder(10); + private readonly ArrayBuilder _referenceFrames = new ArrayBuilder(10); public RenderTreeDiffComputer(Renderer renderer) { @@ -31,13 +32,14 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree ArrayRange newTree) { _entries.Clear(); + _referenceFrames.Clear(); var siblingIndex = 0; var slotId = batchBuilder.ReserveUpdatedComponentSlotId(); AppendDiffEntriesForRange(batchBuilder, oldTree.Array, 0, oldTree.Count, newTree.Array, 0, newTree.Count, ref siblingIndex); batchBuilder.SetUpdatedComponent( slotId, - new RenderTreeDiff(componentId, _entries.ToRange(), newTree)); + new RenderTreeDiff(componentId, _entries.ToRange(), _referenceFrames.ToRange())); } private void AppendDiffEntriesForRange( @@ -136,7 +138,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree var newFrameType = newFrame.FrameType; if (newFrameType == RenderTreeFrameType.Attribute) { - Append(RenderTreeEdit.SetAttribute(siblingIndex, newStartIndex)); + var referenceFrameIndex = _referenceFrames.Append(newFrame); + Append(RenderTreeEdit.SetAttribute(siblingIndex, referenceFrameIndex)); newStartIndex++; } else @@ -146,7 +149,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree InstantiateChildComponents(batchBuilder, newTree, newStartIndex); } - Append(RenderTreeEdit.PrependFrame(siblingIndex, newStartIndex)); + var referenceFrameIndex = AppendSubtreeToReferenceFrames(newTree, newStartIndex); + Append(RenderTreeEdit.PrependFrame(siblingIndex, referenceFrameIndex)); newStartIndex = NextSiblingIndex(newFrame, newStartIndex); siblingIndex++; } @@ -180,6 +184,35 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree } } + public UIEventHandler TemporaryGetEventHandlerMethod(int referenceFrameIndex) + { + // TODO: Change how event handlers are referenced + // The approach used here is invalid. We can't really assume that the event handler exists + // in the most recent diff's _referenceFrames. Nor can we assume that its index in the + // ComponentState's _renderTreeBuilderCurrent frames array is unchanged (any number of rerenders + // might have taken place since then, inserting frames before it, but not actually changing + // the delegate instance and therefore not including it in any _referenceFrames). + // Need some way of referencing event handlers that is independent of this. + var eventHandler = _referenceFrames.Buffer[referenceFrameIndex].AttributeValue as UIEventHandler; + return eventHandler + ?? throw new ArgumentException($"The reference frame at index {referenceFrameIndex} does not specify a {nameof(UIEventHandler)}."); + } + + private int AppendSubtreeToReferenceFrames(RenderTreeFrame[] fromTree, int fromIndex) + { + ref var rootFrame = ref fromTree[fromIndex]; + var subtreeLength = rootFrame.ElementSubtreeLength; + if (subtreeLength == 0) + { + // It's just a single frame (e.g., a text frame) + return _referenceFrames.Append(rootFrame); + } + else + { + return _referenceFrames.Append(fromTree, fromIndex, subtreeLength); + } + } + private void UpdateRetainedChildComponent( RenderBatchBuilder batchBuilder, RenderTreeFrame[] oldTree, int oldComponentIndex, @@ -340,7 +373,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree var newText = newFrame.TextContent; if (!string.Equals(oldText, newText, StringComparison.Ordinal)) { - Append(RenderTreeEdit.UpdateText(siblingIndex, newFrameIndex)); + var referenceFrameIndex = _referenceFrames.Append(newFrame); + Append(RenderTreeEdit.UpdateText(siblingIndex, referenceFrameIndex)); } siblingIndex++; break; @@ -390,7 +424,9 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree // Elements with different names are treated as completely unrelated InstantiateChildComponents(batchBuilder, newTree, newFrameIndex); DisposeChildComponents(batchBuilder, oldTree, oldFrameIndex); - Append(RenderTreeEdit.PrependFrame(siblingIndex, newFrameIndex)); + + var referenceFrameIndex = AppendSubtreeToReferenceFrames(newTree, newFrameIndex); + Append(RenderTreeEdit.PrependFrame(siblingIndex, referenceFrameIndex)); siblingIndex++; Append(RenderTreeEdit.RemoveFrame(siblingIndex)); } @@ -415,7 +451,9 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree // Child components of different types are treated as completely unrelated InstantiateChildComponents(batchBuilder, newTree, newFrameIndex); DisposeChildComponents(batchBuilder, oldTree, oldFrameIndex); - Append(RenderTreeEdit.PrependFrame(siblingIndex, newFrameIndex)); + + var referenceFrameIndex = AppendSubtreeToReferenceFrames(newTree, newFrameIndex); + Append(RenderTreeEdit.PrependFrame(siblingIndex, referenceFrameIndex)); siblingIndex++; Append(RenderTreeEdit.RemoveFrame(siblingIndex)); } @@ -432,7 +470,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree var valueChanged = !Equals(oldFrame.AttributeValue, newFrame.AttributeValue); if (valueChanged) { - Append(RenderTreeEdit.SetAttribute(siblingIndex, newFrameIndex)); + var referenceFrameIndex = _referenceFrames.Append(newFrame); + Append(RenderTreeEdit.SetAttribute(siblingIndex, referenceFrameIndex)); } } else @@ -440,7 +479,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree // 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 - Append(RenderTreeEdit.SetAttribute(siblingIndex, newFrameIndex)); + var referenceFrameIndex = _referenceFrames.Append(newFrame); + Append(RenderTreeEdit.SetAttribute(siblingIndex, referenceFrameIndex)); Append(RenderTreeEdit.RemoveAttribute(siblingIndex, oldName)); } break; diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeEdit.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeEdit.cs index 2b641bcad5..ffefbd510a 100644 --- a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeEdit.cs +++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeEdit.cs @@ -19,11 +19,11 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree public readonly int SiblingIndex; /// - /// Gets the index of related data in an associated render tree. For example, if the + /// Gets the index of related data in an associated render frames array. For example, if the /// value is , gets the /// index of the new frame data in an associated render tree. /// - public readonly int NewTreeIndex; + public readonly int ReferenceFrameIndex; /// /// If the value is , @@ -42,11 +42,11 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree SiblingIndex = siblingIndex; } - private RenderTreeEdit(RenderTreeEditType type, int siblingIndex, int newTreeIndex) : this() + private RenderTreeEdit(RenderTreeEditType type, int siblingIndex, int referenceFrameIndex) : this() { Type = type; SiblingIndex = siblingIndex; - NewTreeIndex = newTreeIndex; + ReferenceFrameIndex = referenceFrameIndex; } private RenderTreeEdit(RenderTreeEditType type, int siblingIndex, string removedAttributeName) : this() @@ -59,14 +59,14 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree internal static RenderTreeEdit RemoveFrame(int siblingIndex) => new RenderTreeEdit(RenderTreeEditType.RemoveFrame, siblingIndex); - internal static RenderTreeEdit PrependFrame(int siblingIndex, int newTreeIndex) - => new RenderTreeEdit(RenderTreeEditType.PrependFrame, siblingIndex, newTreeIndex); + internal static RenderTreeEdit PrependFrame(int siblingIndex, int referenceFrameIndex) + => new RenderTreeEdit(RenderTreeEditType.PrependFrame, siblingIndex, referenceFrameIndex); - internal static RenderTreeEdit UpdateText(int siblingIndex, int newTreeIndex) - => new RenderTreeEdit(RenderTreeEditType.UpdateText, siblingIndex, newTreeIndex); + internal static RenderTreeEdit UpdateText(int siblingIndex, int referenceFrameIndex) + => new RenderTreeEdit(RenderTreeEditType.UpdateText, siblingIndex, referenceFrameIndex); - internal static RenderTreeEdit SetAttribute(int siblingIndex, int newTreeIndex) - => new RenderTreeEdit(RenderTreeEditType.SetAttribute, siblingIndex, newTreeIndex); + internal static RenderTreeEdit SetAttribute(int siblingIndex, int referenceFrameIndex) + => new RenderTreeEdit(RenderTreeEditType.SetAttribute, siblingIndex, referenceFrameIndex); internal static RenderTreeEdit RemoveAttribute(int siblingIndex, string name) => new RenderTreeEdit(RenderTreeEditType.RemoveAttribute, siblingIndex, name); diff --git a/src/Microsoft.AspNetCore.Blazor/Rendering/ComponentState.cs b/src/Microsoft.AspNetCore.Blazor/Rendering/ComponentState.cs index 3c8c441f12..119fb3e058 100644 --- a/src/Microsoft.AspNetCore.Blazor/Rendering/ComponentState.cs +++ b/src/Microsoft.AspNetCore.Blazor/Rendering/ComponentState.cs @@ -58,22 +58,16 @@ namespace Microsoft.AspNetCore.Blazor.Rendering /// /// Invokes the handler corresponding to an event. /// - /// The index of the current render tree frame that holds the event handler to be invoked. + /// The index of the frame in the latest diff reference tree that holds the event handler to be invoked. /// Arguments to be passed to the event handler. - public void DispatchEvent(int renderTreeIndex, UIEventArgs eventArgs) + public void DispatchEvent(int referenceTreeIndex, UIEventArgs eventArgs) { if (eventArgs == null) { throw new ArgumentNullException(nameof(eventArgs)); } - var frames = _renderTreeBuilderCurrent.GetFrames(); - var eventHandler = frames.Array[renderTreeIndex].AttributeValue as UIEventHandler; - if (eventHandler == null) - { - throw new ArgumentException($"The render tree frame at index {renderTreeIndex} does not specify a {nameof(UIEventHandler)}."); - } - + var eventHandler = _diffComputer.TemporaryGetEventHandlerMethod(referenceTreeIndex); eventHandler.Invoke(eventArgs); // After any event, we synchronously re-render. Most of the time this means that diff --git a/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs b/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs index 7c2d3fa4d1..75902d6a85 100644 --- a/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs +++ b/src/Microsoft.AspNetCore.Blazor/Rendering/Renderer.cs @@ -106,10 +106,10 @@ namespace Microsoft.AspNetCore.Blazor.Rendering /// Notifies the specified component that an event has occurred. /// /// The unique identifier for the component within the scope of this . - /// The index into the component's current render tree that specifies which event handler to invoke. + /// The index into the component's latest diff reference tree that specifies which event handler to invoke. /// Arguments to be passed to the event handler. - protected void DispatchEvent(int componentId, int renderTreeIndex, UIEventArgs eventArgs) - => GetRequiredComponentState(componentId).DispatchEvent(renderTreeIndex, eventArgs); + protected void DispatchEvent(int componentId, int referenceTreeIndex, UIEventArgs eventArgs) + => GetRequiredComponentState(componentId).DispatchEvent(referenceTreeIndex, eventArgs); internal void InstantiateChildComponent(RenderTreeFrame[] frames, int componentFrameIndex) { diff --git a/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs b/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs index 6b80b81db9..cdcd043f03 100644 --- a/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Test/RenderTreeDiffComputerTest.cs @@ -74,7 +74,8 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 1); - Assert.Equal(1, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); + AssertFrame.Text(result.ReferenceFrames.Array[0], "text1", 1); }); } @@ -120,12 +121,12 @@ namespace Microsoft.AspNetCore.Blazor.Test public void RecognizesTrailingSequenceWithinLoopBlockBeingAppended() { // Arrange - oldTree.AddText(0, "x"); // Loop start - oldTree.AddText(0, "x"); // Loop start - newTree.AddText(0, "x"); // Loop start - newTree.AddText(1, "x"); // Will be added - newTree.AddText(2, "x"); // Will be added - newTree.AddText(0, "x"); // Loop start + oldTree.AddText(10, "x"); // Loop start + oldTree.AddText(10, "x"); // Loop start + newTree.AddText(10, "x"); // Loop start + newTree.AddText(11, "x"); // Will be added + newTree.AddText(12, "x"); // Will be added + newTree.AddText(10, "x"); // Loop start // Act var result = GetSingleUpdatedComponent(); @@ -135,13 +136,16 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 1); - Assert.Equal(1, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 2); - Assert.Equal(2, entry.NewTreeIndex); + Assert.Equal(1, entry.ReferenceFrameIndex); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Text(frame, "x", 11), + frame => AssertFrame.Text(frame, "x", 12)); } [Fact] @@ -168,12 +172,12 @@ namespace Microsoft.AspNetCore.Blazor.Test public void RecognizesTrailingLoopBlockBeingAdded() { // Arrange - oldTree.AddText(0, "x"); - oldTree.AddText(1, "x"); - newTree.AddText(0, "x"); - newTree.AddText(1, "x"); - newTree.AddText(0, "x"); // Will be added - newTree.AddText(1, "x"); // Will be added + oldTree.AddText(10, "x"); + oldTree.AddText(11, "x"); + newTree.AddText(10, "x"); + newTree.AddText(11, "x"); + newTree.AddText(10, "x"); // Will be added + newTree.AddText(11, "x"); // Will be added // Act var result = GetSingleUpdatedComponent(); @@ -183,25 +187,28 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 2); - Assert.Equal(2, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 3); - Assert.Equal(3, entry.NewTreeIndex); + Assert.Equal(1, entry.ReferenceFrameIndex); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Text(frame, "x", 10), + frame => AssertFrame.Text(frame, "x", 11)); } [Fact] public void RecognizesLeadingLoopBlockItemsBeingAdded() { // Arrange - oldTree.AddText(2, "x"); - oldTree.AddText(2, "x"); // Note that the '0' and '1' items are not present on this iteration - newTree.AddText(2, "x"); - newTree.AddText(0, "x"); - newTree.AddText(1, "x"); - newTree.AddText(2, "x"); + oldTree.AddText(12, "x"); + oldTree.AddText(12, "x"); // Note that the '0' and '1' items are not present on this iteration + newTree.AddText(12, "x"); + newTree.AddText(10, "x"); + newTree.AddText(11, "x"); + newTree.AddText(12, "x"); // Act var result = GetSingleUpdatedComponent(); @@ -211,13 +218,16 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 1); - Assert.Equal(1, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 2); - Assert.Equal(2, entry.NewTreeIndex); + Assert.Equal(1, entry.ReferenceFrameIndex); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Text(frame, "x", 10), + frame => AssertFrame.Text(frame, "x", 11)); } [Fact] @@ -273,12 +283,12 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.UpdateText, 0); - Assert.Equal(0, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => { AssertEdit(entry, RenderTreeEditType.UpdateText, 1); - Assert.Equal(1, entry.NewTreeIndex); + Assert.Equal(1, entry.ReferenceFrameIndex); }); } @@ -303,7 +313,7 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 0); - Assert.Equal(0, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => AssertEdit(entry, RenderTreeEditType.RemoveFrame, 1)); } @@ -333,8 +343,8 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 0); - Assert.Equal(0, entry.NewTreeIndex); - Assert.IsType(updatedComponent1.CurrentState.Array[0].Component); + Assert.Equal(0, entry.ReferenceFrameIndex); + Assert.IsType(updatedComponent1.ReferenceFrames.Array[0].Component); }, entry => AssertEdit(entry, RenderTreeEditType.RemoveFrame, 1)); @@ -344,7 +354,7 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 0); - Assert.Equal(0, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }); } @@ -368,8 +378,10 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.SetAttribute, 0); - Assert.Equal(2, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Attribute(frame, "added", "added value")); } [Fact] @@ -417,8 +429,10 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.SetAttribute, 0); - Assert.Equal(2, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Attribute(frame, "will change", "did change value")); } [Fact] @@ -445,8 +459,10 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.SetAttribute, 0); - Assert.Equal(2, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Attribute(frame, "will change", addedHandler)); } [Fact] @@ -468,13 +484,15 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.SetAttribute, 0); - Assert.Equal(1, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => { AssertEdit(entry, RenderTreeEditType.RemoveAttribute, 0); Assert.Equal("oldname", entry.RemovedAttributeName); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Attribute(frame, "newname", "same value")); } [Fact] @@ -510,11 +528,13 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.UpdateText, 0); - Assert.Equal(4, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0), entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0), entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0)); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Text(frame, "grandchild new text", 13)); } [Fact] @@ -548,9 +568,11 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.UpdateText, 0); - Assert.Equal(1, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0)); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Text(frame, "Text that has changed", 11)); } [Fact] @@ -574,8 +596,10 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.UpdateText, 1); - Assert.Equal(1, entry.NewTreeIndex); + Assert.Equal(0, entry.ReferenceFrameIndex); }); + Assert.Collection(result.ReferenceFrames, + frame => AssertFrame.Text(frame, "text2modified", 11)); } [Fact] @@ -606,35 +630,30 @@ namespace Microsoft.AspNetCore.Blazor.Test entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 0); - Assert.Equal(2, entry.NewTreeIndex); - - var newTreeFrame = newTree.GetFrames().Array[entry.NewTreeIndex]; - Assert.Equal(0, newTreeFrame.ComponentId); - Assert.IsType(newTreeFrame.Component); + Assert.Equal(0, entry.ReferenceFrameIndex); }, entry => { AssertEdit(entry, RenderTreeEditType.PrependFrame, 1); - Assert.Equal(3, entry.NewTreeIndex); - - var newTreeFrame = newTree.GetFrames().Array[entry.NewTreeIndex]; - Assert.Equal(1, newTreeFrame.ComponentId); - Assert.IsType(newTreeFrame.Component); + Assert.Equal(1, entry.ReferenceFrameIndex); }, entry => AssertEdit(entry, RenderTreeEditType.StepOut, 0)); + Assert.Collection(firstComponentDiff.ReferenceFrames, + frame => AssertFrame.ComponentWithInstance(frame, 0, 12), + frame => AssertFrame.ComponentWithInstance(frame, 1, 13)); // Second in batch is the first child component var secondComponentDiff = renderBatch.UpdatedComponents.Array[1]; Assert.Equal(0, secondComponentDiff.ComponentId); Assert.Empty(secondComponentDiff.Edits); // Because FakeComponent produces no frames - Assert.Empty(secondComponentDiff.CurrentState); // Because FakeComponent produces no frames + Assert.Empty(secondComponentDiff.ReferenceFrames); // Because FakeComponent produces no frames // Third in batch is the second child component var thirdComponentDiff = renderBatch.UpdatedComponents.Array[2]; Assert.Equal(1, thirdComponentDiff.ComponentId); Assert.Collection(thirdComponentDiff.Edits, entry => AssertEdit(entry, RenderTreeEditType.PrependFrame, 0)); - Assert.Collection(thirdComponentDiff.CurrentState, + Assert.Collection(thirdComponentDiff.ReferenceFrames, frame => AssertFrame.Text(frame, $"Hello from {nameof(FakeComponent2)}")); } @@ -776,7 +795,7 @@ namespace Microsoft.AspNetCore.Blazor.Test var diffForChildComponent = batch.UpdatedComponents.Array[1]; // Assert - Assert.Collection(diffForChildComponent.CurrentState, + Assert.Collection(diffForChildComponent.ReferenceFrames, frame => AssertFrame.Text(frame, "Notifications: 1", 0)); } @@ -799,10 +818,10 @@ namespace Microsoft.AspNetCore.Blazor.Test // Act/Assert 0: Initial render var batch0 = GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree); var diffForChildComponent0 = batch0.UpdatedComponents.Array[1]; - var childComponentFrame = batch0.UpdatedComponents.Array[0].CurrentState.Array[0]; + var childComponentFrame = batch0.UpdatedComponents.Array[0].ReferenceFrames.Array[0]; var childComponentInstance = (HandlePropertiesChangedComponent)childComponentFrame.Component; Assert.Equal(1, childComponentInstance.NotificationsCount); - Assert.Collection(diffForChildComponent0.CurrentState, + Assert.Collection(diffForChildComponent0.ReferenceFrames, frame => AssertFrame.Text(frame, "Notifications: 1", 0)); // Act/Assert 1: If properties didn't change, we don't notify @@ -813,7 +832,7 @@ namespace Microsoft.AspNetCore.Blazor.Test var batch2 = GetRenderedBatch(newTree1, newTree2); var diffForChildComponent2 = batch2.UpdatedComponents.Array[1]; Assert.Equal(2, childComponentInstance.NotificationsCount); - Assert.Collection(diffForChildComponent2.CurrentState, + Assert.Collection(diffForChildComponent2.ReferenceFrames, frame => AssertFrame.Text(frame, "Notifications: 2", 0)); } diff --git a/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs b/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs index 540016c0b3..2429cc1411 100644 --- a/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs @@ -31,7 +31,14 @@ namespace Microsoft.AspNetCore.Blazor.Test renderer.RenderNewBatch(componentId); // Assert - Assert.Collection(renderer.Batches.Single().RenderTreesByComponentId[componentId], + var diff = renderer.Batches.Single().DiffsByComponentId[componentId].Single(); + Assert.Collection(diff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(diff.ReferenceFrames, frame => AssertFrame.Element(frame, "my element", 2), frame => AssertFrame.Text(frame, "some text")); } @@ -52,15 +59,23 @@ namespace Microsoft.AspNetCore.Blazor.Test // Act/Assert var componentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(componentId); - var componentFrame = renderer.Batches.Single().RenderTreesByComponentId[componentId] + var batch = renderer.Batches.Single(); + var componentFrame = batch.DiffsByComponentId[componentId].Single().ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component); var nestedComponentId = componentFrame.ComponentId; + var nestedComponentDiff = batch.DiffsByComponentId[nestedComponentId].Single(); // The nested component exists Assert.IsType(componentFrame.Component); // The nested component was rendered as part of the batch - Assert.Collection(renderer.Batches.Single().RenderTreesByComponentId[nestedComponentId], + Assert.Collection(nestedComponentDiff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(nestedComponentDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Nested component output")); } @@ -74,13 +89,27 @@ namespace Microsoft.AspNetCore.Blazor.Test // Act/Assert: first render renderer.RenderNewBatch(componentId); - Assert.Collection(renderer.Batches.Single().RenderTreesByComponentId[componentId], + var firstDiff = renderer.Batches.Single().DiffsByComponentId[componentId].Single(); + Assert.Collection(firstDiff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Initial message")); // Act/Assert: second render component.Message = "Modified message"; renderer.RenderNewBatch(componentId); - Assert.Collection(renderer.Batches[1].RenderTreesByComponentId[componentId], + var secondDiff = renderer.Batches.Skip(1).Single().DiffsByComponentId[componentId].Single(); + Assert.Collection(firstDiff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Modified message")); } @@ -96,21 +125,37 @@ namespace Microsoft.AspNetCore.Blazor.Test }); var parentComponentId = renderer.AssignComponentId(parentComponent); renderer.RenderNewBatch(parentComponentId); - var nestedComponentFrame = renderer.Batches.Single().RenderTreesByComponentId[parentComponentId] + var nestedComponentFrame = renderer.Batches.Single().DiffsByComponentId[parentComponentId] + .Single() + .ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component); var nestedComponent = (MessageComponent)nestedComponentFrame.Component; var nestedComponentId = nestedComponentFrame.ComponentId; - // Act/Assert: inital render + // Assert: inital render nestedComponent.Message = "Render 1"; renderer.RenderNewBatch(nestedComponentId); - Assert.Collection(renderer.Batches[1].RenderTreesByComponentId[nestedComponentId], + var firstDiff = renderer.Batches[1].DiffsByComponentId[nestedComponentId].Single(); + Assert.Collection(firstDiff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Render 1")); // Act/Assert: re-render nestedComponent.Message = "Render 2"; renderer.RenderNewBatch(nestedComponentId); - Assert.Collection(renderer.Batches[2].RenderTreesByComponentId[nestedComponentId], + var secondDiff = renderer.Batches[2].DiffsByComponentId[nestedComponentId].Single(); + Assert.Collection(firstDiff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(firstDiff.ReferenceFrames, frame => AssertFrame.Text(frame, "Render 2")); } @@ -129,7 +174,7 @@ namespace Microsoft.AspNetCore.Blazor.Test renderer.RenderNewBatch(componentId); var (eventHandlerFrameIndex, _) = FirstWithIndex( - renderer.Batches.Single().RenderTreesByComponentId[componentId], + renderer.Batches.Single().DiffsByComponentId[componentId].Single().ReferenceFrames, frame => frame.AttributeValue != null); // Assert: Event not yet fired @@ -157,7 +202,9 @@ namespace Microsoft.AspNetCore.Blazor.Test renderer.RenderNewBatch(parentComponentId); // Arrange: Render nested component - var nestedComponentFrame = renderer.Batches.Single().RenderTreesByComponentId[parentComponentId] + var nestedComponentFrame = renderer.Batches.Single().DiffsByComponentId[parentComponentId] + .Single() + .ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component); var nestedComponent = (EventComponent)nestedComponentFrame.Component; nestedComponent.Handler = args => { receivedArgs = args; }; @@ -166,7 +213,7 @@ namespace Microsoft.AspNetCore.Blazor.Test // Find nested component's event handler ndoe var (eventHandlerFrameIndex, _) = FirstWithIndex( - renderer.Batches[1].RenderTreesByComponentId[nestedComponentId], + renderer.Batches[1].DiffsByComponentId[nestedComponentId].Single().ReferenceFrames, frame => frame.AttributeValue != null); // Assert: Event not yet fired @@ -217,12 +264,12 @@ namespace Microsoft.AspNetCore.Blazor.Test // Act/Assert: Render component in renderer1 renderer1.RenderNewBatch(renderer1ComponentId); - Assert.True(renderer1.Batches.Single().RenderTreesByComponentId.ContainsKey(renderer1ComponentId)); + Assert.True(renderer1.Batches.Single().DiffsByComponentId.ContainsKey(renderer1ComponentId)); Assert.Empty(renderer2.Batches); // Act/Assert: Render same component in renderer2 renderer2.RenderNewBatch(renderer2ComponentId); - Assert.True(renderer2.Batches.Single().RenderTreesByComponentId.ContainsKey(renderer2ComponentId)); + Assert.True(renderer2.Batches.Single().DiffsByComponentId.ContainsKey(renderer2ComponentId)); } [Fact] @@ -313,18 +360,29 @@ namespace Microsoft.AspNetCore.Blazor.Test var rootComponentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(rootComponentId); - var nestedComponentInstance = (MessageComponent)renderer.Batches.Single().RenderTreesByComponentId[rootComponentId] - .Single(frame => frame.FrameType == RenderTreeFrameType.Component) - .Component; + var nestedComponentFrame = renderer.Batches.Single() + .DiffsByComponentId[rootComponentId] + .Single() + .ReferenceFrames + .Single(frame => frame.FrameType == RenderTreeFrameType.Component); + var nestedComponentInstance = (MessageComponent)nestedComponentFrame.Component; // Act: Second render message = "Modified message"; renderer.RenderNewBatch(rootComponentId); // Assert - Assert.Collection(renderer.Batches[1].RenderTreesByComponentId[rootComponentId], - frame => AssertFrame.Text(frame, "Modified message"), - frame => Assert.Same(nestedComponentInstance, frame.Component)); + var batch = renderer.Batches[1]; + var diff = batch.DiffsByComponentId[rootComponentId].Single(); + Assert.Collection(diff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(diff.ReferenceFrames, + frame => AssertFrame.Text(frame, "Modified message")); + Assert.False(batch.DiffsByComponentId.ContainsKey(nestedComponentFrame.ComponentId)); } [Fact] @@ -346,28 +404,25 @@ namespace Microsoft.AspNetCore.Blazor.Test var rootComponentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(rootComponentId); - var originalComponentInstance = (FakeComponent)renderer.Batches.Single().RenderTreesByComponentId[rootComponentId] - .Single(frame => frame.FrameType == RenderTreeFrameType.Component) - .Component; + var originalComponentFrame = renderer.Batches.Single().DiffsByComponentId[rootComponentId] + .Single() + .ReferenceFrames + .Single(frame => frame.FrameType == RenderTreeFrameType.Component); + var childComponentInstance = (FakeComponent)originalComponentFrame.Component; // Assert 1: properties were assigned - Assert.Equal(123, originalComponentInstance.IntProperty); - Assert.Equal("String that will change", originalComponentInstance.StringProperty); - Assert.Same(objectThatWillNotChange, originalComponentInstance.ObjectProperty); + Assert.Equal(123, childComponentInstance.IntProperty); + Assert.Equal("String that will change", childComponentInstance.StringProperty); + Assert.Same(objectThatWillNotChange, childComponentInstance.ObjectProperty); // Act: Second render firstRender = false; renderer.RenderNewBatch(rootComponentId); - var updatedComponentInstance = (FakeComponent)renderer.Batches[1].RenderTreesByComponentId[rootComponentId] - .Single(frame => frame.FrameType == RenderTreeFrameType.Component) - .Component; - // Assert - Assert.Same(originalComponentInstance, updatedComponentInstance); - Assert.Equal(256, updatedComponentInstance.IntProperty); - Assert.Equal("String that did change", updatedComponentInstance.StringProperty); - Assert.Same(objectThatWillNotChange, updatedComponentInstance.ObjectProperty); + Assert.Equal(256, childComponentInstance.IntProperty); + Assert.Equal("String that did change", childComponentInstance.StringProperty); + Assert.Same(objectThatWillNotChange, childComponentInstance.ObjectProperty); } [Fact] @@ -386,19 +441,25 @@ namespace Microsoft.AspNetCore.Blazor.Test var rootComponentId = renderer.AssignComponentId(component); renderer.RenderNewBatch(rootComponentId); - var childComponentId = renderer.Batches.Single().RenderTreesByComponentId[rootComponentId] + var childComponentId = renderer.Batches.Single().DiffsByComponentId[rootComponentId] + .Single() + .ReferenceFrames .Single(frame => frame.FrameType == RenderTreeFrameType.Component) .ComponentId; // Act: Second render firstRender = false; renderer.RenderNewBatch(rootComponentId); - - var updatedComponentFrame = renderer.Batches[1].RenderTreesByComponentId[rootComponentId] - .Single(frame => frame.FrameType == RenderTreeFrameType.Component); + var diff = renderer.Batches[1].DiffsByComponentId[childComponentId].Single(); // Assert - Assert.Collection(renderer.Batches[1].RenderTreesByComponentId[updatedComponentFrame.ComponentId], + Assert.Collection(diff.Edits, + edit => + { + Assert.Equal(RenderTreeEditType.UpdateText, edit.Type); + Assert.Equal(0, edit.ReferenceFrameIndex); + }); + Assert.Collection(diff.ReferenceFrames, frame => AssertFrame.Text(frame, "second")); } @@ -427,7 +488,9 @@ namespace Microsoft.AspNetCore.Blazor.Test // Act/Assert 1: First render, capturing child component IDs renderer.RenderNewBatch(rootComponentId); - var childComponentIds = renderer.Batches.Single().RenderTreesByComponentId[rootComponentId] + var childComponentIds = renderer.Batches.Single().DiffsByComponentId[rootComponentId] + .Single() + .ReferenceFrames .Where(frame => frame.FrameType == RenderTreeFrameType.Component) .Select(frame => frame.ComponentId) .ToList(); @@ -476,8 +539,7 @@ namespace Microsoft.AspNetCore.Blazor.Test for (var i = 0; i < renderBatch.UpdatedComponents.Count; i++) { ref var renderTreeDiff = ref renderBatch.UpdatedComponents.Array[i]; - capturedBatch.RenderTreesByComponentId[renderTreeDiff.ComponentId] = - renderTreeDiff.CurrentState.ToArray(); + capturedBatch.AddDiff(renderTreeDiff); } capturedBatch.DisposedComponentIDs = renderBatch.DisposedComponentIDs.ToList(); @@ -486,10 +548,20 @@ namespace Microsoft.AspNetCore.Blazor.Test private class CapturedBatch { - public IDictionary RenderTreesByComponentId { get; } - = new Dictionary(); + public IDictionary> DiffsByComponentId { get; } + = new Dictionary>(); public IList DisposedComponentIDs { get; set; } + + internal void AddDiff(RenderTreeDiff diff) + { + var componentId = diff.ComponentId; + if (!DiffsByComponentId.ContainsKey(componentId)) + { + DiffsByComponentId.Add(componentId, new List()); + } + DiffsByComponentId[componentId].Add(diff); + } } private class TestComponent : IComponent diff --git a/test/shared/AssertFrame.cs b/test/shared/AssertFrame.cs index ad2bd6b1cf..faa9b1d497 100644 --- a/test/shared/AssertFrame.cs +++ b/test/shared/AssertFrame.cs @@ -60,6 +60,13 @@ namespace Microsoft.AspNetCore.Blazor.Test.Shared AssertFrame.Sequence(frame, sequence); } + public static void ComponentWithInstance(RenderTreeFrame frame, int componentId, int? sequence = null) where T : IComponent + { + AssertFrame.Component(frame, sequence); + Assert.IsType(frame.Component); + Assert.Equal(componentId, frame.ComponentId); + } + public static void Whitespace(RenderTreeFrame frame, int? sequence = null) { Assert.Equal(RenderTreeFrameType.Text, frame.FrameType); diff --git a/test/testapps/BasicTestApp/KeyPressEventComponent.cshtml b/test/testapps/BasicTestApp/KeyPressEventComponent.cshtml index f929f23651..82a1e0fbfd 100644 --- a/test/testapps/BasicTestApp/KeyPressEventComponent.cshtml +++ b/test/testapps/BasicTestApp/KeyPressEventComponent.cshtml @@ -1,6 +1,6 @@ @using System.Collections.Generic @using Microsoft.AspNetCore.Blazor.RenderTree -Type here: +Type here:
    @foreach (var key in keysPressed) { @@ -11,7 +11,22 @@ Type here: @functions { List keysPressed = new List(); - void OnKeyPressed(UIEventArgs eventArgs) + // TODO: Fix this + // Currently, you can only trigger an event handler whose value changed in the most + // recent render cycle. That's because we reference the event handlers by their index + // into the current diff's ReferenceFrames array. We need some better mechanism of + // locating the delegates that is independent of whether the corresponding attribute + // changed in the last diff, and not assuming the attribute in the original render + // tree is still at the same index. + // Once that's fixed, remove the 'CreateNewDelegateInstance' method entirely and + // the 'irrelevantObject' arg from below, and simplify to onkeypress=@OnKeyPressed + UIEventHandler CreateNewDelegateInstance() + { + var irrelevantObject = new object(); + return args => OnKeyPressed(args, irrelevantObject); + } + + void OnKeyPressed(UIEventArgs eventArgs, object irrelevantObject) { keysPressed.Add(((UIKeyboardEventArgs)eventArgs).Key); }