Open11

「猫でもわかるHotwire入門 Turbo編」の実践メモ

ピン留めされたアイテム
Yuma ItoYuma Ito

概要

Hotwireに入門したく、以下のチュートリアルを実施した。
https://zenn.dev/shita1112/books/cat-hotwire-turbo

環境

記事では以下のバージョンが対象であるが、自分は最新バージョンを使ったので、差分があった。

記事

Ruby: 3.1.0
Rails: 7.0.2
Turbo: 7.1.0
Stimulus: 3.0.1
turbo-rails: 1.0.1
stimulus-rails: 1.0.4

自分の環境

  • Ruby: 3.3.4
  • Rails: 7.2.2
  • Turbo:8.0.10
  • Stimulus: 3.2.2
  • turbo-rails: 2.0.11
  • stimulus-rails: 1.3.4
Yuma ItoYuma Ito

rails new

下記コマンドでrails newを実行したところ、bunがインストールできていなかったので一部の処理がスキップされた。

$ rails new cat-hotwire --css=bootstrap --skip-jbuilder --skip-action-mailbox --skip-action-mailer --skip-test --skip-active-storage --skip-action-text
ログ
rails new cat-hotwire --css=bootstrap --skip-jbuilder --skip-action-mailbox --skip-action-mailer --skip-test --skip-active-storage --skip-action-text
      create
      create  README.md
      create  Rakefile
      create  .node-version
      create  .ruby-version
      create  config.ru
      create  .gitignore
      create  .gitattributes
      create  Gemfile
         run  git init -b main from "."
Initialized empty Git repository in /Users/yuma.ito/repositories/personal/cat-hotwire/.git/
      create  app
      create  app/assets/config/manifest.js
      create  app/assets/stylesheets/application.css
      create  app/channels/application_cable/channel.rb
      create  app/channels/application_cable/connection.rb
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/jobs/application_job.rb
      create  app/mailers/application_mailer.rb
      create  app/models/application_record.rb
      create  app/views/layouts/application.html.erb
      create  app/views/layouts/mailer.html.erb
      create  app/views/layouts/mailer.text.erb
      create  app/views/pwa/manifest.json.erb
      create  app/views/pwa/service-worker.js
      create  app/assets/images
      create  app/assets/images/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/brakeman
      create  bin/rails
      create  bin/rake
      create  bin/rubocop
      create  bin/setup
      create  Dockerfile
      create  .dockerignore
      create  bin/docker-entrypoint
      create  .rubocop.yml
      create  .github/workflows
      create  .github/workflows/ci.yml
      create  .github/dependabot.yml
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/cable.yml
      create  config/puma.rb
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/content_security_policy.rb
      create  config/initializers/cors.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/new_framework_defaults_7_2.rb
      create  config/initializers/permissions_policy.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/master.key
      append  .gitignore
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/406-unsupported-browser.html
      create  public/422.html
      create  public/500.html
      create  public/icon.png
      create  public/icon.svg
      create  public/robots.txt
      create  tmp
      create  tmp/.keep
      create  tmp/pids
      create  tmp/pids/.keep
      create  vendor
      create  vendor/.keep
      create  storage
      create  storage/.keep
      create  tmp/storage
      create  tmp/storage/.keep
      remove  app/views/layouts/mailer.html.erb
      remove  app/views/layouts/mailer.text.erb
      remove  app/mailers
      remove  test/mailers
      remove  config/initializers/cors.rb
      remove  config/initializers/new_framework_defaults_7_2.rb
         run  bundle install --quiet
         run  bundle lock --add-platform=x86_64-linux
Writing lockfile to /Users/yuma.ito/repositories/personal/cat-hotwire/Gemfile.lock
         run  bundle lock --add-platform=aarch64-linux
Writing lockfile to /Users/yuma.ito/repositories/personal/cat-hotwire/Gemfile.lock
         run  bundle binstubs bundler
       rails  importmap:install
       apply  /Users/yuma.ito/.asdf/installs/ruby/3.3.4/lib/ruby/gems/3.3.0/gems/importmap-rails-2.0.3/lib/install/install.rb
  Add Importmap include tags in application layout
      insert    app/views/layouts/application.html.erb
  Create application.js module as entrypoint
      create    app/javascript/application.js
  Use vendor/javascript for downloaded pins
      create    vendor/javascript
      create    vendor/javascript/.keep
  Ensure JavaScript files are in the Sprocket manifest
      append    app/assets/config/manifest.js
  Configure importmap paths in config/importmap.rb
      create    config/importmap.rb
  Copying binstub
      create    bin/importmap
         run  bundle install --quiet
       rails  turbo:install stimulus:install
       apply  /Users/yuma.ito/.asdf/installs/ruby/3.3.4/lib/ruby/gems/3.3.0/gems/turbo-rails-2.0.11/lib/install/turbo_with_importmap.rb
  Import Turbo
      append    app/javascript/application.js
  Pin Turbo
      append    config/importmap.rb
         run  bundle install --quiet
       apply  /Users/yuma.ito/.asdf/installs/ruby/3.3.4/lib/ruby/gems/3.3.0/gems/stimulus-rails-1.3.4/lib/install/stimulus_with_importmap.rb
  Create controllers directory
      create    app/javascript/controllers
      create    app/javascript/controllers/index.js
      create    app/javascript/controllers/application.js
      create    app/javascript/controllers/hello_controller.js
  Import Stimulus controllers
      append    app/javascript/application.js
  Pin Stimulus
  Appending: pin "@hotwired/stimulus", to: "stimulus.min.js"
      append    config/importmap.rb
  Appending: pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
      append    config/importmap.rb
  Pin all controllers
  Appending: pin_all_from "app/javascript/controllers", under: "controllers"
      append    config/importmap.rb
         run  bundle install --quiet
       rails  css:install:bootstrap
       apply  /Users/yuma.ito/.asdf/installs/ruby/3.3.4/lib/ruby/gems/3.3.0/gems/cssbundling-rails-1.4.1/lib/install/bootstrap/install.rb
       apply    /Users/yuma.ito/.asdf/installs/ruby/3.3.4/lib/ruby/gems/3.3.0/gems/cssbundling-rails-1.4.1/lib/install/install.rb
    Build into app/assets/builds
      create      app/assets/builds
      create      app/assets/builds/.keep
      append      app/assets/config/manifest.js
    Stop linking stylesheets automatically
        gsub      app/assets/config/manifest.js
      append      .gitignore
      append      .gitignore
    Remove app/assets/stylesheets/application.css so build output can take over
      remove      app/assets/stylesheets/application.css
    Add stylesheet link tag in application layout
   unchanged      app/views/layouts/application.html.erb
    Add default package.json
      create      package.json
    Add default Procfile.dev
      create      Procfile.dev
    Ensure foreman is installed
         run      gem install foreman from "."
Successfully installed foreman-0.88.1
Parsing documentation for foreman-0.88.1
Done installing documentation for foreman after 0 seconds
1 gem installed
    Add bin/dev to start foreman
      create      bin/dev
  Install Bootstrap with Bootstrap Icons, Popperjs/core and Autoprefixer
      create    app/assets/stylesheets/application.bootstrap.scss
         run    bun add sass bootstrap bootstrap-icons @popperjs/core postcss postcss-cli autoprefixer nodemon from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4
      insert    config/initializers/assets.rb
  Appending Bootstrap JavaScript import to default entry point
      append    app/javascript/application.js
  Pin Bootstrap
      append    config/importmap.rb
      insert    config/initializers/assets.rb
      append    config/initializers/assets.rb
         run    bun run build:css:compile from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4
         run    bun run build:css:prefix from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4
         run    bun run build:css from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4
        gsub    Procfile.dev
         run  bundle install --quiet
         run    bun add sass bootstrap bootstrap-icons @popperjs/core postcss postcss-cli autoprefixer nodemon from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4

         run    bun run build:css:compile from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4
         run    bun run build:css:prefix from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4
         run    bun run build:css from "."
No version is set for command bun
Consider adding one of the following versions in your config file at /Users/yuma.ito/.tool-versions
bun 1.0.11
bun 1.0.29
bun 1.1.4

など

Yuma ItoYuma Ito

--css=bootstrapを指定しているので、cssbundling-railsによってBootstrapがインストールされる。

-jは指定していないので、jsbundling-railsではなくデフォルトのimportmap-railsが使われる。

本の方には

$ rails newのオプションで--css bootstrapを指定したけれども、その場合は自動的にjsbundling-railsを使うことになる

と記載があるが、jsbundling-railsにはならなかった。(Railsのバージョンの違い?)

Yuma ItoYuma Ito

途中でインストールに失敗していたので、bunをインストールした後に下記コマンドを実行した

bun add sass bootstrap bootstrap-icons @popperjs/core postcss postcss-cli autoprefixer nodemon
Yuma ItoYuma Ito

チュートリアル3の登録・編集機能でモーダル化する際にbootstrapのModalがimportできずエラー

Failed to register controller: modal (controllers/modal_controller) SyntaxError: The requested module 'bootstrap' does not provide an export named 'Modal'

import mapsで正しくモジュールを読み込めていない?

Yuma ItoYuma Ito

importmapにはbootstrapは存在しているので、マッピングはうまくできていそうだが。

該当のコードは以下。

app/javascript/controllers/modal_controller.js
import {Controller} from "@hotwired/stimulus"
import {Modal} from "bootstrap"

// Connects to data-controller="modal"
export default class extends Controller {
  // コントローラーがHTML要素にアタッチされたときに呼ばれる
  connect() {
    this.modal = new Modal(this.element);
    this.modal.show();
  }

  close(event) {
    if (event.detail.success) {
      this.modal.hide();
    }
  }
}
Yuma ItoYuma Ito

config/importmap.rbに以下の2行を追加した。

config/importmap.rb
pin "bootstrap", to: "bootstrap.min.js", preload: true
pin "@popperjs/core", to: "popper.js", preload: true

config/assets.rbに以下のコードを追加

config/assets.rb
Rails.application.config.assets.precompile += %w[bootstrap.min.js popper.js]

参考:
https://github.com/twbs/bootstrap-rubygem

Yuma ItoYuma Ito

Gemfileにbootstrapが入っていなかったので追加

gem "bootstrap"

やはり同じエラーになり原因がさっぱり分からなかったので、CDNから取得するとうまく行った

pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@5.2.3/dist/js/bootstrap.esm.js"
Yuma ItoYuma Ito

Stimulusで {data: {controller: 'hoge'}}を指定する条件は?

チュートリアル3の「編集・登録のモーダル化」にて{controller: 'modal'}を指定していなかった。

app/views/cats/_form.html.erb
  <%= bootstrap_form_with(model: cat, data: { action: "turbo:submit-end->modal#close" }) do |form| %>

controllerを指定するときとしないときの違いを知りたい

Yuma ItoYuma Ito

Copilotに聞いてみた

Stimulusでdata-controllerを指定するときとしないときの違いは

Stimulusdata-controllerを指定する場合としない場合の違いは、以下の通りです。

data-controllerを指定する場合

data-controllerを指定すると、そのHTML要素に対して特定のStimulusコントローラーがアタッチされます。これにより、コントローラーのライフサイクルメソッド(例:connect)やアクション(例:clickイベントに対応するメソッド)が実行されます。

例:

<div data-controller="modal">
  <!-- この要素に対してmodal_controller.jsがアタッチされる -->
</div>

data-controllerを指定しない場合

data-controllerを指定しない場合、そのHTML要素にはStimulusコントローラーがアタッチされません。したがって、コントローラーのライフサイクルメソッドやアクションは実行されません。

例:

<div>
  <!-- この要素にはコントローラーがアタッチされない -->
</div>

data-controllerを指定することで、特定のHTML要素に対してStimulusコントローラーの機能を適用することができます。

Yuma ItoYuma Ito

DOM要素とStimulusのコントローラーの紐づけを行っている模様。