Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public partial class AutoComplete
[NotNull]
private RenderTemplate? _dropdown = null;

private string _clientValue = "";

private string? ClassString => CssBuilder.Default("auto-complete")
.AddClass("is-clearable", IsClearable)
.Build();
Expand Down Expand Up @@ -117,6 +119,8 @@ protected override void OnInitialized()
_filterItems = [.. _filterItems.Take(DisplayCount.Value)];
}
}

_clientValue = Value ?? "";
}

/// <summary>
Expand All @@ -135,8 +139,29 @@ protected override void OnParametersSet()
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="firstRender"></param>
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value);
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);

if (!firstRender)
{
if (Value != _clientValue)
{
_clientValue = Value;
await InvokeVoidAsync("setValue", Id, _clientValue);
}
}
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, Value, GetChangedEventCallbackName());

private string? GetChangedEventCallbackName() => (OnValueChanged != null || ValueChanged.HasDelegate) ? nameof(TriggerChange) : null;

/// <summary>
/// Gets whether show the clear button.
Expand Down Expand Up @@ -199,6 +224,20 @@ public async Task TriggerFilter(string val)
_dropdown.Render();
}

/// <summary>
/// 支持双向绑定 由客户端 JavaScript 触发
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
[JSInvokable]
public Task TriggerChange(string v)
{
_clientValue = v;
CurrentValueAsString = v;

return Task.CompletedTask;
}

private List<string> GetFilterItemsByValue(string val)
{
var comparison = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import EventHandler from "../../modules/event-handler.js"
import Input from "../../modules/input.js"
import Popover from "../../modules/base-popover.js"

export function init(id, invoke) {
export function init(id, invoke, value, changedEventCallback) {
const el = document.getElementById(id)
const menu = el.querySelector('.dropdown-menu')
const input = document.getElementById(`${id}_input`)
const ac = { el, invoke, menu, input }
Data.set(id, ac)

input.value = value;

const isPopover = input.getAttribute('data-bs-toggle') === 'bb.dropdown';
if (isPopover) {
ac.popover = Popover.init(el, { toggleClass: '[data-bs-toggle="bb.dropdown"]' });
Expand Down Expand Up @@ -62,6 +64,12 @@ export function init(id, invoke) {
}
});

if (changedEventCallback) {
EventHandler.on(input, 'change', e => {
invoke.invokeMethodAsync(changedEventCallback, e.target.value);
});
}

let filterDuration = duration;
if (filterDuration === 0) {
filterDuration = 200;
Expand Down
31 changes: 31 additions & 0 deletions test/UnitTest/Components/AutoCompleteTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,37 @@ public void Value_Ok()
Assert.Equal(2, menus.Count);
}

[Fact]
public async Task BindValue_Ok()
{
// 由于设置了双向绑定 Value 改变后触发 change 事件
var clientValue = "";
var cut = Context.RenderComponent<AutoComplete>(pb =>
{
pb.Add(a => a.Items, new List<string>() { "test1", "test12", "test123", "test1234" });
pb.Add(a => a.Value, "test12");
pb.Add(a => a.OnValueChanged, v =>
{
clientValue = v;
return Task.CompletedTask;
});
});

await cut.InvokeAsync(() => cut.Instance.TriggerChange("test4"));
Assert.Equal("test4", clientValue);

cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.OnValueChanged, null);
pb.Add(a => a.ValueChanged, v =>
{
clientValue = v;
});
});
await cut.InvokeAsync(() => cut.Instance.TriggerChange("test5"));
Assert.Equal("test5", clientValue);
}

[Fact]
public void IsClearable_Ok()
{
Expand Down
Loading