4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

stimulus、「data-target」「data-action」が複数ある場合、対象をどのように特定するか

Posted at

##まずは前々回の復習がてら

ハンドブックにある「Hello, Stimulus」を若干書き換えて、ボタンを押すと「A」「B」に入力した文字が「C」に表示されるようにしてみた。

.html.erb
<div data-controller="test-desu">

  A :<input data-target="test-desu.name_1" type="text">
  <button data-action="click->test-desu#greet" >Greet</button>

  B :<input data-target="test-desu.name_2" type="text">

  <label data-target="test-desu.write_here">C :write here</label>

</div>

↓できた画面が、こちら。
スクリーンショット 2019-05-19 0.18.01.png
ターゲット名「name_1」→A行の入力枠
ターゲット名「name_2」→B行の入力枠
ターゲット名「write_here」→"C:write here"の部分

これに対してstimulusのjsで、(ファイル名=thmlで振ったデータコントローラー名……なお名称については気にしない方向で(「てすと_です」って……いや、他にぱっと思いつかなかったんだ))

test_desu_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name_1","name_2","write_here" ]

  greet() {
    this.write_hereTarget.textContent = this.name + "。そして、" + this.name2;
  }

  get name() {
    return this.name_1Target.value
  }
  get name2() {
    return this.name_2Target.value
  }
}

「A」「B」に入力してボタンを押すと……

スクリーンショット 2019-05-19 0.27.20.png
うん、ちゃんと表示された。

ターゲット名は、
【実際に使う部分……「name_1Target」「this.name_2Target」「write_hereTarget」】
だけでなく、
【初めの宣言……「static targets = [ "name_1","name_2","write_here" ]」】
にも記述しなければいけないことに注意
なお、
・static targets = [ "name_1" ]
・static targets = [ "name_2" ]
・static targets = [ "write_here" ]
と分けて書いたりすると、エラーになる(当たり前かもしれないけど、実際自分はこう書いてエラーを出しました orz。

##ではもし、「A」の部分が複数あったら?

つまり、

.html.erb
<div data-controller="test-desu">
  <div>
    A-1 :<input data-target="test-desu.name_1" type="text">
    <button data-action="click->test-desu#greet" >Greet-1</button>
  </div>

  <div>
    A-2 :<input data-target="test-desu.name_1" type="text" >
    <button data-action="click->test-desu#greet" >Greet-2</button>
  </div>

  <div>
    A-3 :<input data-target="test-desu.name_1" type="text" >
    <button data-action="click->test-desu#greet" >Greet-3</button>
  </div>

  B :<input data-target="test-desu.name_2" type="text">

  <label data-target="test-desu.write_here">C :write here</label>

</div>

スクリーンショット 2019-05-19 0.48.26.png
これで、「A1〜3のうち、押したボタンの行内容」+「Bの内容」が「Cの部分」に表示されるようにしたい。

なおハンドブックの4にあるように、同名のターゲットを複数用意することは可能。その場合、「Target」ではなく「Targets」を使用する……はず。

なので試しに、jsを止めて検証ツールで見てみると、
スクリーンショット 2019-05-19 1.03.39.png
「this.name_1Targets(番号)」で、行情報を取得できる。

また
「 greet() {」→→→「 greet(el) {」
と変更すれば、
スクリーンショット 2019-05-19 1.09.48.png
「el.target.dataset」で、アクションを発生させたボタンの情報を取得できる。
本当はここで「.index」とかして、【複数あるgreetアクションのうち、何番目が刺激されたのか】を取得したかったのだけど……見つからなくって諦めて(ありそうな気はしているので、「何番目の要素か」の取得方法、ご存知の方がいれば教えてください)、html側に「何番目の要素か」を取得するための「data-」要素を追加することにした。

具体的には、
【data-greet-num = "0〜2の数字"】
を追加記述。これでjs側に番号を渡し、jsでは
el.target.dataset.greetNum
で受け取ってやることにする。

つまり、、、、

.html.erb
<div data-controller="test-desu">
  <div>
    A-1 :<input data-target="test-desu.name_1" type="text">
    <button data-action="click->test-desu#greet" data-greet-num = "0" >Greet-1</button>
  </div>

  <div>
    A-2 :<input data-target="test-desu.name_1" type="text">
    <button data-action="click->test-desu#greet" data-greet-num = "1" >Greet-2</button>
  </div>

  <div>
    A-3 :<input data-target="test-desu.name_1" type="text">
    <button data-action="click->test-desu#greet" data-greet-num = "2" >Greet-3</button>
  </div>

  B :<input data-target="test-desu.name_2" type="text">


  <label data-target="test-desu.write_here">C :write here</label>

</div>
test_desu_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name_1","name_2","write_here" ]

  greet(el) {
    this.write_hereTarget.textContent =
    this.name_1Targets[el.target.dataset.greetNum].value +
    "。そして、" + this.name_2Target.value;
  }

// ↓「name」に「el」を渡せなかったため、関数自体を取り消して「greet(el)」に纏めた
  // get name(el) {
  //   return this.name_1Targets[el.target.dataset.greetNum].value
  // }
  // get name2() {
  //   return this.name_2Target.value
  // }
}

こうすると、
スクリーンショット 2019-05-19 1.35.17.png
こうなる。

#さらに応用
< data-greet-num = "数字" >
の数字番号は、ただ0から順番に振っているだけ。
だから当然、【一覧画面、検索されたデータを「モデル名.each do」で回して一行ずつ表示】している部分にも簡単に当てることができる。

以下は、【「cd」と「name」の列を持つuser情報の一覧画面(scaffoldで作成)】に追記したもの

user/index.html.erb
<h1>Users</h1>
<div data-controller="greet-user">

  <table>
    <thead>
      <tr>
        <th>Cd</th>
        <th>Name</th>
        <th colspan="4"></th>
      </tr>
    </thead>

    <tbody >

      <% @users.each_with_index do |user,i| %>
        <tr>
          <td><%= user.cd %></td>
          <td data-target="greet-user.name" ><%= user.name %></td>
          <td><%= link_to 'Show', user %></td>
          <td><%= link_to 'Edit', edit_user_path(user) %></td>
          <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>

          <td><button data-action="click->greet-user#greet" data-greet-num = <%= i.to_s %> >Greet</button></td>

        </tr>
      <% end %>
    </tbody>

  </table>

  <label data-target="greet-user.write_here">write here</label>

</div>

<br>

<%= link_to 'New User', new_user_path %>

greet_user_controller.js


import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name","write_here" ]

  greet(el) {
    this.write_hereTarget.textContent =
    this.nameTargets[el.target.dataset.greetNum].textContent +
    "さん、こんにちは。" ;
  }

}

↓↓↓↓
スクリーンショット 2019-05-19 2.21.11.png

ボタンを押すと、
【選択した行の「Name」 + "さん、こんにちは。"】が表示される。

うん、少しずつではあるが、stimulusの使い方がつかめてきた気がする。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?