Skip to content

Commit 0addc2f

Browse files
ArgoZhangbraia123
andauthored
feat(AutoFill): add IsAutoClearWhenInvalid parameter (#6950)
* AutoFill组件变动 input输入Enter时,如候选项唯一,直接选中 当焦点从AutoFill移出时,如GetDisplayText(Value)对应的内容与input中的内容不符则清空Value和Input * Modify Enter key handling in AutoComplete component Signed-off-by: braia123 <[email protected]> * refactor: 代码重构 * refactor: 移除 onblur 回调 * revert: 撤销清空逻辑 * revert: 撤销 js 方法 * refactor: TriggerBlurString 移动到组件内 * feat: 增加 IsAutoClearWhenInvalid 参数实现逻辑 * doc: 更新示例 * refactor: 更改方法为 TriggerChange * test: 增加单元测试 * doc: 代码格式化 * chore: bump version 9.11.-3-beta04 --------- Signed-off-by: braia123 <[email protected]> Co-authored-by: braia123 <[email protected]> Co-authored-by: Argo Zhang <[email protected]>
1 parent 337f129 commit 0addc2f

File tree

8 files changed

+92
-9
lines changed

8 files changed

+92
-9
lines changed

src/BootstrapBlazor.Server/Components/Samples/AutoFills.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<section ignore>
1010
@((MarkupString)@Localizer["NormalDesc"].Value)
1111
</section>
12-
<AutoFill @bind-Value="Model1" Items="Items1"
12+
<AutoFill @bind-Value="Model1" Items="Items1" IsAutoClearWhenInvalid="true"
1313
IsLikeMatch="true" OnGetDisplayText="OnGetDisplayText" IsSelectAllTextOnFocus="true">
1414
<ItemTemplate>
1515
<div class="d-flex">

src/BootstrapBlazor/BootstrapBlazor.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>9.11.3-beta03</Version>
4+
<Version>9.11.3-beta04</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ public partial class AutoComplete
100100
.AddClass($"text-danger", IsValid.HasValue && !IsValid.Value)
101101
.Build();
102102

103+
private string? TriggerBlurString => OnBlurAsync != null ? "true" : null;
104+
103105
/// <summary>
104106
/// <inheritdoc/>
105107
/// </summary>

src/BootstrapBlazor/Components/AutoComplete/PopoverCompleteBase.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,6 @@ public abstract class PopoverCompleteBase<TValue> : BootstrapInputBase<TValue>,
121121
/// </summary>
122122
protected string? SkipEnterString => SkipEnter ? "true" : null;
123123

124-
/// <summary>
125-
/// 获得 是否跳过 Blur 处理字符串
126-
/// </summary>
127-
protected string? TriggerBlurString => OnBlurAsync != null ? "true" : null;
128-
129124
/// <summary>
130125
/// 获得 滚动行为字符串
131126
/// </summary>
@@ -181,12 +176,20 @@ protected override void OnParametersSet()
181176
IsPopover |= BootstrapBlazorOptions.Value.IsPopover;
182177
}
183178

179+
/// <summary>
180+
/// 触发 OnBlurAsync 回调前回调方法
181+
/// </summary>
182+
/// <returns></returns>
183+
protected virtual Task OnBeforeBlurAsync() => Task.CompletedTask;
184+
184185
/// <summary>
185186
/// 触发 OnBlur 回调方法 由 Javascript 触发
186187
/// </summary>
187188
[JSInvokable]
188189
public async Task TriggerBlur()
189190
{
191+
await OnBeforeBlurAsync();
192+
190193
if (OnBlurAsync != null)
191194
{
192195
await OnBlurAsync(Value);

src/BootstrapBlazor/Components/AutoFill/AutoFill.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
data-bs-toggle="@ToggleString" data-bs-placement="@PlacementString"
1313
data-bs-offset="@OffsetString" data-bs-custom-class="@CustomClassString"
1414
data-bb-auto-dropdown-focus="@ShowDropdownListOnFocusString" data-bb-debounce="@DurationString"
15-
data-bb-skip-esc="@SkipEscString" data-bb-skip-enter="@SkipEnterString"
15+
data-bb-skip-esc="@SkipEscString" data-bb-skip-enter="@SkipEnterString" data-bb-blur="@TriggerBlurString"
1616
data-bb-scroll-behavior="@ScrollIntoViewBehaviorString"
1717
placeholder="@PlaceHolder" disabled="@Disabled" />
1818
<span class="form-select-append"><i class="@Icon"></i></span>

src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,20 @@ public partial class AutoFill<TValue>
119119
[Parameter]
120120
public Func<Task>? OnClearAsync { get; set; }
121121

122+
/// <summary>
123+
/// 获得/设置 输入框内容无效时是否自动清空内容 默认 false
124+
/// </summary>
125+
[Parameter]
126+
public bool IsAutoClearWhenInvalid { get; set; }
127+
122128
[Inject]
123129
[NotNull]
124130
private IStringLocalizer<AutoComplete>? Localizer { get; set; }
125131

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

134+
private string? TriggerBlurString => (OnBlurAsync != null || IsAutoClearWhenInvalid) ? "true" : null;
135+
128136
private string? _displayText;
129137

130138
private List<TValue>? _filterItems;
@@ -175,7 +183,20 @@ protected override void OnParametersSet()
175183
/// <inheritdoc/>
176184
/// </summary>
177185
/// <returns></returns>
178-
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, _displayText);
186+
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, _displayText, nameof(TriggerChange));
187+
188+
private string _clientValue = "";
189+
190+
/// <summary>
191+
/// 由客户端 JavaScript 触发
192+
/// </summary>
193+
/// <param name="v"></param>
194+
/// <returns></returns>
195+
[JSInvokable]
196+
public void TriggerChange(string v)
197+
{
198+
_clientValue = v;
199+
}
179200

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

@@ -275,4 +296,17 @@ public async Task TriggerFilter(string val)
275296
}
276297
_dropdown.Render();
277298
}
299+
300+
/// <summary>
301+
/// <inheritdoc/>
302+
/// </summary>
303+
/// <returns></returns>
304+
protected override async Task OnBeforeBlurAsync()
305+
{
306+
if (IsAutoClearWhenInvalid && GetDisplayText(Value) != _clientValue)
307+
{
308+
CurrentValue = default;
309+
await InvokeVoidAsync("setValue", Id, "");
310+
}
311+
}
278312
}

src/BootstrapBlazor/Components/Search/Search.razor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ public partial class Search<TValue>
142142
[NotNull]
143143
private IStringLocalizer<Search<TValue>>? Localizer { get; set; }
144144

145+
private string? TriggerBlurString => OnBlurAsync != null ? "true" : null;
146+
145147
private string? ClassString => CssBuilder.Default("search auto-complete")
146148
.AddClassFromAttributes(AdditionalAttributes)
147149
.Build();

test/UnitTest/Components/AutoFillTest.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,48 @@ await cut.InvokeAsync(() =>
513513
Assert.True(invalid);
514514
}
515515

516+
[Fact]
517+
public async Task IsAutoClearWhenInvalid_Ok()
518+
{
519+
var model = new MockModel() { Value = new Foo() { Name = "Test-Select1" } };
520+
var items = new List<Foo>()
521+
{
522+
new() { Name = "test1" },
523+
new() { Name = "test2" }
524+
};
525+
var cut = Context.RenderComponent<AutoFill<Foo>>(pb =>
526+
{
527+
pb.Add(a => a.Items, items);
528+
pb.Add(a => a.Value, model.Value);
529+
pb.Add(a => a.IsAutoClearWhenInvalid, true);
530+
pb.Add(a => a.OnGetDisplayText, f => f?.Name);
531+
});
532+
// 设置一个无效值
533+
await cut.InvokeAsync(() => cut.Instance.TriggerChange("Invalid-Value"));
534+
535+
// 触发 OnBlur 回调
536+
await cut.InvokeAsync(() => cut.Instance.TriggerBlur());
537+
538+
// 值应被清空
539+
Assert.Null(cut.Instance.Value);
540+
541+
// 设定 IsAutoClearWhenInvalid false
542+
cut.SetParametersAndRender(pb =>
543+
{
544+
pb.Add(a => a.IsAutoClearWhenInvalid, false);
545+
pb.Add(a => a.Value, model.Value);
546+
});
547+
548+
// 设置一个无效值
549+
await cut.InvokeAsync(() => cut.Instance.TriggerChange("Invalid-Value"));
550+
551+
// 触发 OnBlur 回调
552+
await cut.InvokeAsync(() => cut.Instance.TriggerBlur());
553+
554+
// 值不应被清空
555+
Assert.NotNull(cut.Instance.Value);
556+
}
557+
516558
class MockModel
517559
{
518560
[Required]

0 commit comments

Comments
 (0)