diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffBuilder.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffBuilder.cs index 83c25bdd7e..864e21e05d 100644 --- a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffBuilder.cs +++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeDiffBuilder.cs @@ -179,8 +179,8 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree // These two attributes have the same sequence and name. Keep merging. AppendDiffEntriesForAttributeFrame(ref diffContext, oldStartIndex, newStartIndex); - oldStartIndex = NextSiblingIndex(oldTree[oldStartIndex], oldStartIndex); - newStartIndex = NextSiblingIndex(newTree[newStartIndex], newStartIndex); + oldStartIndex++; + newStartIndex++; hasMoreOld = oldEndIndexExcl > oldStartIndex; hasMoreNew = newEndIndexExcl > newStartIndex; } diff --git a/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs b/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs index d505bded92..ea8f842085 100644 --- a/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Test/RendererTest.cs @@ -919,6 +919,36 @@ namespace Microsoft.AspNetCore.Blazor.Test }); } + [Fact] + public void CanCombineBindAndConditionalAttribute() + { + // This test represents https://github.com/aspnet/Blazor/issues/624 + + // Arrange: Rendered with textbox enabled + var renderer = new TestRenderer(); + var component = new BindPlusConditionalAttributeComponent(); + var componentId = renderer.AssignComponentId(component); + component.TriggerRender(); + var checkboxChangeEventHandlerId = renderer.Batches.Single() + .ReferenceFrames + .First(frame => frame.FrameType == RenderTreeFrameType.Attribute && frame.AttributeEventHandlerId != 0) + .AttributeEventHandlerId; + + // Act: Toggle the checkbox + var eventArgs = new UIChangeEventArgs { Value = true }; + renderer.DispatchEvent(componentId, checkboxChangeEventHandlerId, eventArgs); + var latestBatch = renderer.Batches.Last(); + var latestDiff = latestBatch.DiffsInOrder.Single(); + var referenceFrames = latestBatch.ReferenceFrames; + + // Assert: Textbox's "disabled" attribute was removed + Assert.Equal(2, renderer.Batches.Count); + Assert.Equal(componentId, latestDiff.ComponentId); + Assert.Contains(latestDiff.Edits, edit => + edit.SiblingIndex == 1 + && edit.RemovedAttributeName == "disabled"); + } + private class NoOpRenderer : Renderer { public NoOpRenderer() : base(new TestServiceProvider()) @@ -1113,5 +1143,31 @@ namespace Microsoft.AspNetCore.Blazor.Test } } } + + private class BindPlusConditionalAttributeComponent : AutoRenderComponent, IHandleEvent + { + public bool CheckboxEnabled; + public string SomeStringProperty; + + public void HandleEvent(UIEventHandler handler, UIEventArgs args) + { + handler(args); + TriggerRender(); + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + builder.OpenElement(0, "input"); + builder.AddAttribute(1, "type", "checkbox"); + builder.AddAttribute(2, "value", BindMethods.GetValue(CheckboxEnabled)); + builder.AddAttribute(3, "onchange", BindMethods.SetValueHandler(__value => CheckboxEnabled = __value, CheckboxEnabled)); + builder.CloseElement(); + builder.OpenElement(4, "input"); + builder.AddAttribute(5, "value", BindMethods.GetValue(SomeStringProperty)); + builder.AddAttribute(6, "onchange", BindMethods.SetValueHandler(__value => SomeStringProperty = __value, SomeStringProperty)); + builder.AddAttribute(7, "disabled", !CheckboxEnabled); + builder.CloseElement(); + } + } } }