runceel / ReactiveProperty

ReactiveProperty provides MVVM and asynchronous support features under Reactive Extensions. Target frameworks are .NET 6+, .NET Framework 4.7.2 and .NET Standard 2.0.
MIT License
903 stars 101 forks source link

ReactivePropertiesValidator で ICollection でもバリデーションできないでしょうか #493

Closed smasanor closed 4 weeks ago

smasanor commented 1 month ago

ICollection<IReactiveProperty>やICollection<ValidatableReactiveProperty>のような形でもバリデーションしたいです

runceel commented 1 month ago

EditContextEditFormModel プロパティに設定しているオブジェクトが以下のようになっているということですか?

class SomeModel
{
  public ICollection<ValidatableReactiveProperty<string>> SomeValues { get; set; }
}

そして、こんな感じでフォームで使っているという理解であっていますか?

<EditForm Model="_someModel" ...>
    ...
    @foreach (var p in _someValues)
    {
        <InputText @bind-Value="p.Value" />
    }
</EditForm>

@code {
  private SomeModel _someModel = new();
}
smasanor commented 1 month ago

回答ありがとうございます 例示も環境もなく、失礼しました Blazor でした ほぼご指摘通りですがご指摘の内容に沿って例示します

class SomeModel
{
    public ICollection<SomeEntity> SomeValues { get; set; }
}

class SomeEntity
{
    ValidatableReactiveProperty<string> vrpA { get; set; }
    ValidatableReactiveProperty<string?> vrpB { get; set; }
}

<FluentEditForm Model="_someModel" ...>
    <ReactivePropertiesValidator />
    <OreoreValidationSummary />

    <FluentDataGrid Items="_someModel.SomeValues" ...>
        <PropertyColumn>
            <FluentTextField @bind-Value="@context.vrpA.Value" />
        </PropertyColumn>
        <PropertyColumn>
            <FluentTextField @bind-Value="@context.vrpB.Value" />
        </PropertyColumn>
    </FluentDataGrid>
</EditForm>

@code {
  private SomeModel _someModel = new();
}

UI には FluentUI を使っています FluentDataGrid の IQueryable Items へ渡したコレクションの中の SomeEntity クラス内の ValidatableReactiveProperty が ReactivePropertiesValidator のバリデーションの対象になってほしいです ReactivePropertiesValidatorEditContextExtensions.EnableReactivePropertiesValidation を見たらモデルに直接あるプロパティだけでコレクションは対象外のように見えました バリデーションした結果の ValidationMessageStore は OreoreValidationSummary で参照できていました

やりたいこと

現状は FluentDataGrid の列を一つにして ValidatableReactiveProperty をコンポーネントにまとめてその中でも FluentEditForm と ReactivePropertiesValidator をつけることでエラー時に赤枠がつくようにしています 全体として EditForm が一つになったほうがスマートな構成になると思って書き込みました

runceel commented 1 month ago

任意の構造を持ったオブジェクトに対してバリデーションをさせるのは、汎用部品として用意するときりがない (コレクション内の要素のオブジェクトがさらにプロパティを持っていて、その中がコレクションだったら等) と思うので、以下のサンプルのように自分でカスタムの処理を書いた方が楽だと思います。

https://github.com/runceel/NestedRPObjValidationSample

このリポジトリを Clone して実行するとページに SomeValues に 3 件のデータを持たせた状態のフォームが表示されます。 送信ボタンを押すとバリデーションが走ります。Addボタンを押すとSomeValuesに要素を追加します。

runceel commented 1 month ago

このサンプルだと ValidationSummary が何故か表示されないですね…。 EditContext の GetValidationMessages() でメッセージをとって表示すれば表示はされるのですが、それは ValidationSummary の実装と同じ処理のはずなのですが…。

smasanor commented 1 month ago

わざわざサンプルを作成していただいてありがとうございます サンプルを参考に実装してみます DataAnnotationsValidator を追加したら ValidationSummary が表示されるようになりました

runceel commented 1 month ago

EditContextNotifyValidationStateChanged を呼び出すことで ValidationSummary に表示されるようになりました。 DataAnnotationsValidator は不要になります。

private void ValidationRequested(object? sender, ValidationRequestedEventArgs args)
{
    Verify.ThrowIfNull(_editContext);
    Verify.ThrowIfNull(_validationMessageStore);

    _validationMessageStore.Clear();

    foreach (var someValue in _model.SomeValues)
    {
        someValue.Validate();

        AddErrorIfHasError(someValue.A);
        AddErrorIfHasError(someValue.B);
    }

    _editContext.NotifyValidationStateChanged(); // これを追加
    StateHasChanged();
}
smasanor commented 1 month ago

調査してもらってありがとうございます DataAnnotationsValidator は必須だと思っていこんでいましたが、AddErrorIfHasError()があるのでなくてもエラーを設定できるんですね

runceel commented 4 weeks ago

DataAnnotationsValidator は内部で DataAnnotations 使ってバリデーションしてエラーを Add して NotifyValidationStateChanged とかを呼んでいるだけなので同じことをしてあげればエラーは表示されるようになります。

smasanor commented 4 weeks ago

ありがとうございました!