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 2a95198c5b..10f382b4f9 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/BrowserRenderer.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/BrowserRenderer.ts @@ -84,7 +84,11 @@ export class BrowserRenderer { const element = getLogicalChild(parent, childIndexAtCurrentDepth + siblingIndex); if (element instanceof HTMLElement) { const attributeName = renderTreeEdit.removedAttributeName(edit)!; - element.removeAttribute(attributeName); + // First try to remove any special property we use for this attribute + if (!this.tryApplySpecialProperty(element, attributeName, null)) { + // If that's not applicable, it's a regular DOM attribute so remove that + element.removeAttribute(attributeName); + } } else { throw new Error(`Cannot remove attribute from non-element child`); } @@ -205,53 +209,76 @@ export class BrowserRenderer { return; } - if (attributeName === 'value') { - if (this.tryApplyValueProperty(toDomElement, renderTreeFrame.attributeValue(attributeFrame))) { - return; // If this DOM element type has special 'value' handling, don't also write it as an attribute - } + // First see if we have special handling for this attribute + if (!this.tryApplySpecialProperty(toDomElement, attributeName, attributeFrame)) { + // If not, treat it as a regular string-valued attribute + toDomElement.setAttribute( + attributeName, + renderTreeFrame.attributeValue(attributeFrame)! + ); } - - // Treat as a regular string-valued attribute - toDomElement.setAttribute( - attributeName, - renderTreeFrame.attributeValue(attributeFrame)! - ); } - private tryApplyValueProperty(element: Element, value: string | null) { + private tryApplySpecialProperty(element: Element, attributeName: string, attributeFrame: RenderTreeFramePointer | null) { + switch (attributeName) { + case 'value': + return this.tryApplyValueProperty(element, attributeFrame); + case 'checked': + return this.tryApplyCheckedProperty(element, attributeFrame); + default: + return false; + } + } + + private tryApplyValueProperty(element: Element, attributeFrame: RenderTreeFramePointer | null) { // Certain elements have built-in behaviour for their 'value' property switch (element.tagName) { case 'INPUT': case 'SELECT': - case 'TEXTAREA': - if (isCheckbox(element)) { - (element as HTMLInputElement).checked = value === ''; - } else { - (element as any).value = value; + case 'TEXTAREA': { + const value = attributeFrame ? renderTreeFrame.attributeValue(attributeFrame) : null; + (element as any).value = value; - if (element.tagName === 'SELECT') { - // is special, in that anything we write to .value will be lost if there + // isn't yet a matching