3
2

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 1 year has passed since last update.

Stimulus 5: 状態の管理

Last updated at Posted at 2022-12-14

この記事はGoogle翻訳の結果を編集したものです。

最新のフレームワークのほとんどは常にJavaScriptで状態を維持することを推奨しています。それらはDOMを書き込み専用のレンダリングターゲットとして扱い、サーバーからJSONを消費するクライアント側のテンプレートによって調整されます。

Stimulusは別のアプローチを取ります。Stimulusアプリケーションの状態はDOMの属性として存在します。コントローラー自体はほとんどステートレスです。このアプローチにより最初のドキュメント、Ajax リクエスト、Turboへのアクセス、さらには別のJavaScriptライブラリなど、どこからでもHTMLを操作できるようになり、明示的な初期化手順を行わなくても関連するコントローラーが自動的に起動します。

スライドショーの作成

前の章では要素にクラス名を追加することでStimulusコントローラーがドキュメント内の単純な状態を維持する方法を学びました。しかし、単純なフラグだけでなく値を格納する必要がある場合はどうすればよいでしょうか?

現在選択されているスライドインデックスを属性に保持するスライドショーコントローラーを作成することで、この問題を調査します。

いつものようにHTMLから始めます。

<div data-controller="slideshow">
  <button data-action="slideshow#previous"></button>
  <button data-action="slideshow#next"></button>

  <div data-slideshow-target="slide">🐵</div>
  <div data-slideshow-target="slide">🙈</div>
  <div data-slideshow-target="slide">🙉</div>
  <div data-slideshow-target="slide">🙊</div>
</div>

それぞれのslideターゲットはスライドショーの1つのスライドを表します。 コントローラーは一度に1つのスライドのみが表示されるようにする責任があります。

コントローラーを下書きしましょう。次のように新しいファイルsrc/controllers/slideshow_controller.jsを作成します。

// src/controllers/slideshow_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]

  initialize() {
    this.index = 0
    this.showCurrentSlide()
  }

  next() {
    this.index++
    this.showCurrentSlide()
  }

  previous() {
    this.index--
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index !== this.index
    })
  }
}

コントローラーはメソッドshowCurrentSlide()を定義します。このメソッドは各スライドターゲットをループし、インデックスが一致する場合に隠し属性を切り替えます。

最初のスライドを表示してコントローラを初期化し、next()およびprevious()アクションメソッドで現在のスライドを進めたり巻き戻したりします。

ライフサイクル コールバックの説明

initialize()メソッドは何をしますか?以前に使用したconnect()メソッドとの違いは何ですか?

これらはStimulusのライフサイクルコールバックメソッドであり、コントローラーがドキュメントに出入りするときに関連する状態を設定または破棄するのに役立ちます。

メソッド Stimulusによる呼び出し
initialize() コントローラーが最初にインスタンス化されたときに1回
connect() コントローラが DOM に接続されているときはいつでも
disconnect() コントローラーが DOM から切断されたときはいつでも

ページを再読み込みし、次へボタンで次のスライドに進むことを確認します。

DOMから初期状態を読み取る

this.indexプロパティでコントローラーがその状態(現在選択されているスライド)を追跡する方法に注目してください。

ここで最初のスライドではなく2番目のスライドを表示して、スライドショーの1つを開始したいとします。マークアップで開始インデックスをエンコードするにはどうすればよいでしょうか?

1つの方法はHTMLデータ属性を使用して初期インデックスをロードすることです。たとえばコントローラーの要素にdata-index属性を追加できます。

<div data-controller="slideshow" data-index="1">

次にinitialize()メソッドでその属性を読み取り、整数に変換してshowCurrentSlide()に渡します。

initialize() {
  this.index = Number(this.element.dataset.index)
  this.showCurrentSlide()
}

これで作業は完了するかもしれませんが、扱いにくく属性の名前を決定する必要があり、インデックスに再度アクセスしたり、インデックスをインクリメントして結果をDOMに保存したりしたい場合には役に立ちません。

値の使用

Stimulusコントローラーはデータ属性に自動的にマップされる型指定された値のプロパティをサポートします。コントローラークラスの先頭に値の定義を追加すると次のようになります。

static values = { index: Number }

Stimulusはdata-slideshow-index-value属性に関連付けられたthis.indexValueコントローラープロパティを作成し、数値変換を処理します。

実際に見てみましょう。 関連するデータ属性をHTMLに追加します。

<div data-controller="slideshow" data-slideshow-index-value="1">

次にコントローラーに静的な値の定義を追加し、initialize()メソッドを変更してthis.indexValueをログに記録します。

export default class extends Controller {
  static values = { index: Number }

  initialize() {
    console.log(this.indexValue)
    console.log(typeof this.indexValue)
  }

  // …
}

ページをリロードし、コンソールに1Numberが表示されていることを確認します。

その静的な値の行は何ですか?

ターゲットと同様に値と呼ばれる静的オブジェクトプロパティに値を記述して、Stimulusコントローラーで値を定義します。この場合、indexと呼ばれる単一の数値を定義しました。値の定義の詳細についてはリファレンスドキュメントを参照してください。

次にinitialize() とコントローラーの他のメソッドを更新して、this.indexの代わりにthis.indexValueを使用します。 完了するとコントローラーは次のようになります。

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]
  static values = { index: Number }

  initialize() {
    this.showCurrentSlide()
  }

  next() {
    this.indexValue++
    this.showCurrentSlide()
  }

  previous() {
    this.indexValue--
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index !== this.indexValue
    })
  }
}

ページを再読み込みしWebインスペクターを使用してあるスライドから次のスライドに移動するときに、コントローラー要素のdata-slideshow-index-value属性が変化することを確認します。

コールバックの変更

改訂されたコントローラーは元のバージョンを改善していますが、this.showCurrentSlide()への繰り返しの呼び出しが目立ちます。コントローラーの初期化時とthis.indexValueを更新するたびにドキュメントの状態を手動で更新する必要があります。

Stimulusの値変更コールバックを定義して繰り返しをクリーンアップし、インデックス値が変更されるたびにコントローラーがどのように応答するかを指定できます。

まずinitialize()メソッドを削除し、新しいメソッドindexValueChanged()を定義します。 次にnext()previous()からthis.showCurrentSlide()への呼び出しを削除します。

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]
  static values = { index: Number }

  next() {
    this.indexValue++
  }

  previous() {
    this.indexValue--
  }

  indexValueChanged() {
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index !== this.indexValue
    })
  }
}

ページを再読み込みしてスライドショーの動作が同じであることを確認します。

Stimulusは初期化時にindexValueChanged()メソッドを呼び出し、data-slideshow-index-value属性への変更に応答して呼び出します。Webインスペクターで属性をいじることもでき、コントローラーはそれに応じてスライドを変更します。さあ、試してみてください!

デフォルト設定

静的定義の一部としてデフォルト値を設定することもできます。これは次のように行われます。

static values = { index: { type: Number, default: 2 } }

コントローラー要素でdata-slideshow-index-value属性が定義されていない場合、インデックスは2から始まります。他の値がある場合、デフォルトが必要なものとそうでないものを組み合わせて一致させることができます。

static values = { index: Number, effect: { type: String, default: "kenburns" } }

まとめと次のステップ

この章では値を使用してスライドショーコントローラーの現在のインデックスを読み込んで永続化する方法を見てきました。

使いやすさの観点から、私たちのコントローラーは不完全です。 最初のスライドを見ているときは、前へボタンは何もしていないように見えます。内部的にindexValue0から-1に減少します。代わりに値を最後のスライドインデックスにラップアラウンドすることはできますか?(次へボタンにも同様の問題があります。)

次にStimulusコントローラーでタイマーやHTTPリクエストなどの外部リソースを追跡する方法を見ていきます。

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?