diff --git a/src/Components/Web/src/Forms/InputBase.cs b/src/Components/Web/src/Forms/InputBase.cs index cd8d7ee8ac50..9b2a62e39021 100644 --- a/src/Components/Web/src/Forms/InputBase.cs +++ b/src/Components/Web/src/Forms/InputBase.cs @@ -207,13 +207,7 @@ protected string NameAttributeValue if (_shouldGenerateFieldNames) { - if (_formattedValueExpression is null && ValueExpression is not null) - { - _formattedValueExpression = FieldPrefix != null ? FieldPrefix.GetFieldName(ValueExpression) : - ExpressionFormatter.FormatLambda(ValueExpression); - } - - return _formattedValueExpression ?? string.Empty; + return GetFieldName(); } return string.Empty; @@ -225,7 +219,7 @@ protected string NameAttributeValue /// /// /// If an explicit "id" is provided via , that value takes precedence. - /// Otherwise, the id is derived from with invalid characters sanitized. + /// Otherwise, the id is a sanitized version of in SSR mode; generated independently in interactive mode. /// protected string IdAttributeValue { @@ -236,10 +230,20 @@ protected string IdAttributeValue return Convert.ToString(idAttributeValue, CultureInfo.InvariantCulture) ?? string.Empty; } - return FieldIdGenerator.SanitizeHtmlId(NameAttributeValue); + var fieldName = NameAttributeValue; + if (string.IsNullOrEmpty(fieldName)) + { + fieldName = GetFieldName(); + } + + return FieldIdGenerator.SanitizeHtmlId(fieldName); } } + private string GetFieldName() + => _formattedValueExpression ??= FieldPrefix?.GetFieldName(ValueExpression!) + ?? ExpressionFormatter.FormatLambda(ValueExpression!); + /// public override Task SetParametersAsync(ParameterView parameters) { diff --git a/src/Components/Web/test/Forms/InputTextTest.cs b/src/Components/Web/test/Forms/InputTextTest.cs index f5850029183f..695ab5da3180 100644 --- a/src/Components/Web/test/Forms/InputTextTest.cs +++ b/src/Components/Web/test/Forms/InputTextTest.cs @@ -63,6 +63,26 @@ public async Task ExplicitIdOverridesGenerated() Assert.Equal("custom-id", idAttribute.AttributeValue); } + [Fact] + public async Task RendersIdAttribute_WhenShouldUseFieldIdentifiersIsFalse_InteractiveMode() + { + // simulate interactive mode where ShouldUseFieldIdentifiers is false + var model = new TestModel(); + var editContext = new EditContext(model) { ShouldUseFieldIdentifiers = false }; + var rootComponent = new TestInputHostComponent + { + EditContext = editContext, + ValueExpression = () => model.StringProperty, + }; + + var componentId = await RenderAndGetInputTextComponentIdAsync(rootComponent); + var frames = _testRenderer.GetCurrentRenderTreeFrames(componentId); + + // id should still be generated for Label/Input association to work in interactive mode + var idAttribute = frames.Array.Single(f => f.FrameType == RenderTreeFrameType.Attribute && f.AttributeName == "id"); + Assert.Equal("model_StringProperty", idAttribute.AttributeValue); + } + private async Task RenderAndGetInputTextComponentIdAsync(TestInputHostComponent hostComponent) { var hostComponentId = _testRenderer.AssignRootComponentId(hostComponent);