Close
Angular React Web Components Blazor
Open Source

Blazor Query Builder の概要

Ignite UI for Blazor Query Builder は、指定したデータ セットに対する複雑なデータ フィルタリング クエリを構築できる豊富な UI を提供します。このコンポーネントでは、式ツリーを作成し、各フィールドのデータ型に応じたエディターと条件リストを使用して、式の間に AND / OR 条件を指定できます。作成した式ツリーは、その後バックエンドでサポートされる形式のクエリへ簡単に変換できます。

Blazor Query Builder の使用を開始するには

IgbQueryBuilder の使用を開始するには、まず次のコマンドを実行して Ignite UI for Blazor パッケージをインストールします:

dotnet add package IgniteUI.Blazor --version 26.1.51 (June 2026)

Program.cs ファイルで Query Builder モジュールを登録します:

builder.Services.AddIgniteUIBlazor(typeof(IgbQueryBuilderModule));

プロジェクト構成に応じて、対応するスタイルも参照する必要があります。

<link href="_content/IgniteUI.Blazor/themes/light/bootstrap.css" rel="stylesheet" />

Blazor Query Builder の使用

初期の式ツリーが設定されていない場合は、まずエンティティと、クエリが返す対象フィールドを選択します。その後、条件またはサブグループを追加できます。

条件を追加するには、フィールド、フィールドのデータ型に基づく演算子、そして演算子が単項でない場合は値を選択します。In および Not In 演算子を使用すると、単に値を指定する代わりに、別のエンティティに対する条件を持つ内部クエリを作成できます。条件を確定すると、その条件情報を含むチップが表示されます。チップをクリックまたはホバーすると、その条件を編集したり、直後に別の条件やグループを追加したりできます。

各グループの上にある AND または OR ボタンをクリックすると、グループの種類を変更したり、内部の条件をグループ解除したりするためのメニューが開きます。

各条件は特定のエンティティの特定フィールドに関連付けられているため、エンティティを変更すると、事前に設定された条件およびグループはすべてリセットされます。

このコンポーネントは、Entities プロパティに、エンティティ名とそのフィールド配列を記述した配列を設定することで使用できます。各フィールドは、名前とデータ型によって定義されます。フィールドを選択すると、データ型に応じた対応する演算子が自動的に割り当てられます。 また Query Builder には ExpressionTree プロパティがあります。これを使用すると、コントロールの初期状態を設定したり、ユーザーが指定したフィルタリング ロジックにアクセスしたりできます。

<IgbQueryBuilder @ref="queryBuilder"
    Entities="Entities"
    ExpressionTree="ExpressionTree"
    ExpressionTreeChangeScript="WebQueryBuilderExpressionTreeChange">
</IgbQueryBuilder>

@code {
    private static readonly IgbFieldType[] OrderFields =
    [
        new() { Field = "orderId", DataType = GridColumnDataType.Number },
        new() { Field = "customerId", DataType = GridColumnDataType.String },
        new() { Field = "orderDate", DataType = GridColumnDataType.Date }
    ];

    private static readonly IgbEntityType[] Entities =
    [
        new() { Name = "Orders", Fields = OrderFields }
    ];

    private static readonly IgbExpressionTree ExpressionTree = new()
    {
        FilteringOperands = [],
        Operator = FilteringLogic.And,
        Entity = "Orders"
    };

    private IgbQueryBuilder queryBuilder;
}

IgbExpressionTree はバインド可能なプロパティです。つまり、ExpressionTreeChangeScript を使用して、エンドユーザーが条件を作成、編集、削除して UI を変更したときに通知を受け取ることができます。

// In JavaScript
igRegisterScript("WebQueryBuilderExpressionTreeChange", (evtArgs) => {
    const expressionTree = evtArgs.detail;
    console.log("Expression tree changed:", expressionTree);
}, false);

式のドラッグ

条件チップは、マウスのドラッグ アンド ドロップまたはキーボードによる並べ替えで簡単に位置を変更できます。これにより、ユーザーはクエリ ロジックを動的に調整できます。

  • チップをドラッグしても、変更されるのは位置のみであり、条件や内容自体は変更されません。
  • チップはグループおよびサブグループ間でもドラッグできます。たとえば、式のグループ化やグループ解除はこのドラッグ機能によって実現できます。 既存の条件をグループ化するには、まず「グループ追加」ボタンで新しいグループを追加します。その後、必要な式をドラッグしてそのグループへ移動できます。グループ解除するには、すべての条件を現在のグループ外へドラッグします。最後の条件が移動されると、そのグループは削除されます。

あるクエリ ツリーのチップを別のクエリ ツリーへドラッグすることはできません。たとえば、親クエリから内部クエリへ、またはその逆への移動はできません。

Animated Example of Query Builder Drag and Drop using the Mouse

キーボード操作

キー操作

  • Tab / Shift + Tab - 次 / 前のチップ、ドラッグ インジケーター、削除ボタン、「式を追加」ボタンへ移動します。
  • Arrow Down/Arrow Up - チップのドラッグ インジケーターにフォーカスがある場合、チップを上下に移動できます。
  • Space / Enter - フォーカスされた式が編集モードに入ります。チップの移動中であれば、新しい位置を確定します。
  • Esc - チップの並べ替えをキャンセルし、元の位置に戻します。

キーボードによる並べ替えは、マウスのドラッグ アンド ドロップと同じ機能を提供します。チップを移動した後、ユーザーは新しい位置を確定するか、並べ替えをキャンセルする必要があります。

Animated Example of Keyboard Drag and Drop Using the Ignite UI for Angular Query Builder

テンプレート

Ignite UI for Blazor Query Builder では、コンポーネントのヘッダーおよび検索値に対してテンプレートを定義できます:

ヘッダー テンプレート

既定では、IgbQueryBuilder のヘッダーは表示されません。ヘッダーを定義するには、IgbQueryBuilderHeader コンポーネントを Query Builder 内に追加する必要があります。

<IgbQueryBuilder Entities="Entities" ExpressionTree="ExpressionTree">
    <IgbQueryBuilderHeader Title="My Query Builder"></IgbQueryBuilderHeader>
</IgbQueryBuilder>

検索値テンプレート

Blazor の場合は、SearchValueTemplateScript プロパティを使用して、igRegisterScript で登録したクライアント側関数を参照します。

SearchValueTemplate を使用する場合は、エンティティ内のすべてのフィールド型に対するテンプレートを提供する必要があります。そうしないと Query Builder は正しく動作しません。特定のカスタム テンプレートでカバーされないフィールドや条件を処理する既定 / フォールバック テンプレートを必ず実装してください。これがないと、ユーザーはそれらのフィールドの条件を編集できません。

<IgbQueryBuilder @ref="queryBuilder"
    Entities="Entities"
    ExpressionTree="ExpressionTree"
    ExpressionTreeChangeScript="WebQueryBuilderExpressionTreeChange"
    SearchValueTemplateScript="SearchValueTemplate">
    <IgbQueryBuilderHeader Title="Query Builder Template Sample"></IgbQueryBuilderHeader>
</IgbQueryBuilder>
// In JavaScript
igRegisterScript("SearchValueTemplate", (ctx) => {
    const field = ctx.selectedField?.field;
    const condition = ctx.selectedCondition;
    const matchesEqualityCondition = condition === "equals" || condition === "doesNotEqual";

    if (!ctx.implicit) {
        ctx.implicit = { value: null };
    }

    if (field === "Region" && matchesEqualityCondition) {
        return buildRegionSelect(ctx);
    }

    if (field === "OrderStatus" && matchesEqualityCondition) {
        return buildStatusRadios(ctx);
    }

    if (ctx.selectedField?.dataType === "date") {
        return buildDatePicker(ctx);
    }

    if (field === "RequiredTime") {
        return buildTimeInput(ctx);
    }

    return buildDefaultInput(ctx, matchesEqualityCondition);
}, false);

以下は、各エディター タイプごとに 1 つのテンプレート例を示したものです:

Region Select の例:

// Field definition
new() { Field = "Region", DataType = GridColumnDataType.String }
// In JavaScript
// Template
function buildRegionSelect(ctx) {
    const currentValue = ctx?.implicit?.value;
    const changeHandler = (event) => {
        const value = event && event.detail ? event.detail.value : null;
        ctx.implicit.value = value;
    };

    return html`
      <igc-select
        placeholder="Region"
        .value=${currentValue}
        @igcChange=${changeHandler}>
        ${regionOptions.map(option => html`
          <igc-select-item value=${option.value}>${option.text}</igc-select-item>
        `)}
      </igc-select>
    `;
}

Status Radio Group の例:

// Field definition
new() { Field = "OrderStatus", DataType = GridColumnDataType.Number }
// In JavaScript
// Template
function buildStatusRadios(ctx) {
    const implicitValue = ctx?.implicit?.value;
    const currentValue = implicitValue == null ? '' : implicitValue.toString();

    const changeHandler = (event) => {
        const value = event && event.detail ? event.detail.value : undefined;
        if (!value || ctx.implicit.value === value) {
            return;
        }
        ctx.implicit.value = value;
    };

    return html`
      <igc-radio-group
        style="gap: 5px;"
        .alignment=${"horizontal"}
        .value=${currentValue}
        @igcChange=${changeHandler}>
        ${statusOptions.map(option => html`
          <igc-radio
            name="status"
            value=${option.value}
            ?checked=${option.value.toString() === currentValue}>
            ${option.text}
          </igc-radio>
        `)}
      </igc-radio-group>
    `;
}

Date Picker の例:

// Field definition
new() { Field = "OrderDate", DataType = GridColumnDataType.Date }
// In JavaScript
// Template
function buildDatePicker(ctx) {
    const implicitValue = ctx.implicit?.value;
    const currentValue = implicitValue instanceof Date
        ? implicitValue
        : implicitValue
            ? new Date(implicitValue)
            : null;

    const allowedConditions = ['equals', 'doesNotEqual', 'before', 'after'];
    const isEnabled = allowedConditions.includes(ctx.selectedCondition ?? '');

    return html`
      <igc-date-picker
        .value=${currentValue}
        .disabled=${!isEnabled}
        @click=${(event) => (event.currentTarget).show()}
        @igcChange=${(event) => {
            ctx.implicit.value = event.detail;
        }}>
      </igc-date-picker>
    `;
}

Time Input の例:

// Field definition
new()
{
    Field = "RequiredTime",
    DataType = GridColumnDataType.Time,
    DefaultTimeFormat = "hh:mm tt"
}
// In JavaScript
// Template
function buildTimeInput(ctx) {
    const currentValue = this.normalizeTimeValue(ctx.implicit?.value);
    const allowedConditions = ['at', 'not_at', 'at_before', 'at_after', 'before', 'after'];
    const isDisabled = ctx.selectedField == null || !allowedConditions.includes(ctx.selectedCondition ?? '');

    return html`
      <igc-date-time-input
        .inputFormat=${"hh:mm tt"}
        .value=${currentValue}
        .disabled=${isDisabled}
        @igcChange=${(event) => {
            const picker = event.currentTarget;
            ctx.implicit.value = picker.value;
        }}>
        <igc-icon slot="prefix" name="clock" collection="material"></igc-icon>
      </igc-date-time-input>
    `;
}

Default Input テンプレートの例:

// Field definitions for string, number, and boolean types
new() { Field = "ShipCountry", DataType = GridColumnDataType.String }
new() { Field = "OrderID", DataType = GridColumnDataType.Number }
new() { Field = "IsRushOrder", DataType = GridColumnDataType.Boolean }
// In JavaScript
// Template that handles all these types
function buildDefaultInput(ctx, equalityCondition) {
    const selectedField = ctx.selectedField;
    const dataType = selectedField?.dataType;
    const isNumber = dataType === 'number';
    const isBoolean = dataType === 'boolean';

    const placeholder = ctx.selectedCondition === 'inQuery' || ctx.selectedCondition === 'notInQuery'
        ? 'Sub-query results'
        : 'Value';

    const currentValue = typeof ctx.implicit?.value === 'object' && (ctx.implicit.value && 'text' in ctx.implicit.value)
        ? matchesEqualityCondition ? ctx.implicit.value.text : ''
        : ctx.implicit?.value;

    const inputValue = currentValue == null ? '' : currentValue;
    const disabledConditions = ['empty', 'notEmpty', 'null', 'notNull', 'inQuery', 'notInQuery'];
    const isDisabled = isBoolean || selectedField == null || disabledConditions.includes(ctx.selectedCondition ?? '');

    return html`
      <igc-input 
        .value=${inputValue}
        ?disabled=${isDisabled}
        placeholder=${placeholder}
        type=${isNumber ? 'number' : 'text'}
        @input=${(event) => {
            const target = event.target;
            ctx.implicit.value = isNumber
                ? target.value === '' ? null : Number(target.value)
                : target.value;
        }}>
      </igc-input>
    `;
}

フォーマッター

条件が編集モードではないときにチップ内へ表示される検索値の見た目を変更するには、fields 配列に formatter 関数を設定します。検索値には次のように value 引数からアクセスできます:

private static readonly IgbFieldType[] OrderFields =
[
    new() { Field = "OrderID", DataType = GridColumnDataType.Number },
    new() { Field = "ShipCountry", DataType = GridColumnDataType.String },
    new()
    {
        Field = "OrderDate",
        DataType = GridColumnDataType.Date,
        PipeArgs = new IgbFieldPipeArgs { Format = "MMM d, y" }
    },
    new() { Field = "Region", DataType = GridColumnDataType.String }
];

デモ

この例では、Blazor Query Builder コンポーネントのヘッダーおよび検索値に対するテンプレート機能と formatter 機能を確認できます。

API リファレンス

IgbQueryBuilder IgbQueryBuilderHeader

その他のリソース

コミュニティは活発で、新しいアイデアをいつでも歓迎しています。