Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -9,7 +9,7 @@
<section ignore>
@((MarkupString)@Localizer["NormalDesc"].Value)
</section>
<AutoFill @bind-Value="Model1" Items="Items1"
<AutoFill @bind-Value="Model1" Items="Items1" IsAutoClearWhenInvalid="true"
IsLikeMatch="true" OnGetDisplayText="OnGetDisplayText" IsSelectAllTextOnFocus="true">
<ItemTemplate>
<div class="d-flex">
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>9.11.3-beta03</Version>
<Version>9.11.3-beta04</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public partial class AutoComplete
.AddClass($"text-danger", IsValid.HasValue && !IsValid.Value)
.Build();

private string? TriggerBlurString => OnBlurAsync != null ? "true" : null;

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,6 @@ public abstract class PopoverCompleteBase<TValue> : BootstrapInputBase<TValue>,
/// </summary>
protected string? SkipEnterString => SkipEnter ? "true" : null;

/// <summary>
/// 获得 是否跳过 Blur 处理字符串
/// </summary>
protected string? TriggerBlurString => OnBlurAsync != null ? "true" : null;

/// <summary>
/// 获得 滚动行为字符串
/// </summary>
Expand Down Expand Up @@ -181,12 +176,20 @@ protected override void OnParametersSet()
IsPopover |= BootstrapBlazorOptions.Value.IsPopover;
}

/// <summary>
/// 触发 OnBlurAsync 回调前回调方法
/// </summary>
/// <returns></returns>
protected virtual Task OnBeforeBlurAsync() => Task.CompletedTask;

/// <summary>
/// 触发 OnBlur 回调方法 由 Javascript 触发
/// </summary>
[JSInvokable]
public async Task TriggerBlur()
{
await OnBeforeBlurAsync();

if (OnBlurAsync != null)
{
await OnBlurAsync(Value);
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/Components/AutoFill/AutoFill.razor
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
data-bs-toggle="@ToggleString" data-bs-placement="@PlacementString"
data-bs-offset="@OffsetString" data-bs-custom-class="@CustomClassString"
data-bb-auto-dropdown-focus="@ShowDropdownListOnFocusString" data-bb-debounce="@DurationString"
data-bb-skip-esc="@SkipEscString" data-bb-skip-enter="@SkipEnterString"
data-bb-skip-esc="@SkipEscString" data-bb-skip-enter="@SkipEnterString" data-bb-blur="@TriggerBlurString"
data-bb-scroll-behavior="@ScrollIntoViewBehaviorString"
placeholder="@PlaceHolder" disabled="@Disabled" />
<span class="form-select-append"><i class="@Icon"></i></span>
Expand Down
36 changes: 35 additions & 1 deletion src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,20 @@ public partial class AutoFill<TValue>
[Parameter]
public Func<Task>? OnClearAsync { get; set; }

/// <summary>
/// 获得/设置 输入框内容无效时是否自动清空内容 默认 false
/// </summary>
[Parameter]
public bool IsAutoClearWhenInvalid { get; set; }

[Inject]
[NotNull]
private IStringLocalizer<AutoComplete>? Localizer { get; set; }

private string? ShowDropdownListOnFocusString => ShowDropdownListOnFocus ? "true" : null;

private string? TriggerBlurString => (OnBlurAsync != null || IsAutoClearWhenInvalid) ? "true" : null;

private string? _displayText;

private List<TValue>? _filterItems;
Expand Down Expand Up @@ -175,7 +183,20 @@ protected override void OnParametersSet()
/// <inheritdoc/>
/// </summary>
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, _displayText);
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, _displayText, nameof(TriggerChange));

private string _clientValue = "";

/// <summary>
/// 由客户端 JavaScript 触发
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
[JSInvokable]
public void TriggerChange(string v)
{
_clientValue = v;
}

private bool IsNullable() => !ValueType.IsValueType || NullableUnderlyingType != null;

Expand Down Expand Up @@ -275,4 +296,17 @@ public async Task TriggerFilter(string val)
}
_dropdown.Render();
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <returns></returns>
protected override async Task OnBeforeBlurAsync()
{
if (IsAutoClearWhenInvalid && GetDisplayText(Value) != _clientValue)
{
CurrentValue = default;
await InvokeVoidAsync("setValue", Id, "");
}
}
}
2 changes: 2 additions & 0 deletions src/BootstrapBlazor/Components/Search/Search.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ public partial class Search<TValue>
[NotNull]
private IStringLocalizer<Search<TValue>>? Localizer { get; set; }

private string? TriggerBlurString => OnBlurAsync != null ? "true" : null;

private string? ClassString => CssBuilder.Default("search auto-complete")
.AddClassFromAttributes(AdditionalAttributes)
.Build();
Expand Down
42 changes: 42 additions & 0 deletions test/UnitTest/Components/AutoFillTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,48 @@ await cut.InvokeAsync(() =>
Assert.True(invalid);
}

[Fact]
public async Task IsAutoClearWhenInvalid_Ok()
{
var model = new MockModel() { Value = new Foo() { Name = "Test-Select1" } };
var items = new List<Foo>()
{
new() { Name = "test1" },
new() { Name = "test2" }
};
var cut = Context.RenderComponent<AutoFill<Foo>>(pb =>
{
pb.Add(a => a.Items, items);
pb.Add(a => a.Value, model.Value);
pb.Add(a => a.IsAutoClearWhenInvalid, true);
pb.Add(a => a.OnGetDisplayText, f => f?.Name);
});
// 设置一个无效值
await cut.InvokeAsync(() => cut.Instance.TriggerChange("Invalid-Value"));

// 触发 OnBlur 回调
await cut.InvokeAsync(() => cut.Instance.TriggerBlur());

// 值应被清空
Assert.Null(cut.Instance.Value);

// 设定 IsAutoClearWhenInvalid false
cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.IsAutoClearWhenInvalid, false);
pb.Add(a => a.Value, model.Value);
});

// 设置一个无效值
await cut.InvokeAsync(() => cut.Instance.TriggerChange("Invalid-Value"));

// 触发 OnBlur 回调
await cut.InvokeAsync(() => cut.Instance.TriggerBlur());

// 值不应被清空
Assert.NotNull(cut.Instance.Value);
}

class MockModel
{
[Required]
Expand Down
Loading