RxProperty で最初にボタンを押すまでバリデーションしない方法

こちらの件です。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">雑にかいた<br>すでに議論され尽くされてる感はあるので他の人の考えも聞いてみたいです<br><br>フォームバリデーションと送信ボタンの状態の最適解 - Konifar&#39;s ZATSU <a href="https://t.co/YvPhkM05gg">https://t.co/YvPhkM05gg</a></p>&mdash; こにふぁー (@konifar) <a href="https://twitter.com/konifar/status/957806094713217024?ref_src=twsrc%5Etfw">2018年1月29日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 私見

自分がよく関わっている業務アプリの世界では、UIパーツの非活性(disabled)はあまり好まれません（度々 Reject されます）。その一番の理由は、「なぜボタンが押せないのかが分からない」ことです（利用ユーザーがIT機器に疎い人が多いのでそれだけでパニクることも）。ならばその理由を画面上のどこかに表示してやろうと策を練るよりも、単純に「エラーがあったら DialogBox でその理由と対処方法を表示させたろう」という方法が、実装コストが最も低く、業務アプリ利用者にもわかりやすい、というのが経験・感覚的にあります。

しかしそれではあまりにもモダンでないとも感じるので、個人的には、次点として採用したいのは↓の手法です。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">最初の１回は間違いがあってもとりあえず送信ボタンを押させてあげて、その後はリアルタイムチェックを有効にする派 / “フォームバリデーションと送信ボタンの状態の最適解 - Konifar&#39;s ZATSU” <a href="https://t.co/yWaszGdAkH">https://t.co/yWaszGdAkH</a></p>&mdash; なかざん＠ジャバスクリプトニュービー (@Nkzn) <a href="https://twitter.com/Nkzn/status/958138755298111488?ref_src=twsrc%5Etfw">2018年1月30日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 実際にやってみた

実際にそれを実装してみました、 [RxProperty](https://github.com/k-kagurazaka/rx-property-android) を使って。
最近ちょうどフォームバリデーションのサンプルを実装した例↓

* [RxProperty でイケてる入力フォームをもっとスッキリ実装する - Qiita](https://qiita.com/amay077/items/fc075d4e777f8c7a2816)

があるので、これをカスタマイズしてみます。

## できあがり

こんな感じのものを作ります。

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/8227/a770dd8c-9fc8-f1f4-b220-156e1ff4d1f2.gif)

## 修正箇所

RxProperty は、ViewModel が公開するプロパティとして利用するものですが、それに Validator を持たせることができます。

```kotlin
val nickname = RxProperty<String>("")
    .setValidator {
        if (it.length < 2) "ニックネームは2文字以上にしてください" else null }
```

のようにプロパティの定義と共に設定すれば、画面表示直後からValidatorは作動しますが、ボタンが押されるまではバリデーションしないのであれば、設定のタイミングを遅らせるだけです。

```kotlin
val nickname = RxProperty<String>("")
val nickNameValidator : (String)->String? = {
    if (it.length < 2) "ニックネームは2文字以上にしてください" else null }

private var isFirstExecute = true

/** 登録ボタンを押したとき */
val register = canRegistration.toRxCommand<NoParameter>().apply { this.subscribe {
    // 最初にボタンが押されたときに、Validator を設定する(フラグを使っているのがなんかダサい)
    if (isFirstExecute) {
        isFirstExecute = false

        nickname.setValidator(nickNameValidator, true)

        if (!this.canExecute()) { return@subscribe }
    }

    _toast.postValue("RegistrationCompleteActivity へ移動するよ")
}
```

* 完全な修正差分はこちら → https://github.com/amay077/RxPropertyFormSample/commit/b743a4b187f7c05a1847637c2bfc4371a47a4ab3?diff=split
* 修正したサンプルのコードはこちら → https://github.com/amay077/RxPropertyFormSample/tree/20180131_skip_valid_on_first_time

ちなみに

```kotlin
val nickname = RxProperty<String>("")
    .setValidator ({
        if (it.length < 2) "ニックネームは2文字以上にしてください" else null },
        false)
```

とする(``setValidator`` の第2引数を ``false`` にする)と、画面表示直後の初回のバリデーションを行わない、すなわち、「最初から登録ボタンは押せるが、リアルタイムにバリデーションも行う」ようにもできます。

RxProperty に限らないんですけど、プロパティ／バリデータ／活性非活性判定・変更処理が適切に分離されていれば、それらの組み合わせを変えるだけなのでいかようにでもできますね。急な仕様変更にも割と容易に対応できるということで。