放浪軍師のアプリ開発局

VTuberみたいなアプリケーション夏狂乱など、自由気ままにアプリを開発したりしています。他にもいろいろやってます。尚、このブログはわからないところを頑張って解決するブログであるため、正しい保証がありません。ご注意ください。

Blazor の InputSelect コンポーネントについて学ぶ

最近どうにもやる気というものが削がれてしまっていて、しばらく自主勉強を怠っていました放浪軍師です。今回も大した記事ではないのですが備忘録として残しておきます。何気に苦戦したのよね。

Blazor の InputSelect コンポーネント

さて、今回のお題はタイトルにもあるように InputSelect コンポーネントについてになります。このコンポーネントは所謂ドロップダウンリストで、クリックするとリストが開いてそのうちの一つを選択するというようなありがちかつよく使うコンポーネントになります。ただこれがまた微妙に癖のある作りをしていて、業務で使おうとして中々に苦戦を強いられたためここに記録として残しておきます。

環境

Microsoft Visual Studio Community 2022 (64 ビット) - Current Version 17.0.0 Microsoft.AspNetCore.Components.WebAssembly 6.0.0

GitHub

参考になれば幸い。 github.com

単純に文字列のリストから一つを選ぶ

f:id:roamschemer:20211119015950g:plain

まずは基本的な使い方。文字列のリストを作っておいて、それから一つを選択して文字列プロパティに連動させるというもの。コードはこちら。

<EditForm Model="@dummyModel">
    <InputSelect @bind-Value="Name">
        @foreach (var name in _nameSelectList) {
            <option>@name</option>
        }
    </InputSelect>
</EditForm>
@code {

    private DummyModel dummyModel = new();
    public class DummyModel{}

    public string Name { get; set; } = "";
    private List<string> _nameSelectList = new() { "放浪軍師", "夏狂乱", "漆原 鎌足", "鮫月かこい","遥ノ音ハル" };

    protected override void OnInitialized()
    {
        Name = _nameSelectList.Last();
    }
}

まぁこれは単純で、連動したいプロパティを @bind-Value で指定し、選択用のリストは <option> で作成する。簡単ですね。それよりも InputSelect などの Input 系コンポーネントEditForm コンポーネント内でないと使えないという事の方が重要です。覚えておきましょう。ハマります。

人物クラスから名前のみをリストで表示するが、取得するのは名前ではなくID

f:id:roamschemer:20211119020408g:plain

人物クラスのリストがあって、その中のIDが欲しいという場合。あると思います。そこで素直に先ほどのような書き方をしてしまうと、人物クラスのIDが並ぶリストが出来上がってしまいます。確かに欲しいのはIDですがリストに並ぶのは人物の名前であって欲しいですよね?そういう場合は以下のように記述します。

<EditForm Model="@dummyModel">
    <InputSelect @bind-Value="PersonId">
        @foreach (var person in _personSelectList) {
            <option value="@person.Id">@person.Name</option>
        }
    </InputSelect>
</EditForm>
private DummyModel dummyModel = new();
public class DummyModel{}

public int PersonId { get; set; }

private List<Person> _personSelectList = new()
{
    new(){Id=0,Name="放浪 軍師", Profession="エンジニア", Remarks="最近ブログ更新さぼっててごめんなさい"},
    new(){Id=1,Name="夏狂 乱", Profession="抽選アプリケーション", Remarks="AIではない方法で人格を持たせる事を目指すアプリ"},
    new(){Id=2,Name="漆原 鎌足", Profession="退魔師", Remarks="清楚カメラめちゃ便利なのでもうちょっと有名になっていい!"},
    new(){Id=3,Name="鮫月かこい", Profession="妖精", Remarks="生きているだけで偉いと褒めてもらおう!"},
    new(){Id=4,Name="遥ノ音ハル", Profession="弾き語り", Remarks="エモい弾き語り配信は必見!"},
};

protected override void OnInitialized()
{
    PersonId = _personSelectList.Last().Id;
}

public class Person {
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string Profession { get; set; } = "";
    public string Remarks { get; set; } = "";
}

ポイントは<option value="@person.Id">@person.Name</option>の部分。value 属性にはバインディングしたい値を記述し、中身にリストに表示したい値を記述します。簡単ですね。でも探すだけで結構時間かかったりするものだよ?いやほんとにさ…Blazor の記事増えてくれー

人物クラスから人物クラスにハードコピー

f:id:roamschemer:20211119020037g:plain

人物クラスのIdだけじゃなくて、nameからなにやら全部ぶっこぬきたいという事もあるかと思います。その場合はこちら。

<EditForm Model="@dummyModel">
@*          
    このやり方はエラーになります。できそうなんだけどね。
    <InputSelect @bind-Value="SelectPerson">
        @foreach (var person in _personSelectList) {
            <option value="@person">@person.Name</option>
        }
    </InputSelect>
*@
    <InputSelect @bind-Value="_selectPersonId" @oninput="OnPersonInput">
        @foreach (var person in _personSelectList) {
            <option value="@person.Id">@person.Name</option>
        }
    </InputSelect>
</EditForm>
private DummyModel dummyModel = new();
public class DummyModel{}

public Person SelectPerson { get; set; } = new();
private int _selectPersonId;


private List<Person> _personSelectList = new()
{
    new(){Id=0,Name="放浪 軍師", Profession="エンジニア", Remarks="最近ブログ更新さぼっててごめんなさい"},
    new(){Id=1,Name="夏狂 乱", Profession="抽選アプリケーション", Remarks="AIではない方法で人格を持たせる事を目指すアプリ"},
    new(){Id=2,Name="漆原 鎌足", Profession="退魔師", Remarks="清楚カメラめちゃ便利なのでもうちょっと有名になっていい!"},
    new(){Id=3,Name="鮫月かこい", Profession="妖精", Remarks="生きているだけで偉いと褒めてもらおう!"},
    new(){Id=4,Name="遥ノ音ハル", Profession="弾き語り", Remarks="エモい弾き語り配信は必見!"},
};

protected override void OnInitialized()
{
    SelectPerson.Update(_personSelectList.Last());
    _selectPersonId = SelectPerson.Id;
}

private void OnPersonInput(ChangeEventArgs e)
{
    if (e.Value == null) return;
    var selected_id = Int32.Parse(e.Value.ToString());
    var person = _personSelectList.FirstOrDefault(x => x.Id == selected_id);
    if (person == null) return;
    SelectPerson.Update(person);
}

public class Person {
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string Profession { get; set; } = "";
    public string Remarks { get; set; } = "";
    public void Update(Person person)
    {
        Id = person.Id;
        Name = person.Name;
        Profession = person.Profession;
        Remarks = person.Remarks;
    }
}

単純にリストから選ばれた値やそれに連なる value 属性をバインドさせるだけでなく、さらに何かやりたい場合は<InputSelect @bind-Value="_selectPersonId" @oninput="OnPersonInput">のように、 @opinput 属性でイベントを発火させ、 C# 側で愚直に書く事になります。ただそれにしても、今回のようにハードコピーの場合ならコメントにも書いているようにできてもいいのになぁという感じがしますね。なんかいい書き方があるのかもしれないのですが発見できませんでした。情報求ム。

まとめ

InputSelect の使い方完全に理解した。