Help us understand the problem. What is going on with this article?

Riot.js フォームの扱い

Riot.js

Riotでのフォームの扱い方を紹介します。

DOM要素に直接アクセス

おそらく一番簡単でわかりやすい方法です。フォームがちょっとでも複雑になるとコードがカオスになりがちなので実務ではあまり使いません。
サッと簡単なフォームを作る時にはこれで十分ですね。

<app-form>
  <form onsubmit="{ handleSubmit }">
    <input type="email" name="email" placeholder="メールアドレス" />
    <input type="password" name="password" placeholder="パスワード" />
    <input type="checkbox" id="password_visible" onchange="{ handleCheckChange }">
    <label for="password_visible">パスワード表示</l1abel>
    <button>送信</button>
  </form>
  <script>
    export default {
      handleCheckChange(e) {
        this.$('[name=password]').type = e.target.checked ? 'text' : 'password'
      },

      handleSubmit(e) {
        e.preventDefault()
        const email = this.$('[name=email]').value
        const password = this.$('[name=password]').value
        console.log({ email, password })
      }
    }
  </script>
</app-form>

form要素から値を取得

送信するだけのフォームならおすすめです。
DOMを検索しない分上の方法よりパフォーマンスはいいのですが、誤差ですね😅

<app-form>
  <form onsubmit="{ handleSubmit }">
    <input type="email" name="email" placeholder="メールアドレス" />
    <input type="password" name="password" placeholder="パスワード" />
    <button>送信</button>
  </form>
  <script>
    export default {
      handleSubmit(e) {
        e.preventDefault()
        const fields = e.target.elements
        const email = fields['email'].value
        const password = fields['password'].value
        console.log({ email, password })
      }
    }
  </script>
</app-form>

stateにフォームの値を持つ

冗長だけど凝ったことをするならこれ一択ですね。
自分はフォームを作るとき大体あとから複雑になってくるので、最初から全部この方法で統一しています。

<app-form>
  <form onsubmit="{ handleSubmit }">
    <input type="email" name="email" placeholder="メールアドレス" oninput="{ handleEmail }" value="{ state.email }" />
    <input type="{ state.passwordType }" name="password" placeholder="パスワード" oninput="{ handlePassword }" value="{ state.password }" />
    <input type="checkbox" id="password_visible" onchange="{ handleCheckChange }">
    <label for="password_visible">パスワード表示</l1abel>
      <button>送信</button>
  </form>
  <script>
    export default {
      state: {
        email: '',
        password: '',
        passwordType: 'password'
      },

      handleEmail(e) {
        this.state.email = e.target.value
        this.update()
      },

      handlePassword(e) {
        this.state.password = e.target.value
        this.update()
      },

      handleCheckChange(e) {
        this.state.passwordType = e.target.checked ? 'text' : 'password'
        this.update()
      },

      handleSubmit(e) {
        e.preventDefault()
        console.log(this.state)
      }
    }
  </script>
</app-form>

さすがにフィールドごとにイベントハンドラーhandle...を用意するのは冗長すぎるので、普段は一つのハンドラーで工夫しています。

-    <input type="email" name="email" placeholder="メールアドレス" oninput="{ handleEmail }" value="{ state.email }" />
-    <input type="{ state.passwordType }" name="password" placeholder="パスワード" oninput="{ handlePassword }" value="{ state.password }" />
+    <input type="email" name="email" placeholder="メールアドレス" oninput="{ handleChange }" value="{ state.email }" />
+    <input type="{ state.passwordType }" name="password" placeholder="パスワード" oninput="{ handleChange }" value="{ state.password }" />
...

-      handleEmail(e) {
-        this.state.email = e.target.value
-        this.update()
-      },
-      handlePassword(e) {
-        this.state.password = e.target.value
-        this.update()
-      },
+      handleChange(e) {
+        this.state[e.target.name] = e.target.value
+        this.update()
+      },

その他

name属性がないフィールドも扱う

<awesome-color-picker value="" on-change="" />という感じのname属性のないカスタムなコンポーネントがある時 + なるべくhandleChange一つにまとめたい

<app-form>
  <form onsubmit="{ handleSubmit }">
    <awesome-color-picker on-change="{ color => handleChange('color', color) }" value="{ state.color }" />
    <input type="text" oninput="{ e => handleChange('text', e.target.value) }" value="{ state.text }" />
  </form>
  <script>
    export default {
      state: {
        text: '',
        color: '#ffffff'
      },

      handleChange(name, value) {
        this.state[name] = value
        this.update()
      },

      handleSubmit(e) {
        e.preventDefault()
        console.log(this.state)
      }
    }
  </script>
</app-form>

{ e => handleChange('text', e.target.value) } ← 好みの問題ですがこれが「なんか気持ち悪い...」なら

-    <awesome-color-picker on-change="{ color => handleChange('color', color) }" value="{ state.color }" />
-    <input type="text" oninput="{ e => handleChange('text', e.target.value) }" value="{ state.text }" />
+    <awesome-color-picker on-change="{ handleChange('color') }" value="{ state.color }" />
+    <input type="text" oninput="{ handleChange('text') }" value="{ state.text }" />
...

-      handleChange(name, value) {
-        this.state[name] = value
-        this.update()
-      },
+      handleChange(name) {
+        return e => {
+          this.state[name] = e.target ? e.target.value : e;
+          this.update();
+        }
+      },

こっちの方がすっきりする気がします。

フォームが複雑になってくるとイベントハンドラ一つでカバーできないときもあるので、無理に一つにまとめずに複数のイベントハンドラを使いましょう。自分は単純なものは上にあったようなhandleChangeを使い、それ以外のものは個別にイベントハンドラを作ります。

最後に

質問などがあれば気軽にコメントしてください!(^^♪

elastic
JavaScriptやってます
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away