Как получить двустороннее связывание в Blazor с компонентом SELECT? - PullRequest
0 голосов
/ 14 февраля 2020

Я искал SO, и есть несколько вопросов и ответов на эту тему, но ни один не решает эту конкретную проблему c.

Я пытаюсь написать компонент Blazor , который использует <select...>, чтобы обновить переменную, переданную от родителя.

Я хочу щелкнуть раскрывающийся список (выбрать / опции), выбрать параметр и автоматически обновить переменную в родительском элементе.

Я могу заставить его работать, если я не использую компонент. Но единственный способ заставить его работать с компонентом - это использовать кнопку в компоненте (см. Кнопку в коде компонента). Но в конце концов будет несколько компонентов, вызванных из одного и того же родителя. Было бы непрактично иметь отдельную кнопку для каждого компонента. Таким образом, кнопка должна go прочь.

@ bind использует параметр "onchange" для выбора, так что он даже не скомпилируется.

I пробовал CascadingValue / CascadingParam , но это также не обновляет родительское значение (если я не использую кнопку ...)

Вот родитель:

@page "/test"
@page "/test/{param}"

@namespace ComponentTest.Pages

@using System.Web
@using ComponentTest.Pages

<h3>Test Page</h3>

<div class="border col-3">
    <h4>Parent</h4>
    <hr />
    <label for="parentValue">Parent Value</label>
    <input id="parentValue" @bind="@param"/>
</div>
<div class="col-3">&nbsp</div>
<div class="border col-3">
    <h4>Component Select</h4>
    <hr />
    <SelectComponent @bind-Item="@param"/>
</div>

@code {
    [Parameter]
    public string param { get; set; }
}

И вот компонент:

@using System.Collections.Generic
@using System.Web

<div class="form-group">
    <p>Component Value = @Item</p>
    <label for="Site" class="font-weight-bold form-check-label">List</label>
    @if (list == null)
    {
        <input id="Item" class="form-control-sm" @bind="@Item" />
    }
    else
    {
        <select id="Item" class="form-control-sm" @bind="@Item">
            @foreach (var l in list)
            {
                @if (Item != null && String.Equals(l, Item, StringComparison.OrdinalIgnoreCase))
                {
                    <option selected value="@l">@l</option>
                }
                else
                {
                    <option value="@l">@l</option>
                }
            }
        </select>
    }
</div>
<button @onclick="UpdateParentsItem" class="btn btn-primary">Update Parent</button>
@code {
    public IEnumerable<string> list = new List<string>()
{
        "Item 1",
        "Item 2",
        "Item 3",
        "Item 4",
        "Item 5"
    };

    [Parameter]
    public string Item { get; set; }

    [Parameter]
    public EventCallback<string> ItemChanged { get; set; }

    private async Task UpdateParentsItem()
    {
        await ItemChanged.InvokeAsync(Item);
    }
}

Я следовал нескольким groovy примерам реализации событий, но ни один из найденных мною действительно не решил проблему.

Ответы [ 2 ]

1 голос
/ 17 февраля 2020

Я даю кредит @ enet за ответ, но вот мое полное решение с включенным его (ее?) Битом и с дополнительной проверкой проверки:

Test.razor (Parent)

@page "/test"
@page "/test/{param}"

<h3>Select Component Test</h3>

<div class="border col-2">
    <h4>Parent</h4>
    <hr />
    <div class="form-group">
        <label for="parentValue">Parent Value</label>
        <input id="parentValue" type="text" class="form-control" @bind="@param" />
    </div>
</div>
<div class="col-3">&nbsp</div>
<div class="border col-2">
    <h4>Component</h4>
    <hr />
    <SelectComponent @bind-Item="@param" />
</div>
<hr />
<ul>Links
    <li><a href="/test/item 1">item 1</a></li>
    <li><a href="/test/ITEM 2">ITEM 2</a></li>
    <li><a href="/test/iTeM 3">iTeM 3</a></li>
    <li><a href="/test/ItEm 4">ItEm 4</a></li>
    <li><a href="/test/item 99">item 99</a></li>
</ul>
@code
{
    [Parameter]
    public string param { get; set; }
}

SelectComponent.razor

<!-- Select Component -->
<div class="form-group">
    @if (message != null)
    {
        <p class="text-warning">@message</p>
    }
    <label for="Item" class="form-check-label">Item</label>
    @if (list == null)
    {
        <input id="Item" class="form-control-sm" @bind="@Item" />
    }
    else
    {
        <select id="Item" class="form-control-sm" @bind="@Item">
            @foreach (var l in list)
            {
                <option value="@l">@l</option>
            }
        </select>
    }
</div>
@code {
    private string message;

    public IEnumerable<string> list = new List<string>()
    {
        "Item 1",
        "Item 2",
        "Item 3",
        "Item 4",
        "Item 5"
    };

    private string item { get; set; }

    [Parameter]
    public string Item
    {
        get { return item; }
        set
        {
            if (item != value)  // skip if it's the same string
            {
                if (list == null || list.Count() == 0)
                {
                    item = value;
                }
                else
                {
                    // validate the new item value against my list
                    foreach (var l in list)
                    {
                        if (value != null && String.Equals(l, value, StringComparison.OrdinalIgnoreCase))
                        {
                            item = l;   // match exact case to my list so "selected" option works properly
                            message = null;
                        }
                    }

                    // if there's no match, clear the item
                    if (!String.Equals(item, value, StringComparison.OrdinalIgnoreCase))
                    {
                        item = null;
                        message = "Invalid Item";
                    }
                }

                // make sure there is a parent
                if (ItemChanged.HasDelegate)
                {
                    // this is the magic that updates the parent
                    ItemChanged.InvokeAsync(item);
                }
            }
        }
    }

    [Parameter]
    public EventCallback<string> ItemChanged { get; set; }
}

Я буду использовать его в качестве шаблона для нескольких раскрывающихся списков проверки формы на основе Entity Framework.

1 голос
/ 14 февраля 2020

Этот фрагмент кода работает. Попробуйте ... Запустите код и начните вводить слово Item 3 . При вводе текста вы увидите текст, отображаемый как из дочернего компонента, так и из родительского компонента. Когда вы закончите sh, набрав Item 3, вы заметите, что текст Item 3 отображается в поле со списком. Выберите элемент из поля со списком, и он появится в поле ввода родителя.

SelectComponent.razor

    <div style="border:solid 1px red">
     <select id="Item" class="form-control-sm" @bind="@Item">
        @foreach (var l in list)
        {
            @if (Item != null && String.Equals(l, Item, 
             StringComparison.OrdinalIgnoreCase))
            {
                <option selected value="@l">@l</option>
            }
            else
            {
                <option value="@l">@l</option>
            }
        }
    </select>

 </div>
 <p>@Item</p>

 @code {
  public IEnumerable<string> list = new List<string>()
 {
    "Item 1",
    "Item 2",
    "Item 3",
    "Item 4",
    "Item 5"
 };


 private string item { get; set; } 

 [Parameter]
 public string Item
 {
    get { return item; }
    set
    {
        if (item != value)
        {
            item = value;
            if (ItemChanged.HasDelegate)
            {
                ItemChanged.InvokeAsync(value);
            }
        }
    }
  }

 [Parameter]
 public EventCallback<string> ItemChanged { get; set; }
 }

Test.razor

@page "/test"

<div>

<label for="parentValue">Parent Value</label>

<input type="text" id="parentValue" @bind="@Param" 
     @bind:event="oninput" />

<p>@Param</p>
</div>

<div>

<SelectComponent @bind-Item="@Param"/>
</div>

@code {
  [Parameter]
public string Param { get; set; } = string.Empty;

}

Хорошо удачи ... Если что-то не понятно, не стесняйтесь спрашивать ...

...