Skip to content

Commit 7051bf1

Browse files
authored
fix(AutoComplete): support bind-Value/OnValueChanged callback (#6792)
* fix: 修复初始化值未赋值问题 * feat: 增加 change 事件支持双向绑定 * feat: 支持双向绑定 * feat: 增加 _clientValue 变量支持双向绑定逻辑 * refactor: 通过 GetChangedEventCallbackName 提高性能 * refactor: 使用服务器端传递的回调方法名称 * test: 增加双向绑定单元测试 * refactor: 参数展开 * refactor: 更新 _clientValue 参数位置 * refactor: 简化代码
1 parent f5960e7 commit 7051bf1

File tree

3 files changed

+80
-2
lines changed

3 files changed

+80
-2
lines changed

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ public partial class AutoComplete
8585
[NotNull]
8686
private RenderTemplate? _dropdown = null;
8787

88+
private string? _clientValue;
89+
8890
private string? ClassString => CssBuilder.Default("auto-complete")
8991
.AddClass("is-clearable", IsClearable)
9092
.Build();
@@ -130,13 +132,36 @@ protected override void OnParametersSet()
130132
PlaceHolder ??= Localizer[nameof(PlaceHolder)];
131133
Icon ??= IconTheme.GetIconByKey(ComponentIcons.AutoCompleteIcon);
132134
LoadingIcon ??= IconTheme.GetIconByKey(ComponentIcons.LoadingIcon);
135+
136+
_clientValue = Value;
133137
}
134138

135139
/// <summary>
136140
/// <inheritdoc/>
137141
/// </summary>
142+
/// <param name="firstRender"></param>
138143
/// <returns></returns>
139-
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value);
144+
protected override async Task OnAfterRenderAsync(bool firstRender)
145+
{
146+
await base.OnAfterRenderAsync(firstRender);
147+
148+
if (!firstRender)
149+
{
150+
if (Value != _clientValue)
151+
{
152+
_clientValue = Value;
153+
await InvokeVoidAsync("setValue", Id, _clientValue);
154+
}
155+
}
156+
}
157+
158+
/// <summary>
159+
/// <inheritdoc/>
160+
/// </summary>
161+
/// <returns></returns>
162+
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value, GetChangedEventCallbackName());
163+
164+
private string? GetChangedEventCallbackName() => (OnValueChanged != null || ValueChanged.HasDelegate) ? nameof(TriggerChange) : null;
140165

141166
/// <summary>
142167
/// Gets whether show the clear button.
@@ -199,6 +224,20 @@ public async Task TriggerFilter(string val)
199224
_dropdown.Render();
200225
}
201226

227+
/// <summary>
228+
/// 支持双向绑定 由客户端 JavaScript 触发
229+
/// </summary>
230+
/// <param name="v"></param>
231+
/// <returns></returns>
232+
[JSInvokable]
233+
public Task TriggerChange(string v)
234+
{
235+
_clientValue = v;
236+
CurrentValueAsString = v;
237+
238+
return Task.CompletedTask;
239+
}
240+
202241
private List<string> GetFilterItemsByValue(string val)
203242
{
204243
var comparison = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import EventHandler from "../../modules/event-handler.js"
55
import Input from "../../modules/input.js"
66
import Popover from "../../modules/base-popover.js"
77

8-
export function init(id, invoke) {
8+
export function init(id, invoke, value, changedEventCallback) {
99
const el = document.getElementById(id)
1010
const menu = el.querySelector('.dropdown-menu')
1111
const input = document.getElementById(`${id}_input`)
1212
const ac = { el, invoke, menu, input }
1313
Data.set(id, ac)
1414

15+
input.value = value;
16+
1517
const isPopover = input.getAttribute('data-bs-toggle') === 'bb.dropdown';
1618
if (isPopover) {
1719
ac.popover = Popover.init(el, { toggleClass: '[data-bs-toggle="bb.dropdown"]' });
@@ -62,6 +64,12 @@ export function init(id, invoke) {
6264
}
6365
});
6466

67+
if (changedEventCallback) {
68+
EventHandler.on(input, 'change', e => {
69+
invoke.invokeMethodAsync(changedEventCallback, e.target.value);
70+
});
71+
}
72+
6573
let filterDuration = duration;
6674
if (filterDuration === 0) {
6775
filterDuration = 200;

test/UnitTest/Components/AutoCompleteTest.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,37 @@ public void Value_Ok()
5454
Assert.Equal(2, menus.Count);
5555
}
5656

57+
[Fact]
58+
public async Task BindValue_Ok()
59+
{
60+
// 由于设置了双向绑定 Value 改变后触发 change 事件
61+
var clientValue = "";
62+
var cut = Context.RenderComponent<AutoComplete>(pb =>
63+
{
64+
pb.Add(a => a.Items, new List<string>() { "test1", "test12", "test123", "test1234" });
65+
pb.Add(a => a.Value, "test12");
66+
pb.Add(a => a.OnValueChanged, v =>
67+
{
68+
clientValue = v;
69+
return Task.CompletedTask;
70+
});
71+
});
72+
73+
await cut.InvokeAsync(() => cut.Instance.TriggerChange("test4"));
74+
Assert.Equal("test4", clientValue);
75+
76+
cut.SetParametersAndRender(pb =>
77+
{
78+
pb.Add(a => a.OnValueChanged, null);
79+
pb.Add(a => a.ValueChanged, v =>
80+
{
81+
clientValue = v;
82+
});
83+
});
84+
await cut.InvokeAsync(() => cut.Instance.TriggerChange("test5"));
85+
Assert.Equal("test5", clientValue);
86+
}
87+
5788
[Fact]
5889
public void IsClearable_Ok()
5990
{

0 commit comments

Comments
 (0)