[Vuetify]入力フォームでValidationエラー時は送信ボタンを非活性にするぜっ

- クリーンアーキに沿った実装で、入力フォームのバリデーションにエラーが確認できる場合、送信ボタンを非活性化させます。
- なお、以下の記事では、バリデーションエラーがある場合、ボタンを押しても処理されないようにしています。それの後編みたいなものです。
 
https://qiita.com/ayumu_/items/bc6f2db1b00e02f64dc9

:::note warn
知れること：Vuetifyにおいて、バリデーションエラーがでているときに、ボタンを非活性化させる方法
知れないこと：クリーンアーキの詳細な説明
:::

## 目次	
1. 早速、非活性化させよう
1. クリーンアーキに基づきロジックはviewじゃなくてpresenterに移す
1. おわりに


## 1.早速、非活性化させよう
まず、ボタンを非活性化するためには、btnタグに、disabled属性を当てます。
disabled属性はHTMLの属性の1つで、これがtrueの場合、ボタンは無効化（クリックできない状態）されます(falseではない点に注意する）。

vuetifyにおけるbtnはv-btnなので、以下のように書けます。以下の例では、diabledに、presenter.sendButtonDisabled()を代入していますが、これがtrueだとボタンは非活性化されます。
```vuejs:
<v-btn color="primary" class="mx-auto my-2" @click="onLogin" :disabled="presenter.sendButtonDisabled()">ログイン</v-btn>
```

バリデーションエラーに基づき、disabledのbooleanを変動させたいので、v-formタグにv-model属性を当てます。
```vuejs:
<v-form :lazy-validation="true" ref="form"  v-model="presenter.state.validForm">
```

v-model はVue.jsのディレクティブ（指令）の一つで、主にフォーム入力とデータのバインディングを行うのに使用します。
v-modelを用いると、入力フィールドの値とVueインスタンスのデータプロパティが双方向バインディングされ、つまり、ユーザーが入力フィールドに入力した内容がデータプロパティに反映され、またデータプロパティが変更された場合には入力フィールドの表示も自動的に更新されます。

v-model="presenter.state.validForm"の記述があると、validFormの値がフォームの値にバインドされることを意味しますが、v-formコンポーネントのv-modelはバリデーションの結果（全ての入力が有効であるかどうかを表すブール値）を提供します。そのため、通常はv-formに対してv-modelを使用すると、バリデーションの結果をリアルタイムに取得・監視することができます（一方向のデータフローではなくなるという点に、注意が必要です）。



従って、ログインフォームのカードの実装は以下になります。
```vuejs:
<v-card width=400px class="mx-auto pa-4">
  <v-card-title class="text-center">ログイン</v-card-title>
  <v-form :lazy-validation="true" ref="form"  v-model="presenter.state.validForm">
    <v-text-field v-model="email" label="メールアドレス" :rules="emailRules"></v-text-field>
    <v-text-field v-model="password" label="パスワード" type="password" :rules="passwordRules"></v-text-field>
  </v-form>
  <v-card-action class="d-flex">
    <v-btn color="primary" class="mx-auto my-2" @click="onLogin" :disabled="presenter.sendButtonDisabled()">ログイン</v-btn>
  </v-card-action>
</v-card>
```
これで、バリデーションの値を監視して、状況に応じてボタンを活性化・非活性化する準備ができました。
今回、true・falseに変更するロジックは、presenterに持たせているので、次の章でそれを解説します。
ちなみに、presenterに持たせなくても非活性化は実現できるので、ここを書き換えればpresenter作らなくても試せます！
```vuejs:
v-model="presenter.state.validForm"
:disabled="presenter.sendButtonDisabled()"
```

## 2.クリーンアーキに基づきロジックはviewじゃなくてpresenterに書く

クリーンアーキテクチャに基づいて、presenterを用意します。
ViewとPresenterは、クリーンアーキテクチャを具体的に適用したMVP（Model-View-Presenter）パターンに関連します。これらの役割は以下の通りです：

- View：
これはUIを担当します。ユーザーからの入力を受け取り、Presenterに伝えます。また、Presenterからデータを受け取り、それを用いてUIを更新します。基本的にViewは"何を表示するか"だけを知り、"どうやって"は知りません。
- Presenter：
これはViewとModelの間の橋渡し役として機能します。Viewから受け取ったユーザー入力をビジネスロジック（Model）に伝え、その結果をViewに返します。Presenterは"どうやって"データを取得し、処理し、表示するかを知っています。

短く言うと、クリーンアーキテクチャはアプリケーションをきれいに整理するための方法で、ViewとPresenterはその中でユーザーインターフェースとビジネスロジックをつなぐ役割を果たします。

なので、viewファイル内のscript内と、presenterファイルにviewへの入力をpresenterでロジックを適用するためのコードを記述していきます。

```typescript:viewファイル(index.ts)
<script lang="ts">
import { LoginController } from '../../controllers/LoginController'
import { LoginPresenter } from '../../presenters/LoginPresenter' //ここにロジックを書く
export default defineComponent({
  data() {
    return {
      email: '',
      password: '',
      presenter: new LoginPresenter(),
      emailRules: [
      v => !!v || 'メールアドレスは必須です',
      v => /.+@.+\..+/.test(v) || '有効なメールアドレスを入力してください',
      ],
      passwordRules: [
        v => !!v || 'パスワードは必須です',
        v => v.length >= 8 || 'パスワードは8文字以上である必要があります',
      ]
    }
// これより下の記述は省略

```

Vue.jsでは、各コンポーネントの内部状態はdataオプションで管理されます。dataオプションは関数で、その戻り値が各インスタンスの初期状態になります。各インスタンスが作成されるたびにdata関数が新たに呼び出され、新たなデータオブジェクトが返されます。これにより各インスタンスがそれぞれ独立したデータを持つことができます。

returnの中では、presenterというプロパティを定義しています。
presenterはLoginPresenterの新しいインスタンスを作成して格納するために使われます。これにより、LoginPresenterのメソッドをこのコンポーネント内で利用できるようになります。このpresenterインスタンスは、特定のビューの表示ロジックをカプセル化するために使用され、MVP（Model-View-Presenter）パターンにおけるPresenterの役割を果たします。

```typescript:LoginPresenter.ts
export interface LoginPresenterState {
  enableAlert: boolean,
  validForm: boolean
}
export class LoginPresenter {
  readonly state: LoginPresenterState
  constructor() {
    const enableAlert: boolean = false
    const validForm = false //初期値はfalse。
    this.state = { enableAlert, validForm }
  }

//firebaseで認証に失敗したら、アラートだすようにしている。今回は関係ない。
  public setAlert(enableAlert: boolean) {
    this.state.enableAlert = enableAlert
  }
  
//ここでdiabledの値を変動させる。(trueの時はバリデーションエラーが発生していることを表すことに注意する)
  public sendButtonDisabled(): boolean {
    return !this.state.validForm
  }
}

```

これで、viewの入力に基づいて、presetnerでロジックを適用することができるので、バリデーションにエラーがあるかどうかを監視して、その値に基づき送信ボタンを非活性化することができるようになりました。

- バリデーションエラー時にはボタンを非活性
![スクリーンショット 2023-07-19 21.30.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754596/7458e7b3-661d-17a5-96a6-52efe4a92013.png)

- バリデーションエラーになってないのでボタンを活性化
![スクリーンショット 2023-07-19 21.30.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/754596/c043d13e-0322-33a0-b168-7bdb1e8165ae.png)

グレーアウトさせた方が、より非活性感出そうですね笑

## おわりに
- 正直結構詰まったけど、その分ちゃんと理解できました。
- 遠回りが１番の近道🤞