Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InputRadio component with form support #23415

Merged
merged 15 commits into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected InputBase() { }
protected TValue CurrentValue { get { throw null; } set { } }
protected string? CurrentValueAsString { get { throw null; } set { } }
protected Microsoft.AspNetCore.Components.Forms.EditContext EditContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected Microsoft.AspNetCore.Components.Forms.FieldIdentifier FieldIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected internal Microsoft.AspNetCore.Components.Forms.FieldIdentifier FieldIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
[System.Diagnostics.CodeAnalysis.MaybeNullAttribute]
[System.Diagnostics.CodeAnalysis.AllowNullAttribute]
Expand All @@ -71,7 +71,7 @@ protected InputBase() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Linq.Expressions.Expression<System.Func<TValue>>? ValueExpression { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected virtual void Dispose(bool disposing) { }
protected virtual string? FormatValueAsString(TValue value) { throw null; }
protected virtual string? FormatValueAsString([System.Diagnostics.CodeAnalysis.AllowNullAttribute] TValue value) { throw null; }
MackinnonBuck marked this conversation as resolved.
Show resolved Hide resolved
public override System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) { throw null; }
void System.IDisposable.Dispose() { }
protected abstract bool TryParseValueFromString(string? value, [System.Diagnostics.CodeAnalysis.MaybeNullAttribute] out TValue result, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] out string? validationErrorMessage);
Expand All @@ -88,7 +88,7 @@ public InputDate() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public string ParsingErrorMessage { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override string FormatValueAsString(TValue value) { throw null; }
protected override string FormatValueAsString([System.Diagnostics.CodeAnalysis.AllowNullAttribute] TValue value) { throw null; }
protected override bool TryParseValueFromString(string? value, [System.Diagnostics.CodeAnalysis.MaybeNullAttribute] out TValue result, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] out string? validationErrorMessage) { throw null; }
}
public partial class InputNumber<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
Expand All @@ -97,7 +97,29 @@ public InputNumber() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public string ParsingErrorMessage { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override string? FormatValueAsString(TValue value) { throw null; }
protected override string? FormatValueAsString([System.Diagnostics.CodeAnalysis.AllowNullAttribute] TValue value) { throw null; }
protected override bool TryParseValueFromString(string? value, [System.Diagnostics.CodeAnalysis.MaybeNullAttribute] out TValue result, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] out string? validationErrorMessage) { throw null; }
}
public partial class InputRadioGroup : Microsoft.AspNetCore.Components.ComponentBase
{
public InputRadioGroup() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment? ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public string? Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override void OnParametersSet() { }
}
public partial class InputRadio<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
{
public InputRadio() { }
protected string? GroupName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
[System.Diagnostics.CodeAnalysis.MaybeNullAttribute]
[System.Diagnostics.CodeAnalysis.AllowNullAttribute]
public TValue SelectedValue { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override void OnParametersSet() { }
protected override bool TryParseValueFromString(string? value, [System.Diagnostics.CodeAnalysis.MaybeNullAttribute] out TValue result, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] out string? validationErrorMessage) { throw null; }
}
public partial class InputSelect<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected InputBase() { }
protected TValue CurrentValue { get { throw null; } set { } }
protected string? CurrentValueAsString { get { throw null; } set { } }
protected Microsoft.AspNetCore.Components.Forms.EditContext EditContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected Microsoft.AspNetCore.Components.Forms.FieldIdentifier FieldIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected internal Microsoft.AspNetCore.Components.Forms.FieldIdentifier FieldIdentifier { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public TValue Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
Expand Down Expand Up @@ -96,6 +96,26 @@ protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Renderin
protected override string? FormatValueAsString(TValue value) { throw null; }
protected override bool TryParseValueFromString(string? value, out TValue result, out string? validationErrorMessage) { throw null; }
}
public partial class InputRadioGroup : Microsoft.AspNetCore.Components.ComponentBase
{
public InputRadioGroup() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment? ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public string? Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override void OnParametersSet() { }
}
public partial class InputRadio<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
{
public InputRadio() { }
protected string? GroupName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public TValue SelectedValue { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
protected override void OnParametersSet() { }
protected override bool TryParseValueFromString(string? value, out TValue result, out string? validationErrorMessage) { throw null; }
}
public partial class InputSelect<TValue> : Microsoft.AspNetCore.Components.Forms.InputBase<TValue>
{
public InputSelect() { }
Expand Down
4 changes: 2 additions & 2 deletions src/Components/Web/src/Forms/InputBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public abstract class InputBase<TValue> : ComponentBase, IDisposable
/// <summary>
/// Gets the <see cref="FieldIdentifier"/> for the bound value.
/// </summary>
protected FieldIdentifier FieldIdentifier { get; set; }
protected internal FieldIdentifier FieldIdentifier { get; set; }

/// <summary>
/// Gets or sets the current value of the input.
Expand Down Expand Up @@ -142,7 +142,7 @@ protected InputBase()
/// </summary>
/// <param name="value">The value to format.</param>
/// <returns>A string representation of the value.</returns>
protected virtual string? FormatValueAsString(TValue value)
protected virtual string? FormatValueAsString([AllowNull] TValue value)
MackinnonBuck marked this conversation as resolved.
Show resolved Hide resolved
=> value?.ToString();

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Web/src/Forms/InputDate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
}

/// <inheritdoc />
protected override string FormatValueAsString(TValue value)
protected override string FormatValueAsString([AllowNull] TValue value)
{
switch (value)
{
Expand Down
40 changes: 40 additions & 0 deletions src/Components/Web/src/Forms/InputExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;

namespace Microsoft.AspNetCore.Components.Forms
{
internal static class InputExtensions
{
public static bool TryParseSelectableValueFromString<TValue>(this InputBase<TValue> input, string? value, [MaybeNull] out TValue result, [NotNullWhen(false)] out string? validationErrorMessage)
{
if (typeof(TValue) == typeof(string))
{
result = (TValue)(object?)value;
validationErrorMessage = null;
return true;
}
else if (typeof(TValue).IsEnum || (Nullable.GetUnderlyingType(typeof(TValue))?.IsEnum ?? false))
{
var success = BindConverter.TryConvertTo<TValue>(value, CultureInfo.CurrentCulture, out var parsedValue);
if (success)
{
result = parsedValue;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = $"The {input.FieldIdentifier.FieldName} field is not valid.";
return false;
}
}

throw new InvalidOperationException($"{input.GetType()} does not support the type '{typeof(TValue)}'.");
}
}
}
2 changes: 1 addition & 1 deletion src/Components/Web/src/Forms/InputNumber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ protected override bool TryParseValueFromString(string? value, [MaybeNull] out T
/// </summary>
/// <param name="value">The value to format.</param>
/// <returns>A string representation of the value.</returns>
protected override string? FormatValueAsString(TValue value)
protected override string? FormatValueAsString([AllowNull] TValue value)
{
// Avoiding a cast to IFormattable to avoid boxing.
switch (value)
Expand Down
68 changes: 68 additions & 0 deletions src/Components/Web/src/Forms/InputRadio.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components.Rendering;

namespace Microsoft.AspNetCore.Components.Forms
{
/// <summary>
/// An input component used for selecting a value from a group of choices.
/// </summary>
public class InputRadio<TValue> : InputBase<TValue>
{
/// <summary>
/// Gets the name of this <see cref="InputRadio{TValue}"/> group.
/// </summary>
protected string? GroupName { get; private set; }

/// <summary>
/// Gets or sets the value that will be bound when this radio input is selected.
/// </summary>
[AllowNull]
[MaybeNull]
[Parameter]
public TValue SelectedValue { get; set; } = default;

/// <summary>
/// Gets or sets group name inherited from an ancestor <see cref="InputRadioGroup"/>.
/// </summary>
[CascadingParameter] InputRadioGroup? CascadedRadioGroup { get; set; }

/// <inheritdoc />
protected override void OnParametersSet()
{
GroupName = AdditionalAttributes != null && AdditionalAttributes.TryGetValue("name", out var nameAttribute) ?
MackinnonBuck marked this conversation as resolved.
Show resolved Hide resolved
Convert.ToString(nameAttribute) :
MackinnonBuck marked this conversation as resolved.
Show resolved Hide resolved
CascadedRadioGroup?.GroupName;

if (string.IsNullOrEmpty(GroupName))
{
throw new InvalidOperationException($"{GetType()} requires either an explicit 'name' attribute or " +
$"a cascading parameter 'Name'. Normally this is provided by an ancestor {nameof(InputRadioGroup)}.");
}
}

/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
Debug.Assert(GroupName != null);

builder.OpenElement(0, "input");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddAttribute(2, "type", "radio");
builder.AddAttribute(3, "class", CssClass);
builder.AddAttribute(4, "name", GroupName);
builder.AddAttribute(5, "value", BindConverter.FormatValue(FormatValueAsString(SelectedValue)));
builder.AddAttribute(6, "checked", SelectedValue?.Equals(CurrentValue));
builder.AddAttribute(7, "onchange", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.CloseElement();
}

/// <inheritdoc />
protected override bool TryParseValueFromString(string? value, [MaybeNull] out TValue result, [NotNullWhen(false)] out string? validationErrorMessage)
=> this.TryParseSelectableValueFromString(value, out result, out validationErrorMessage);
}
}
45 changes: 45 additions & 0 deletions src/Components/Web/src/Forms/InputRadioGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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;
using System.Diagnostics;
using Microsoft.AspNetCore.Components.Rendering;

namespace Microsoft.AspNetCore.Components.Forms
{
/// <summary>
/// Groups child <see cref="InputRadio{TValue}"/> components.
/// </summary>
public class InputRadioGroup : ComponentBase
{
internal string? GroupName { get; private set; }

/// <summary>
/// Gets or sets the child content to be rendering inside the <see cref="InputRadioGroup"/>.
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }

/// <summary>
/// Gets or sets the name of the group.
/// </summary>
[Parameter] public string? Name { get; set; }

/// <inheritdoc />
protected override void OnParametersSet()
{
GroupName ??= !string.IsNullOrEmpty(Name) ? Name : Guid.NewGuid().ToString("N");
MackinnonBuck marked this conversation as resolved.
Show resolved Hide resolved
}

/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
Debug.Assert(GroupName != null);

builder.OpenComponent<CascadingValue<InputRadioGroup>>(0);
builder.AddAttribute(1, "IsFixed", true);
MackinnonBuck marked this conversation as resolved.
Show resolved Hide resolved
builder.AddAttribute(2, "Value", this);
builder.AddAttribute(3, "ChildContent", ChildContent);
builder.CloseComponent();
}
}
}
31 changes: 1 addition & 30 deletions src/Components/Web/src/Forms/InputSelect.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// 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;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Microsoft.AspNetCore.Components.Rendering;

namespace Microsoft.AspNetCore.Components.Forms
Expand All @@ -13,8 +11,6 @@ namespace Microsoft.AspNetCore.Components.Forms
/// </summary>
public class InputSelect<TValue> : InputBase<TValue>
{
private static readonly Type? _nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(TValue));

/// <summary>
/// Gets or sets the child content to be rendering inside the select element.
/// </summary>
Expand All @@ -34,31 +30,6 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)

/// <inheritdoc />
protected override bool TryParseValueFromString(string? value, [MaybeNull] out TValue result, [NotNullWhen(false)] out string? validationErrorMessage)
{
if (typeof(TValue) == typeof(string))
{
result = (TValue)(object?)value;
validationErrorMessage = null;
return true;
}
else if (typeof(TValue).IsEnum || (_nullableUnderlyingType != null && _nullableUnderlyingType.IsEnum))
{
var success = BindConverter.TryConvertTo<TValue>(value, CultureInfo.CurrentCulture, out var parsedValue);
if (success)
{
result = parsedValue;
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = $"The {FieldIdentifier.FieldName} field is not valid.";
return false;
}
}

throw new InvalidOperationException($"{GetType()} does not support the type '{typeof(TValue)}'.");
}
=> this.TryParseSelectableValueFromString(value, out result, out validationErrorMessage);
}
}
Loading