Blazor propertychange 奇怪的行为
Blazor propertychange strange behavior
我创建了一个可搜索的多 select 下拉菜单。我不确定这是否是我的代码中可能存在的错误或错误。
要重现,请执行以下操作:
1 Select 第一项
2 在输入搜索框中输入“o”或“one”
3 FilteredItems 的第一个复选框被选中,尽管它不在 SelectedItems
中
4 清除输入搜索框后再次输入“o”时显示未选中
<div>
<div class="form-group">
<button class="btn dropdown-toggle btn-light"
@onclick="@ToggleSelectMenu"
title="@ButtonHoverTitleText">
@ButtonText
</button>
</div>
<div hidden="@toggleSelectBox" class="shadow p-2 mb-5 bg-white rounded">
<div class="m-1">
<input class="form-control" @bind="FilterText" @bind:event="oninput" />
</div>
@foreach (var item in FilteredItems)
{
<label class="item">
<input class="form-check-input" type="checkbox" checked="@item.IsSelected"
@onchange="_e => {SelectionChanged(item.Item, _e.Value);}" /><span>@item.Item</span>
</label>
<br />
}
</div>
</div>
<p>
@string.Join(",", SelectedItems)
</p>
@code {
private string? _filterText;
private bool toggleSelectBox = true;
[Parameter] public List<string> Items { get; set; } = new List<string>() { "item", "one 1", "two 3", "last 4" };
[Parameter] public List<string> SelectedItems { get; set; } = new();
[Parameter] public EventCallback<List<string>> SelectedItemChanged { get; set; }
public List<SelectedItem> FilteredItems { get; set; } = new();
public string? ButtonText { get; set; } = "Nothing selected";
public string? ButtonHoverTitleText { get; set; }
public string? FilterText
{
get => _filterText;
set
{
_filterText = value;
FilteredItems = new();
if (!string.IsNullOrWhiteSpace(_filterText))
{
foreach (var item in Items?.Where(x => x.ToLower().Contains(_filterText.ToLower())))
{
FilteredItems.Add(new SelectedItem { Item = item, IsSelected = SelectedItems.Contains(item) });
}
}
else
{
foreach (var item in Items)
{
FilteredItems.Add(new SelectedItem { Item = item, IsSelected = SelectedItems.Contains(item) });
}
}
}
}
protected override void OnInitialized()
{
Items?.ForEach(i => FilteredItems.Add(new SelectedItem { Item = i }));
base.OnInitialized();
}
public void ToggleSelectMenu()
{
toggleSelectBox = !toggleSelectBox;
ClearSearchText();
}
private void ClearSearchText() => FilterText = null;
public void SelectionChanged(string item, object checkedValue)
{
if ((bool)checkedValue)
{
if (!SelectedItems.Contains(item))
{
SelectedItems.Add(item);
}
}
else
{
if (SelectedItems.Contains(item))
{
SelectedItems.Remove(item);
}
}
ButtonHoverTitleText = SelectedItems.Any() ? string.Join(",", SelectedItems.Select(x => x)) : null;
ButtonText = SelectedItems.Any()
? (SelectedItems.Count == 1 ? ButtonHoverTitleText : $"{SelectedItems.Count} items selected")
: "Nothing selected";
SelectedItemChanged.InvokeAsync(SelectedItems);
}
public class SelectedItem
{
public string? Item { get; set; }
public bool IsSelected { get; set; }
}
}
给出了答案,但为了更多地解释发生了什么,下面是当您键入“o”时会发生什么。请注意 item
如何被 one 1
替换 并且没有其他变化。换句话说,Blazor 检测更新 DOM 所需的数量并应用这些更改。这通常是您想要的,但是,当您动态更新一组项目(例如您的案例)时,这种行为是不可取的。 (注意:我在下面的输入中添加了一个 firstitem
属性只是为了表明它没有得到更新)。
答案是使用 @key
:
<label class="item" @key=item>
当您这样做时,只要您指定的键的 值 发生变化,DOM 就会更新。这将确保不仅更新 span 中的文本,而且更新输入元素。因此,根据经验,当您是列表中的 adding/removing/reordering 个元素时,请始终使用 @key
。另外,我上面没有提到,但是当您重新排序项目时,@key
起着至关重要的作用,因为它确保未更改的元素得以保留。
这里是link到documentation on @key。
我创建了一个可搜索的多 select 下拉菜单。我不确定这是否是我的代码中可能存在的错误或错误。
要重现,请执行以下操作:
1 Select 第一项
2 在输入搜索框中输入“o”或“one”
3 FilteredItems 的第一个复选框被选中,尽管它不在 SelectedItems
中4 清除输入搜索框后再次输入“o”时显示未选中
<div>
<div class="form-group">
<button class="btn dropdown-toggle btn-light"
@onclick="@ToggleSelectMenu"
title="@ButtonHoverTitleText">
@ButtonText
</button>
</div>
<div hidden="@toggleSelectBox" class="shadow p-2 mb-5 bg-white rounded">
<div class="m-1">
<input class="form-control" @bind="FilterText" @bind:event="oninput" />
</div>
@foreach (var item in FilteredItems)
{
<label class="item">
<input class="form-check-input" type="checkbox" checked="@item.IsSelected"
@onchange="_e => {SelectionChanged(item.Item, _e.Value);}" /><span>@item.Item</span>
</label>
<br />
}
</div>
</div>
<p>
@string.Join(",", SelectedItems)
</p>
@code {
private string? _filterText;
private bool toggleSelectBox = true;
[Parameter] public List<string> Items { get; set; } = new List<string>() { "item", "one 1", "two 3", "last 4" };
[Parameter] public List<string> SelectedItems { get; set; } = new();
[Parameter] public EventCallback<List<string>> SelectedItemChanged { get; set; }
public List<SelectedItem> FilteredItems { get; set; } = new();
public string? ButtonText { get; set; } = "Nothing selected";
public string? ButtonHoverTitleText { get; set; }
public string? FilterText
{
get => _filterText;
set
{
_filterText = value;
FilteredItems = new();
if (!string.IsNullOrWhiteSpace(_filterText))
{
foreach (var item in Items?.Where(x => x.ToLower().Contains(_filterText.ToLower())))
{
FilteredItems.Add(new SelectedItem { Item = item, IsSelected = SelectedItems.Contains(item) });
}
}
else
{
foreach (var item in Items)
{
FilteredItems.Add(new SelectedItem { Item = item, IsSelected = SelectedItems.Contains(item) });
}
}
}
}
protected override void OnInitialized()
{
Items?.ForEach(i => FilteredItems.Add(new SelectedItem { Item = i }));
base.OnInitialized();
}
public void ToggleSelectMenu()
{
toggleSelectBox = !toggleSelectBox;
ClearSearchText();
}
private void ClearSearchText() => FilterText = null;
public void SelectionChanged(string item, object checkedValue)
{
if ((bool)checkedValue)
{
if (!SelectedItems.Contains(item))
{
SelectedItems.Add(item);
}
}
else
{
if (SelectedItems.Contains(item))
{
SelectedItems.Remove(item);
}
}
ButtonHoverTitleText = SelectedItems.Any() ? string.Join(",", SelectedItems.Select(x => x)) : null;
ButtonText = SelectedItems.Any()
? (SelectedItems.Count == 1 ? ButtonHoverTitleText : $"{SelectedItems.Count} items selected")
: "Nothing selected";
SelectedItemChanged.InvokeAsync(SelectedItems);
}
public class SelectedItem
{
public string? Item { get; set; }
public bool IsSelected { get; set; }
}
}
item
如何被 one 1
替换 并且没有其他变化。换句话说,Blazor 检测更新 DOM 所需的数量并应用这些更改。这通常是您想要的,但是,当您动态更新一组项目(例如您的案例)时,这种行为是不可取的。 (注意:我在下面的输入中添加了一个 firstitem
属性只是为了表明它没有得到更新)。
答案是使用 @key
:
<label class="item" @key=item>
当您这样做时,只要您指定的键的 值 发生变化,DOM 就会更新。这将确保不仅更新 span 中的文本,而且更新输入元素。因此,根据经验,当您是列表中的 adding/removing/reordering 个元素时,请始终使用 @key
。另外,我上面没有提到,但是当您重新排序项目时,@key
起着至关重要的作用,因为它确保未更改的元素得以保留。
这里是link到documentation on @key。