Skip to content

Commit ee043f6

Browse files
author
Ryan Nowak
committed
Clean up of form components
Part of #10713 This change addresses the cleanup of the form components by removing the Id and Class parameters as explicit parameters. These components will now propagate the id and class attributes to the created HTML element without the need for explicit parameters. Note that we preserve the combining behaviour of class with FieldClass.
1 parent eb4e641 commit ee043f6

File tree

9 files changed

+47
-79
lines changed

9 files changed

+47
-79
lines changed

src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,13 @@ public abstract partial class InputBase<T> : Microsoft.AspNetCore.Components.Com
153153
protected InputBase() { }
154154
[Parameter(CaptureUnmatchedValues = true)]
155155
public System.Collections.Generic.IReadOnlyDictionary<string, object> AdditionalAttributes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }}
156-
[Microsoft.AspNetCore.Components.ParameterAttribute]
157-
public string Class { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }}
158156
protected string CssClass { get { throw null; } }
159157
protected T CurrentValue { get { throw null; } set { } }
160158
protected string CurrentValueAsString { get { throw null; } set { } }
161159
protected Microsoft.AspNetCore.Components.Forms.EditContext EditContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
162160
protected string FieldClass { get { throw null; } }
163161
protected Microsoft.AspNetCore.Components.Forms.FieldIdentifier FieldIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
164162
[Microsoft.AspNetCore.Components.ParameterAttribute]
165-
public string Id { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }}
166-
[Microsoft.AspNetCore.Components.ParameterAttribute]
167163
public T Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }}
168164
[Microsoft.AspNetCore.Components.ParameterAttribute]
169165
public Microsoft.AspNetCore.Components.EventCallback<T> ValueChanged { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }}

src/Components/Components/src/Forms/InputComponents/InputBase.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,6 @@ public abstract class InputBase<T> : ComponentBase
2626
/// </summary>
2727
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; private set; }
2828

29-
/// <summary>
30-
/// Gets a value for the component's 'id' attribute.
31-
/// </summary>
32-
[Parameter] public string Id { get; private set; }
33-
34-
/// <summary>
35-
/// Gets a value for the component's 'class' attribute.
36-
/// </summary>
37-
[Parameter] public string Class { get; private set; }
38-
3929
/// <summary>
4030
/// Gets or sets the value of the input. This should be used with two-way binding.
4131
/// </summary>
@@ -157,14 +147,25 @@ protected string FieldClass
157147
=> EditContext.FieldClass(FieldIdentifier);
158148

159149
/// <summary>
160-
/// Gets a CSS class string that combines the <see cref="Class"/> and <see cref="FieldClass"/>
150+
/// Gets a CSS class string that combines the <c>class</c> attribute and <see cref="FieldClass"/>
161151
/// properties. Derived components should typically use this value for the primary HTML element's
162152
/// 'class' attribute.
163153
/// </summary>
164154
protected string CssClass
165-
=> string.IsNullOrEmpty(Class)
166-
? FieldClass // Never null or empty
167-
: $"{Class} {FieldClass}";
155+
{
156+
get
157+
{
158+
if (AdditionalAttributes != null &&
159+
AdditionalAttributes.TryGetValue("class", out var @class) &&
160+
!string.IsNullOrEmpty(Convert.ToString(@class)))
161+
{
162+
return $"{@class} {FieldClass}";
163+
}
164+
165+
return FieldClass; // Never null or empty
166+
}
167+
}
168+
168169

169170
/// <inheritdoc />
170171
public override Task SetParametersAsync(ParameterCollection parameters)

src/Components/Components/src/Forms/InputComponents/InputCheckbox.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,9 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
2626
builder.OpenElement(0, "input");
2727
builder.AddMultipleAttributes(1, AdditionalAttributes);
2828
builder.AddAttribute(2, "type", "checkbox");
29-
builder.AddAttribute(3, "id", Id);
30-
builder.AddAttribute(4, "class", CssClass);
31-
builder.AddAttribute(5, "checked", BindMethods.GetValue(CurrentValue));
32-
builder.AddAttribute(6, "onchange", EventCallback.Factory.CreateBinder<bool>(this, __value => CurrentValue = __value, CurrentValue));
29+
builder.AddAttribute(3, "class", CssClass);
30+
builder.AddAttribute(4, "checked", BindMethods.GetValue(CurrentValue));
31+
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<bool>(this, __value => CurrentValue = __value, CurrentValue));
3332
builder.CloseElement();
3433
}
3534

src/Components/Components/src/Forms/InputComponents/InputDate.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
2525
builder.OpenElement(0, "input");
2626
builder.AddMultipleAttributes(1, AdditionalAttributes);
2727
builder.AddAttribute(2, "type", "date");
28-
builder.AddAttribute(3, "id", Id);
29-
builder.AddAttribute(4, "class", CssClass);
30-
builder.AddAttribute(5, "value", BindMethods.GetValue(CurrentValueAsString));
31-
builder.AddAttribute(6, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
28+
builder.AddAttribute(3, "class", CssClass);
29+
builder.AddAttribute(4, "value", BindMethods.GetValue(CurrentValueAsString));
30+
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
3231
builder.CloseElement();
3332
}
3433

src/Components/Components/src/Forms/InputComponents/InputNumber.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,12 @@ static InputNumber()
6262
protected override void BuildRenderTree(RenderTreeBuilder builder)
6363
{
6464
builder.OpenElement(0, "input");
65-
builder.AddMultipleAttributes(1, AdditionalAttributes);
66-
builder.AddAttribute(2, "type", "number");
67-
builder.AddAttribute(3, "step", _stepAttributeValue);
68-
builder.AddAttribute(4, "id", Id);
69-
builder.AddAttribute(5, "class", CssClass);
70-
builder.AddAttribute(6, "value", BindMethods.GetValue(CurrentValueAsString));
71-
builder.AddAttribute(7, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
65+
builder.AddAttribute(1, "step", _stepAttributeValue); // Before the splat so the user can override
66+
builder.AddMultipleAttributes(2, AdditionalAttributes);
67+
builder.AddAttribute(3, "type", "number");
68+
builder.AddAttribute(4, "class", CssClass);
69+
builder.AddAttribute(5, "value", BindMethods.GetValue(CurrentValueAsString));
70+
builder.AddAttribute(6, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
7271
builder.CloseElement();
7372
}
7473

src/Components/Components/src/Forms/InputComponents/InputSelect.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
2121
{
2222
builder.OpenElement(0, "select");
2323
builder.AddMultipleAttributes(1, AdditionalAttributes);
24-
builder.AddAttribute(2, "id", Id);
25-
builder.AddAttribute(3, "class", CssClass);
26-
builder.AddAttribute(4, "value", BindMethods.GetValue(CurrentValueAsString));
27-
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
28-
builder.AddContent(6, ChildContent);
24+
builder.AddAttribute(2, "class", CssClass);
25+
builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValueAsString));
26+
builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
27+
builder.AddContent(5, ChildContent);
2928
builder.CloseElement();
3029
}
3130

src/Components/Components/src/Forms/InputComponents/InputText.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
namespace Microsoft.AspNetCore.Components.Forms
77
{
8-
// TODO: Support maxlength etc.
9-
108
/* This is almost equivalent to a .razor file containing:
119
*
1210
* @inherits InputBase<string>
@@ -26,10 +24,9 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
2624
{
2725
builder.OpenElement(0, "input");
2826
builder.AddMultipleAttributes(1, AdditionalAttributes);
29-
builder.AddAttribute(2, "id", Id);
30-
builder.AddAttribute(3, "class", CssClass);
31-
builder.AddAttribute(4, "value", BindMethods.GetValue(CurrentValue));
32-
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
27+
builder.AddAttribute(2, "class", CssClass);
28+
builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValue));
29+
builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
3330
builder.CloseElement();
3431
}
3532

src/Components/Components/src/Forms/InputComponents/InputTextArea.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55

66
namespace Microsoft.AspNetCore.Components.Forms
77
{
8-
// TODO: Support rows/cols/etc
9-
108
/* This is almost equivalent to a .razor file containing:
119
*
1210
* @inherits InputBase<string>
@@ -26,10 +24,9 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
2624
{
2725
builder.OpenElement(0, "textarea");
2826
builder.AddMultipleAttributes(1, AdditionalAttributes);
29-
builder.AddAttribute(2, "id", Id);
30-
builder.AddAttribute(3, "class", CssClass);
31-
builder.AddAttribute(4, "value", BindMethods.GetValue(CurrentValue));
32-
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
27+
builder.AddAttribute(2, "class", CssClass);
28+
builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValue));
29+
builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
3330
builder.CloseElement();
3431
}
3532

src/Components/Components/test/Forms/InputBaseTest.cs

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public async Task ThrowsOnFirstRenderIfNoEditContextIsSupplied()
2121
var inputComponent = new TestInputComponent<string>();
2222
var testRenderer = new TestRenderer();
2323
var componentId = testRenderer.AssignRootComponentId(inputComponent);
24-
24+
2525
// Act/Assert
2626
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
2727
() => testRenderer.RenderRootComponentAsync(componentId));
@@ -73,25 +73,6 @@ public async Task GetsCurrentValueFromValueParameter()
7373
Assert.Equal("some value", inputComponent.CurrentValue);
7474
}
7575

76-
[Fact]
77-
public async Task ExposesIdToSubclass()
78-
{
79-
// Arrange
80-
var model = new TestModel();
81-
var rootComponent = new TestInputHostComponent<string, TestInputComponent<string>>
82-
{
83-
Id = "test-id",
84-
EditContext = new EditContext(model),
85-
ValueExpression = () => model.StringProperty
86-
};
87-
88-
// Act
89-
var inputComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
90-
91-
// Assert
92-
Assert.Same(rootComponent.Id, inputComponent.Id);
93-
}
94-
9576
[Fact]
9677
public async Task ExposesEditContextToSubclass()
9778
{
@@ -264,7 +245,10 @@ public async Task CssClassCombinesClassWithFieldClass()
264245
var model = new TestModel();
265246
var rootComponent = new TestInputHostComponent<string, TestInputComponent<string>>
266247
{
267-
Class = "my-class other-class",
248+
AdditionalAttributes = new Dictionary<string, object>()
249+
{
250+
{ "class", "my-class other-class" },
251+
},
268252
EditContext = new EditContext(model),
269253
ValueExpression = () => model.StringProperty
270254
};
@@ -370,7 +354,7 @@ private static TComponent FindComponent<TComponent>(CapturedBatch batch)
370354
.OfType<TComponent>()
371355
.Single();
372356

373-
private static async Task<TComponent> RenderAndGetTestInputComponentAsync<TValue, TComponent>(TestInputHostComponent<TValue, TComponent> hostComponent) where TComponent: TestInputComponent<TValue>
357+
private static async Task<TComponent> RenderAndGetTestInputComponentAsync<TValue, TComponent>(TestInputHostComponent<TValue, TComponent> hostComponent) where TComponent : TestInputComponent<TValue>
374358
{
375359
var testRenderer = new TestRenderer();
376360
var componentId = testRenderer.AssignRootComponentId(hostComponent);
@@ -401,7 +385,7 @@ class TestInputComponent<T> : InputBase<T>
401385
set { base.CurrentValueAsString = value; }
402386
}
403387

404-
public new string Id => base.Id;
388+
public new IReadOnlyDictionary<string, object> AdditionalAttributes => base.AdditionalAttributes;
405389

406390
public new string CssClass => base.CssClass;
407391

@@ -437,11 +421,9 @@ protected override bool TryParseValueFromString(string value, out DateTime resul
437421
}
438422
}
439423

440-
class TestInputHostComponent<TValue, TComponent> : AutoRenderComponent where TComponent: TestInputComponent<TValue>
424+
class TestInputHostComponent<TValue, TComponent> : AutoRenderComponent where TComponent : TestInputComponent<TValue>
441425
{
442-
public string Id { get; set; }
443-
444-
public string Class { get; set; }
426+
public Dictionary<string, object> AdditionalAttributes { get; set; }
445427

446428
public EditContext EditContext { get; set; }
447429

@@ -462,8 +444,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
462444
childBuilder.AddAttribute(1, "ValueChanged",
463445
EventCallback.Factory.Create(this, ValueChanged));
464446
childBuilder.AddAttribute(2, "ValueExpression", ValueExpression);
465-
childBuilder.AddAttribute(3, nameof(Id), Id);
466-
childBuilder.AddAttribute(4, nameof(Class), Class);
447+
childBuilder.AddMultipleAttributes(3, AdditionalAttributes);
467448
childBuilder.CloseComponent();
468449
}));
469450
builder.CloseComponent();

0 commit comments

Comments
 (0)