Skip to content

Commit

Permalink
Merge pull request #672 from havit/feature/net8-inputs-two-way-binding
Browse files Browse the repository at this point in the history
#468 [HxInputBase] Incorporate two-way input binding (SetUpdatesAttri…
  • Loading branch information
hakenr authored Dec 7, 2023
2 parents 61d1048 + 1168db5 commit 934bee2
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 14 deletions.
2 changes: 1 addition & 1 deletion BlazorAppTest/BlazorAppTest.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

Expand Down
3 changes: 3 additions & 0 deletions Havit.Blazor.Components.Web.Bootstrap/Forms/HxCheckbox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ protected override void BuildRenderInput(RenderTreeBuilder builder)
BuildRenderInput_AddCommonAttributes(builder, "checkbox");
builder.AddAttribute(1000, "checked", BindConverter.FormatValue(CurrentValue));
builder.AddAttribute(1001, "onchange", value: EventCallback.Factory.CreateBinder<bool>(this, value => CurrentValue = value, CurrentValue));
#if NET8_0_OR_GREATER
builder.SetUpdatesAttributeName("checked");
#endif
builder.AddEventStopPropagationAttribute(1002, "onclick", true);
builder.AddElementReferenceCapture(1003, elementReference => InputElement = elementReference);
builder.CloseElement(); // input
Expand Down
4 changes: 3 additions & 1 deletion Havit.Blazor.Components.Web.Bootstrap/Forms/HxInputNumber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@ protected override void BuildRenderInput(RenderTreeBuilder builder)

builder.AddAttribute(1002, "onfocus", "this.select();"); // source: https://stackoverflow.com/questions/4067469/selecting-all-text-in-html-text-input-when-clicked
builder.AddAttribute(1003, "onchange", EventCallback.Factory.CreateBinder<string>(this, value => CurrentValueAsString = value, CurrentValueAsString));

#if NET8_0_OR_GREATER
builder.SetUpdatesAttributeName("value");
#endif
// normalize pasted value
builder.AddAttribute(1004, "onpaste", @"this.value = event.clipboardData.getData('text/plain').replace(/[^\d.,\-eE]/g, ''); this.dispatchEvent(new Event('change')); return false;");

Expand Down
4 changes: 3 additions & 1 deletion Havit.Blazor.Components.Web.Bootstrap/Forms/HxInputRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ protected override void BuildRenderInput(RenderTreeBuilder builder)

builder.AddAttribute(4, "value", BindConverter.FormatValue(Value));
builder.AddAttribute(5, BindEventEffective.ToEventName(), EventCallback.Factory.CreateBinder(this, async value => await HandleValueChanged(value), Value));

#if NET8_0_OR_GREATER
builder.SetUpdatesAttributeName("value");
#endif
builder.AddAttribute(10, "min", Min);
builder.AddAttribute(11, "max", Max);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ protected override void BuildRenderInput(RenderTreeBuilder builder)

builder.AddAttribute(1002, "value", FormatValueAsString(Value));
builder.AddAttribute(1003, BindEvent.ToEventName(), EventCallback.Factory.CreateBinder<string>(this, value => CurrentValueAsString = value, CurrentValueAsString));

#if NET8_0_OR_GREATER
builder.SetUpdatesAttributeName("value");
if (!String.IsNullOrEmpty(this.NameAttributeValue))
{
builder.AddAttribute(1004, "name", NameAttributeValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ protected void BuildRenderInput_RenderRadioItem(RenderTreeBuilder builder, int i
builder.AddAttribute(207, "disabled", !CascadeEnabledComponent.EnabledEffective(this));
int j = index;
builder.AddAttribute(208, "onclick", EventCallback.Factory.Create(this, () => HandleInputClick(j)));
#if NET8_0_OR_GREATER
builder.SetUpdatesAttributeName("checked");
#endif
builder.AddEventStopPropagationAttribute(209, "onclick", true);
builder.AddMultipleAttributes(250, this.AdditionalAttributes);
builder.CloseElement(); // input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,18 @@ public partial class HxInputDateInternal<TValue> : InputBase<TValue>, IAsyncDisp

protected DateTime GetCalendarDisplayMonthEffective => GetDateTimeFromValue(CurrentValue) ?? CalendarDisplayMonth;

#if !NET8_0_OR_GREATER
private TValue previousValue;

private bool previousParsingAttemptFailed;
private ValidationMessageStore validationMessageStore;
#endif

private HxDropdownToggleElement hxDropdownToggleElement;
private ElementReference iconWrapperElement;
private IJSObjectReference jsModule;
private bool firstRenderCompleted;

#if !NET8_0_OR_GREATER
protected override void OnParametersSet()
{
base.OnParametersSet();
Expand All @@ -96,13 +98,17 @@ protected override void OnParametersSet()
previousValue = Value;
}
}
#endif

protected override string FormatValueAsString(TValue value) => HxInputDate<TValue>.FormatValue(value);

private void HandleValueChanged(ChangeEventArgs changeEventArgs)
{
#if NET8_0_OR_GREATER
CurrentValueAsString = changeEventArgs.Value.ToString();
#else
// HandleValueChanged is used instead of TryParseValueFromString
// When TryParseValueFromString is used, invalid input is replaced by previous value.
// When TryParseValueFromString is used (pre net8), invalid input is replaced by previous value.
bool parsingFailed;
validationMessageStore.Clear(FieldIdentifier);

Expand All @@ -123,12 +129,27 @@ private void HandleValueChanged(ChangeEventArgs changeEventArgs)
EditContext.NotifyValidationStateChanged();
previousParsingAttemptFailed = parsingFailed;
}

#endif
}

protected override bool TryParseValueFromString(string value, out TValue result, out string validationErrorMessage)
{
#if NET8_0_OR_GREATER
if (HxInputDate<DateTime>.TryParseDateTimeOffsetFromString(value, null, out var date))
{
result = GetValueFromDateTimeOffset(date);
validationErrorMessage = null;
return true;
}
else
{
result = default;
validationErrorMessage = ParsingErrorMessageEffective;
return false;
}
#else
throw new NotSupportedException();
#endif
}

protected override async Task OnAfterRenderAsync(bool firstRender)
Expand All @@ -148,11 +169,10 @@ private CalendarDateCustomizationResult GetCalendarDateCustomization(CalendarDat
{
return CalendarDateCustomizationProviderEffective?.Invoke(request with { Target = CalendarDateCustomizationTarget.InputDate }) ?? null;
}

private async Task HandleClearClickAsync()
{
CurrentValue = default;
ClearPreviousParsingMessage();

SetCurrentDate(null);
await CloseDropdownAsync();
}

Expand All @@ -164,26 +184,43 @@ private async Task CloseDropdownAsync()

private async Task HandleCalendarValueChangedAsync(DateTime? date)
{
CurrentValue = GetValueFromDateTimeOffset((date != null) ? new DateTimeOffset(date.Value) : null);
SetCurrentDate(date);
await CloseDropdownAsync();
}

protected async Task HandleCustomDateClick(DateTime value)
{
CurrentValue = GetValueFromDateTimeOffset(new DateTimeOffset(DateTime.SpecifyKind(value, DateTimeKind.Unspecified), TimeSpan.Zero));
ClearPreviousParsingMessage();
SetCurrentDate(value);
await CloseDropdownAsync();
}

protected void SetCurrentDate(DateTime? date)
{
#if NET8_0_OR_GREATER
CurrentValueAsString = date?.ToShortDateString(); // we need to trigger the logic in CurrentValueAsString setter
#else
if (date == null)
{
CurrentValue = default;
}
else
{
CurrentValue = GetValueFromDateTimeOffset(new DateTimeOffset(DateTime.SpecifyKind(date.Value, DateTimeKind.Unspecified), TimeSpan.Zero));
}
ClearPreviousParsingMessage();
#endif
}

#if !NET8_0_OR_GREATER
private void ClearPreviousParsingMessage()
{
if (previousParsingAttemptFailed)
{
previousParsingAttemptFailed = false;
validationMessageStore.Clear(FieldIdentifier);
EditContext.NotifyValidationStateChanged();
}
}
#endif

private string GetNameAttributeValue()
{
Expand Down Expand Up @@ -246,7 +283,9 @@ public async ValueTask DisposeAsync()

protected virtual async ValueTask DisposeAsyncCore()
{
#if !NET8_0_OR_GREATER
validationMessageStore?.Clear();
#endif

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ protected override GridCellTemplate GetHeaderCellTemplate(GridHeaderCellContext
builder.AddAttribute(103, "checked", AllDataItemsSelected);
builder.AddAttribute(104, "onchange", EventCallback.Factory.Create<ChangeEventArgs>(this, HandleSelectAllOrNoneClick));
#if NET8_0_OR_GREATER
builder.SetUpdatesAttributeName("checked");
#endif
builder.AddEventStopPropagationAttribute(105, "onclick", true);
builder.CloseElement(); // input
Expand All @@ -51,6 +54,9 @@ protected override GridCellTemplate GetItemCellTemplate(TItem item)
bool selected = SelectedDataItems?.Contains(item) ?? false;
builder.AddAttribute(103, "checked", selected);
builder.AddAttribute(104, "onchange", EventCallback.Factory.Create<ChangeEventArgs>(this, HandleSelectDataItemClick(item, selected)));
#if NET8_0_OR_GREATER
builder.SetUpdatesAttributeName("checked");
#endif
builder.AddEventStopPropagationAttribute(105, "onclick", true);
builder.CloseElement(); // input
Expand Down

0 comments on commit 934bee2

Please sign in to comment.