RubyonRailsアプリケーションに刺激を追加する方法

提供:Dev Guides
移動先:案内検索

序章

Ruby on Rails プロジェクトで作業している場合、要件には、ビューテンプレートによって生成されたHTMLとの対話性が含まれる場合があります。 その場合、この対話性を実装する方法についていくつかの選択肢があります。

たとえば、ReactEmberのようなJavaScriptフレームワークを実装できます。 要件にクライアント側での状態の処理が含まれている場合、またはサーバーへの頻繁なクエリに関連するパフォーマンスの問題が懸念される場合は、これらのフレームワークのいずれかを選択するのが理にかなっている場合があります。 多くのシングルページアプリケーション(SPA)は、このアプローチを採用しています。

ただし、クライアント側で状態と頻繁な更新を管理するフレームワークを実装する際に留意すべきいくつかの考慮事項があります。

  1. JavaScriptの解析、JSONのフェッチとHTMLへの変換などの読み込みと変換の要件により、パフォーマンスが制限される可能性があります。
  2. フレームワークへのコミットメントには、特に小規模なJavaScriptの拡張機能を探している場合に、特定のユースケースで必要とされるよりも多くのコードを記述することが含まれる場合があります。
  3. クライアント側とサーバー側の両方で状態管理を行うと、作業が重複し、エラーの表面積が増える可能性があります。

別の方法として、 Basecamp のチーム(Railsを作成したのと同じチーム)が Stimulus.js を作成しました。これは、「既存のHTML用の控えめなJavaScriptフレームワーク」と説明されています。 」 Stimulusは、サーバー側で生成されたHTMLを操作することにより、最新のRailsアプリケーションを強化することを目的としています。 状態はDocumentObject Model(DOM)に存在し、フレームワークはDOM内の要素やイベントと対話するための標準的な方法を提供します。 Turbolinks (デフォルトではRails 5+に含まれています)と連携して動作し、明確に定義された目的に限定され、スコープが設定されたコードでパフォーマンスとロード時間を改善します。

このチュートリアルでは、Stimulusをインストールして使用し、サメに関する情報を読者に提供する既存のRailsアプリケーションを構築します。 このアプリケーションにはすでにサメのデータを処理するためのモデルがありますが、個々のサメに関する投稿用のネストされたリソースを追加して、ユーザーがサメに関する一連の考えや意見を構築できるようにします。 この部分は、JavaScriptを使用してページ上の投稿の位置と外観を操作することを除いて、 RubyonRailsアプリケーションのネストされたリソースを作成する方法とほぼ並行して実行されます。 また、ポストモデル自体を構築するために少し異なるアプローチを取ります。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Ubuntu18.04を実行しているローカルマシンまたは開発サーバー。 開発マシンには、管理者権限を持つroot以外のユーザーと、ufwで構成されたファイアウォールが必要です。 これを設定する方法については、 Ubuntu18.04を使用した初期サーバー設定のチュートリアルを参照してください。
  • Node.jsおよびnpmがローカルマシンまたは開発サーバーにインストールされています。 このチュートリアルでは、Node.jsバージョン10.16.3とnpmバージョン6.9.0を使用します。 Ubuntu 18.04にNode.jsとnpmをインストールする手順については、 Ubuntu18.04にNode.jsをインストールする方法の「PPAを使用したインストール」セクションの手順に従ってください。
  • Ubuntu18.04でrbenvを使用してRubyonRailsをインストールする方法の手順1〜4 に従って、ローカルマシンまたは開発サーバーにインストールされたRuby、 rbenv 、およびRails 。 このチュートリアルでは、Ruby 2.5.1 、rbenv 1.1.2 、およびRails 5.2.3を使用します。
  • Ruby on Railsアプリケーションの構築方法の指示に従って、SQLiteをインストールし、基本的なサメ情報アプリケーションを作成しました。

ステップ1—ネストされたモデルの作成

最初のステップは、ネストされたPost モデルを作成することです。これを、既存のSharkモデルに関連付けます。 これを行うには、モデル間にActive Recordアソシエーションを作成します。投稿は特定のサメに属し、各サメは複数の投稿を持つことができます。

開始するには、前提条件でRailsプロジェクト用に作成したsharkappディレクトリに移動します。

cd sharkapp

Postモデルを作成するには、 railsgenerateコマンドとmodelジェネレーターを使用します。 次のコマンドを入力して、モデルを作成します。

rails generate model Post body:text shark:references

body:textを使用して、Railsにbodyフィールドをpostsデータベーステーブル(Postモデルにマップするテーブル)に含めるように指示しています。 :referencesキーワードも含まれています。これはSharkモデルとPostモデル間の関連付けを設定します。 具体的には、これにより、sharksデータベースの各サメエントリを表す外部キーpostsデータベースに追加されます。

コマンドを実行すると、Railsがアプリケーション用に生成したリソースを確認する出力が表示されます。 先に進む前に、データベース移行ファイルをチェックして、モデルとデータベーステーブルの間に現在存在する関係を確認できます。 次のコマンドを使用してファイルの内容を確認し、ここに表示されているものを自分の移行ファイルのタイムスタンプに置き換えてください。

cat db/migrate/20190805132506_create_posts.rb

次の出力が表示されます。

Outputclass CreatePosts < ActiveRecord::Migration[5.2]
  def change
    create_table :posts do |t|
      t.text :body
      t.references :shark, foreign_key: true

      t.timestamps
    end
  end
end

ご覧のとおり、テーブルにはサメの外部キーの列が含まれています。 このキーはmodel_name_idの形式を取ります—この場合はshark_idです。

Railsは他の場所でもモデル間の関係を確立しています。 次のコマンドを使用して、新しく生成されたPostモデルを確認します。

cat app/models/post.rb
Outputclass Post < ApplicationRecord
  belongs_to :shark
end

belongs_toアソシエーションは、宣言モデルの単一インスタンスが名前付きモデルの単一インスタンスに属するモデル間の関係を設定します。 私たちのアプリケーションの場合、これは単一の投稿が単一のサメに属することを意味します。

RailsはすでにPostモデルでbelongs_toの関連付けを設定していますが、Sharkモデルでもhas_manyの関連付けを指定する必要があります。正しく機能するためのその関係。

has_manyアソシエーションをSharkモデルに追加するには、nanoまたはお気に入りのエディターを使用してapp/models/shark.rbを開きます。

nano app/models/shark.rb

次の行をファイルに追加して、サメと投稿の関係を確立します。

〜/ sharkapp / app / models / shark.rb

class Shark < ApplicationRecord
  has_many :posts
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

ここで検討する価値のあることの1つは、特定のサメが削除されると投稿がどうなるかということです。 削除されたサメに関連付けられた投稿をデータベースに残したくない場合があります。 特定のサメが削除されたときに、そのサメに関連付けられている投稿が確実に削除されるように、dependentオプションを関連付けに含めることができます。

次のコードをファイルに追加して、特定のサメに対するdestroyアクションで関連する投稿が削除されるようにします。

〜/ sharkapp / app / models / shark.rb

class Shark < ApplicationRecord
  has_many :posts, dependent: :destroy
  validates :name, presence: true, uniqueness: true
  validates :facts, presence: true
end

これらの変更が完了したら、ファイルを保存して閉じます。 nanoを使用している場合は、CTRL+XYENTERの順に押してください。

これで投稿用にモデルが生成されましたが、データベース内のデータと生成されてユーザーに表示されるHTMLを調整するためのコントローラーも必要になります。

ステップ2—ネストされたリソースのコントローラーを作成する

投稿コントローラーを作成するには、アプリケーションのメインルーティングファイルにネストされたリソースルートを設定し、コントローラーファイル自体を作成して、特定のアクションに関連付けるメソッドを指定する必要があります。

まず、config/routes.rbファイルを開いて、リソースの豊富なルート間の関係を確立します。

nano config/routes.rb

現在、ファイルは次のようになっています。

〜/sharkapp/config/routes.rb

Rails.application.routes.draw do
  resources :sharks

  root 'sharks#index'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

サメと投稿リソースの間に依存関係関係を作成したいと思います。 これを行うには、ルート宣言を更新して、:sharks:postsの親にします。 ファイル内のコードを次のように更新します。

〜/sharkapp/config/routes.rb

Rails.application.routes.draw do
  resources :sharks do
    resources :posts
  end
  root 'sharks#index'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

編集が終了したら、ファイルを保存して閉じます。

次に、コントローラー用にapp/controllers/posts_controller.rbという名前の新しいファイルを作成します。

nano app/controllers/posts_controller.rb

このファイルでは、個々の投稿を作成および破棄するために使用するメソッドを定義します。 ただし、これはネストされたモデルであるため、特定の投稿を特定のサメに関連付けるために使用できるローカルインスタンス変数@sharkも作成する必要があります。

まず、PostsControllerクラス自体を、特定のサメを参照できるget_sharkpost_paramsの2つのprivateメソッドとともに作成できます。 、paramsメソッドを介してユーザーが送信した情報にアクセスできます。

次のコードをファイルに追加します。

〜/sharkapp/app/controllers/posts_controller.rb

class PostsController < ApplicationController
  before_action :get_shark 

  private

  def get_shark
    @shark = Shark.find(params[:shark_id])
  end

  def post_params
    params.require(:post).permit(:body, :shark_id)
  end
end

これで、:shark_idキーを使用して、投稿が関連付けられる特定のsharkインスタンスと、ユーザーが投稿を作成するために入力するデータを取得するメソッドができました。 これらのオブジェクトは両方とも、投稿の作成と破棄を処理するために定義するメソッドで使用できるようになります。

次に、privateメソッドの上に、次のコードをファイルに追加して、createおよびdestroyメソッドを定義します。

〜/sharkapp/app/controllers/posts_controller.rb

. . .
  def create
    @post = @shark.posts.create(post_params)
  end
      
  def destroy
    @post = @shark.posts.find(params[:id])
    @post.destroy   
  end
. . .

これらのメソッドは、@postインスタンスを特定の@sharkインスタンスに関連付け、サメと投稿。 findcreateなどのメソッドを使用すると、特定のサメに関連付けられた投稿のコレクションをターゲットにすることができます。

完成したファイルは次のようになります。

〜/sharkapp/app/controllers/posts_controller.rb

class PostsController < ApplicationController
  before_action :get_shark 

  def create
    @post = @shark.posts.create(post_params)
  end
      
  def destroy
    @post = @shark.posts.find(params[:id])
    @post.destroy   
  end

  private

  def get_shark
    @shark = Shark.find(params[:shark_id])
  end

  def post_params
    params.require(:post).permit(:body, :shark_id)
  end
end

編集が終了したら、ファイルを保存して閉じます。

コントローラとモデルを配置したら、ビューテンプレートと、アプリケーションで生成されたHTMLをどのように整理するかについて考え始めることができます。

ステップ3—パーシャルを使用したビューの再編成

Postモデルとコントローラーを作成したので、Railsの観点から最後に考えるのは、ユーザーがサメに関する情報を表示および入力できるようにするビューです。 ビューは、Stimulusとの双方向性を構築する機会もあります。

このステップでは、Stimulusでの作業の開始点となる、ビューとパーシャルをマップします。

投稿および投稿に関連付けられたすべてのパーシャルのベースとして機能するビューは、sharks/showビューです。

ファイルを開きます。

nano app/views/sharks/show.html.erb

現在、ファイルは次のようになっています。

〜/ sharkapp / app / views / sharks / show.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @shark.name %>
</p>

<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

Postモデルを作成したとき、投稿のビューはsharks/showビューで処理するため、生成しないことを選択しました。 したがって、このビューでは、最初に対処するのは、新しい投稿に対するユーザー入力を受け入れる方法と、ユーザーに投稿を提示する方法です。

注:このアプローチの代替方法については、 RubyonRailsアプリケーションのネストされたリソースを作成する方法を参照してください。投稿コントローラーで定義されているRead、Update、Delete (CRUD)メソッド。 これらの方法とその仕組みについては、 RubyonRailsアプリケーションステップ3を参照してください。


すべての機能をこのビューに組み込む代わりに、パーシャル(特定の機能を提供する再利用可能なテンプレート)を使用します。 新しい投稿用に1つを作成し、投稿をユーザーに表示する方法を制御するために別の部分を作成します。 全体を通して、JavaScriptを使用して投稿の表示を制御することが目標であるため、Stimulusを使用してページ上の投稿の外観を操作する方法と場所について検討します。

まず、サメの事実の下に、投稿用の<h2>ヘッダーと、sharks/postsという部分をレンダリングする行を追加します。

〜/ sharkapp / app / views / sharks / show.html.erb

. . . 
<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<h2>Posts</h2>
<%= render 'sharks/posts' %>
. . . 

これにより、新しい投稿オブジェクトのフォームビルダーでパーシャルがレンダリングされます。

次に、EditリンクとBackリンクの下に、ページ上の古い投稿の表示を制御するセクションを追加します。 次の行をファイルに追加して、sharks/allという部分をレンダリングします。

〜/ sharkapp / app / views / sharks / show.html.erb

<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

<div>
  <%= render 'sharks/all' %>
</div>

<div>要素は、Stimulusをこのファイルに統合し始めるときに役立ちます。

これらの編集が終了したら、ファイルを保存して閉じます。 Rails側で行った変更により、Stimulusのインストールとアプリケーションへの統合に進むことができます。

ステップ4—刺激をインストールする

Stimulusを使用する最初のステップは、アプリケーションをインストールして構成し、それを使用するようにすることです。 これには、YarnパッケージマネージャーやWebpacker、JavaScriptプリプロセッサーおよびバンドラーとの連携を可能にするgemなど、正しい依存関係があることの確認が含まれます。 webpack。 これらの依存関係を設定すると、Stimulusをインストールし、JavaScriptを使用してDOM内のイベントと要素を操作できるようになります。

Yarnをインストールすることから始めましょう。 まず、パッケージリストを更新します。

sudo apt update

次に、DebianYarnリポジトリのGPGキーを追加します。

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -

リポジトリをAPTソースに追加します。

echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list

新しく追加されたYarnパッケージでパッケージデータベースを更新します。

sudo apt update

そして最後に、Yarnをインストールします。

sudo apt install yarn

yarnをインストールすると、プロジェクトにwebpackergemを追加することができます。

プロジェクトのGemfileを開きます。これには、プロジェクトのgem依存関係が一覧表示されています。

nano Gemfile

ファイル内には、デフォルトで有効になっているターボリンクが表示されます。

〜/ sharkapp / Gemfile

. . . 
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
. . . 

Turbolinksは、ページの読み込みを最適化することでパフォーマンスを向上させるように設計されています。リンククリックで新しいページに移動する代わりに、Turbolinksはこれらのクリックイベントをインターセプトし、非同期JavaScriptおよびHTML(AJAX)を使用してページリクエストを行います。 次に、現在のページの本文を置き換え、<head>セクションのコンテンツをマージしますが、JavaScriptwindowおよびdocumentオブジェクトと<html>要素は保持されますレンダリング間。 これは、ページの読み込み時間が遅くなる主な原因の1つであるCSSおよびJavaScriptリソースの再読み込みに対処します。

デフォルトではGemfileにTurbolinksがありますが、Stimulusをインストールして使用できるように、webpackergemを追加する必要があります。 turbolinks宝石の下に、webpackerを追加します。

〜/ sharkapp / Gemfile

. . . 
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
gem 'webpacker', '~> 4.x'
. . . 

終了したら、ファイルを保存して閉じます。

次に、bundleコマンドを使用して、gemをプロジェクトのバンドルに追加します。

bundle

これにより、新しいGemfile.lockファイルが生成されます。これは、プロジェクトのgemとバージョンの最も信頼のおけるレコードです。

次に、次のbundle execコマンドを使用して、バンドルのコンテキストにgemをインストールします。

bundle exec rails webpacker:install

インストールが完了したら、アプリケーションのコンテンツセキュリティファイルを少し調整する必要があります。 これは、コンテンツセキュリティポリシー(CSP)制限付き環境であるRails 5.2+を使用しているためです。つまり、アプリケーションで許可されるスクリプトは、信頼できるソースからのもののみである必要があります。

Railsがアプリケーション全体のセキュリティポリシーを定義するために提供するデフォルトのファイルであるconfig/initializers/content_security_policy.rbを開きます。

nano config/initializers/content_security_policy.rb

ファイルの最後に次の行を追加して、webpack-dev-server(アプリケーションのwebpackバンドルを提供するサーバー)を許可されたオリジンとして許可します。

〜/ sharkapp / config / initializers / content_security_policy.rb

. . . 
Rails.application.config.content_security_policy do |policy|
  policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035' if Rails.env.development?
end

これにより、webpacker-dev-serverが信頼できるアセットソースとして認識されるようになります。

この変更が完了したら、ファイルを保存して閉じます。

webpackerをインストールすることにより、プロジェクトのappディレクトリ(メインアプリケーションコードが配置されているディレクトリ)に2つの新しいディレクトリを作成しました。 新しい親ディレクトリapp/javascriptは、プロジェクトのJavaScriptコードが存在する場所であり、次の構造になります。

Output├── javascript
│   ├── controllers
│   │   ├── hello_controller.js
│   │   └── index.js
│   └── packs
│       └── application.js

app/javascriptディレクトリには、2つの子ディレクトリが含まれます。Webpackエントリポイントを持つapp/javascript/packsと、Stimulusコントローラーを定義するapp/javascript/controllersです。 。 先ほど使用したbundle execコマンドでapp/javascript/packsディレクトリが作成されますが、app/javascript/controllersディレクトリを自動生成するにはStimulusをインストールする必要があります。

webpackerをインストールすると、次のコマンドでStimulusをインストールできるようになります。

bundle exec rails webpacker:install:stimulus

インストールが成功したことを示す次のような出力が表示されます。

Output. . . 
success Saved lockfile.
success Saved 5 new dependencies.
info Direct dependencies
└─ [email protected]
info All dependencies
├─ @stimulus/[email protected]
├─ @stimulus/[email protected]
├─ @stimulus/[email protected]
├─ @stimulus/[email protected]
└─ [email protected]
Done in 8.30s.
Webpacker now supports Stimulus.js 🎉

これでStimulusがインストールされ、Stimulusを操作するために必要なメインディレクトリが配置されました。 コードの記述に進む前に、インストールプロセスを完了するために、アプリケーションレベルでいくつかの調整を行う必要があります。

まず、app/views/layouts/application.html.erbを調整して、JavaScriptコードが使用可能であり、メインのwebpackerエントリポイントであるapp/javascript/packs/application.jsで定義されたコードが実行されるようにする必要があります。ページが読み込まれるたび。

そのファイルを開きます。

nano app/views/layouts/application.html.erb

次のjavascript_include_tagタグをjavascript_pack_tagに変更して、app/javascript/packs/application.jsをロードします。

〜/ sharkapp / app / views / layouts / application.html.erb

. . .
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
. . . 

この変更を行ったら、ファイルを保存して閉じます。

次に、app/javascript/packs/application.jsを開きます。

nano app/javascript/packs/application.js

最初、ファイルは次のようになります。

〜/ sharkapp / app / javascript / packs / application.js

. . . 
console.log('Hello World from Webpacker')

import "controllers"

そこにあるボイラープレートコードを削除し、次のコードを追加してStimulusコントローラーファイルをロードし、アプリケーションインスタンスを起動します。

〜/ sharkapp / app / javascript / packs / application.js

. . . 
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"

const application = Application.start()
const context = require.context("../controllers", true, /\.js$/)
application.load(definitionsFromContext(context))

このコードは、webpackヘルパーメソッドを使用してapp/javascript/controllersディレクトリ内のコントローラーを要求し、アプリケーションで使用するためにこのコンテキストをロードします。

編集が終了したら、ファイルを保存して閉じます。

これでStimulusがインストールされ、アプリケーションで使用できるようになりました。 次に、Stimulus controllersを使用して、サメshowビュー(sharks/postsおよびsharks/all)で参照したパーシャルを構築します。ターゲット、およびアクション

ステップ5—RailsPartialsでの刺激の使用

sharks/postsパーシャルは、 form_with formhelperを使用して新しい投稿オブジェクトを作成します。 また、Stimulusの3つのコアコンセプトであるコントローラー、ターゲット、およびアクションを利用します。 これらの概念は次のように機能します。

  • コントローラは、JavaScriptモジュールで定義され、モジュールのデフォルトオブジェクトとしてエクスポートされるJavaScriptクラスです。 コントローラを介して、app/javascript/packs/application.jsで定義されている特定のHTML要素とStimulusApplicationインスタンスにアクセスできます。
  • ターゲットを使用すると、特定のHTML要素を名前で参照でき、特定のコントローラーに関連付けられます。
  • アクションは、DOMイベントがコントローラーによって処理される方法を制御し、特定のコントローラーにも関連付けられます。 これらは、コントローラーに関連付けられたHTML要素、コントローラーで定義されたメソッド、およびDOMイベントリスナーの間に接続を作成します。

パーシャルでは、最初にRailsを使用する場合と同じようにフォームを作成します。 次に、JavaScriptを使用して新しい投稿をページに追加する方法を制御するために、Stimulusコントローラー、アクション、およびターゲットをフォームに追加します。

まず、パーシャルの新しいファイルを作成します。

nano app/views/sharks/_posts.html.erb

ファイル内に次のコードを追加して、form_withヘルパーを使用して新しい投稿オブジェクトを作成します。

〜/ sharkapp / app / views / sharks / _posts.html.erb

        <%= form_with model: [@shark, @shark.posts.build] do |form| %>
                <%= form.text_area :body, placeholder: "Your post here" %>
                <br>
                <%= form.submit %>
        <% end %>

これまでのところ、このフォームは通常のRailsフォームのように動作し、form_withヘルパーを使用して、Postモデル用に定義されたフィールドで投稿オブジェクトを作成します。 したがって、フォームには投稿:bodyのフィールドがあり、それにplaceholderと投稿の入力を求めるプロンプトが追加されています。

さらに、フォームは、SharkモデルとPostモデル間の関連付けに付属する収集メソッドを利用するようにスコープが設定されています。 この場合、ユーザーが送信したデータから作成された新しい投稿オブジェクトは、現在表示しているサメに関連付けられた投稿のコレクションに属します。

ここでの目標は、いくつかのStimulusコントローラー、イベント、およびアクションを追加して、投稿データがページに表示される方法を制御することです。 ユーザーは最終的に投稿データを送信し、Stimulusアクションのおかげでページに投稿されたことがわかります。

まず、<div>要素のpostsというフォームにコントローラーを追加します。

〜/ sharkapp / app / views / sharks / _posts.html.erb

<div data-controller="posts">
        <%= form_with model: [@shark, @shark.posts.build] do |form| %>
                 <%= form.text_area :body, placeholder: "Your post here" %>
                 <br>
                 <%= form.submit %>
        <% end %>
</div>

必ずクロージングを追加してください

鬼ごっこコントローラを適切にスコープします。 次に、フォーム送信イベントによってトリガーされるアクションをフォームに添付します。 このアクションは、ユーザー入力がページにどのように表示されるかを制御します。 これは、postsStimulusコントローラーで定義するaddPostメソッドを参照します。

〜/ sharkapp / app / views / sharks / _posts.html.erb

<div data-controller="posts">
        <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody" } do |form| %>
        . . . 
                 <%= form.submit %>
        <% end %>
</div>

:dataオプションとform_withを使用して、追加のHTMLデータ属性としてStimulusアクションを送信します。 アクション自体には、アクション記述子と呼ばれる値があり、次の要素で構成されています。

  • リッスンするDOMイベント。 ここでは、フォーム要素に関連付けられたデフォルトのイベントsubmitを使用しているため、記述子自体でイベントを指定する必要はありません。 一般的な要素/イベントのペアの詳細については、刺激に関するドキュメントを参照してください。
  • コントローラー識別子、この場合はposts
  • イベントが呼び出す必要のあるメソッド。 この場合、これはコントローラーで定義するaddBodyメソッドです。

次に、:body <textarea>要素で定義されたユーザー入力にデータターゲットをアタッチします。これは、この入力値をaddBodyメソッドで使用するためです。

次の:dataオプションを:body<textarea>要素に追加します。

〜/ sharkapp / app / views / sharks / _posts.html.erb

<div data-controller="posts">
        <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody" } do |form| %>
                <%= form.text_area :body, placeholder: "Your post here", data: { target: "posts.body" } %>
. . .

アクション記述子と同様に、Stimulusターゲットにはターゲット記述子があり、これにはコントローラー識別子とターゲット名が含まれます。 この場合、postsがコントローラーであり、bodyがターゲットそのものです。

最後のステップとして、入力されたbody値のデータターゲットを追加して、ユーザーが送信されるとすぐに投稿を表示できるようにします。

次の<ul>要素をaddターゲットとともに、フォームの下と終了<div>の上に追加します。

〜/ sharkapp / app / views / sharks / _posts.html.erb

. . .
        <% end %>
  <ul data-target="posts.add">
  </ul>

</div>

bodyターゲットと同様に、ターゲット記述子には、コントローラーの名前とターゲットの両方(この場合はadd)が含まれます。

完成したパーシャルは次のようになります。

〜/ sharkapp / app / views / sharks / _posts.html.erb

<div data-controller="posts">
        <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody"} do |form| %>
                <%= form.text_area :body, placeholder: "Your post here", data: { target: "posts.body" } %>
                <br>
                <%= form.submit %>
        <% end %>
  <ul data-target="posts.add">
  </ul>

</div>

これらの変更を行ったら、ファイルを保存して閉じることができます。

これで、sharks/showビューテンプレートに追加した2つのパーシャルのうちの1つが作成されました。 次に、2番目のsharks/allを作成します。これにより、データベースの古い投稿がすべて表示されます。

app/views/sharks/ディレクトリに_all.html.erbという名前の新しいファイルを作成します。

nano app/views/sharks/_all.html.erb

次のコードをファイルに追加して、選択したサメに関連付けられた投稿のコレクションを繰り返し処理します。

〜/ sharkapp / app / views / sharks / _all.html.erb

<% for post in @shark.posts  %>
    <ul>

        <li class="post">
            <%= post.body %>
        </li>

    </ul>
    <% end %>

このコードは、forループを使用して、特定のサメに関連付けられた投稿オブジェクトのコレクション内の各投稿インスタンスを反復処理します。

これで、このパーシャルにいくつかの刺激アクションを追加して、ページ上の投稿の外観を制御できます。 具体的には、賛成票と投稿がページに表示されるかどうかを制御するアクションを追加します

ただし、その前に、プロジェクトにgemを追加して、賛成票の登録に使用する FontAwesomeアイコンを操作できるようにする必要があります。 2番目のターミナルウィンドウを開き、sharkappプロジェクトディレクトリに移動します。

Gemfileを開きます。

nano Gemfile

webpacker gemの下に、次の行を追加して、プロジェクトに font-awesome-railsgemを含めます。

〜/ sharkapp / Gemfile

. . . 
gem 'webpacker', '~> 4.x'
gem 'font-awesome-rails', '~>4.x'
. . . 

ファイルを保存して閉じます。

次に、gemをインストールします。

bundle install

最後に、アプリケーションのメインスタイルシートapp/assets/stylesheets/application.cssを開きます。

nano app/assets/stylesheets/application.css

次の行を追加して、FontAwesomeのスタイルをプロジェクトに含めます。

〜/ sharkapp / app / Assets / stylesheets / application.css

. . . 
*
 *= require_tree .
 *= require_self
 *= require font-awesome
 */

ファイルを保存して閉じます。 これで、2番目のターミナルウィンドウを閉じることができます。

app/views/sharks/_all.html.erbパーシャルに戻ると、クリックイベントでトリガーされる2つのbutton_tagsと関連する刺激アクションを追加できるようになりました。 1つのボタンはユーザーに投稿に賛成するオプションを提供し、もう1つのボタンはユーザーにページビューから投稿を削除するオプションを提供します。

次のコードをapp/views/sharks/_all.html.erbに追加します。

〜/ sharkapp / app / views / sharks / _all.html.erb

<% for post in @shark.posts  %>
    <ul>

        <li class="post">
            <%= post.body %>
            <%= button_tag "Remove Post", data: { controller: "posts", action: "posts#remove" } %>
            <%= button_tag "Upvote Post", data: { controller: "posts", action: "posts#upvote" } %>
        </li>

    </ul>
    <% end %>

ボタンタグも:dataオプションを使用するため、投稿の刺激コントローラーと2つのアクションremoveupvoteを追加しました。 繰り返しになりますが、アクション記述子では、ボタン要素に関連付けられたデフォルトのイベントがクリックであるため、コントローラーとメソッドを定義するだけで済みます。 これらの各ボタンをクリックすると、コントローラーで定義されているそれぞれのremoveおよびupvoteメソッドがトリガーされます。

編集が終了したら、ファイルを保存して閉じます。

コントローラの定義に進む前に行う最後の変更は、sharks/allパーシャルを表示する方法とタイミングを制御するためのデータターゲットとアクションを設定することです。

showテンプレートを再度開きます。ここで、sharks/allをレンダリングするための最初の呼び出しが現在定義されています。

nano app/views/sharks/show.html.erb

ファイルの下部には、現在次のような<div>要素があります。

〜/ sharkapp / app / views / sharks / show.html.erb

. . . 
<div>
  <%= render 'sharks/all' %>
</div>

まず、この<div>要素にコントローラーを追加して、アクションとターゲットをスコープします。

〜/ sharkapp / app / views / sharks / show.html.erb

. . . 
<div data-controller="posts">
  <%= render 'sharks/all' %>
</div>

次に、ページ上のパーシャルの外観を制御するボタンを追加します。 このボタンは、投稿コントローラーでshowAllメソッドをトリガーします。

<div>要素の下とrenderステートメントの上にボタンを追加します。

〜/ sharkapp / app / views / sharks / show.html.erb

. . . 
<div data-controller="posts">

<button data-action="posts#showAll">Show Older Posts</button>

  <%= render 'sharks/all' %>

ここでも、postsコントローラーとshowAllメソッドを識別するだけで済みます。アクションはクリックイベントによってトリガーされます。

次に、データターゲットを追加します。 このターゲットを設定する目的は、ページ上のパーシャルの外観を制御することです。 最終的には、Show Older Postsボタンをクリックして古い投稿を表示することを選択した場合にのみ、ユーザーに古い投稿を表示してもらいたいと考えています。

したがって、showというデータターゲットをsharks/allパーシャルにアタッチし、デフォルトのスタイルを visible:hiddenに設定します。 これにより、ユーザーがボタンをクリックして表示することを選択しない限り、パーシャルが非表示になります。

次の<div>要素をshowターゲットとstyle定義とともに、ボタンの下と部分レンダリングステートメントの上に追加します。

〜/ sharkapp / app / views / sharks / show.html.erb

. . . 
<div data-controller="posts">

<button data-action="posts#showAll">Show Older Posts</button>

<div data-target="posts.show" style="visibility:hidden">
  <%= render 'sharks/all' %>
</div>

必ず終了</div>タグを追加してください。

完成したshowテンプレートは次のようになります。

〜/ sharkapp / app / views / sharks / show.html.erb

<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @shark.name %>
</p>

<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>

<h2>Posts</h2>

<%= render 'sharks/posts' %>

<%= link_to 'Edit', edit_shark_path(@shark) %> |
<%= link_to 'Back', sharks_path %>

<div data-controller="posts">

<button data-action="posts#showAll">Show Older Posts</button>

<div data-target="posts.show" style="visibility:hidden">
  <%= render 'sharks/all' %>
</div>
</div>

編集が終了したら、ファイルを保存して閉じます。

このテンプレートとそれに関連するパーシャルが完成したら、これらのファイルで参照したメソッドを使用してコントローラーの作成に進むことができます。

ステップ6—刺激コントローラーの作成

Stimulusをインストールすると、app/javascript/controllersディレクトリが作成されます。このディレクトリから、webpackがアプリケーションコンテキストをロードします。このディレクトリにpostsコントローラーを作成します。 このコントローラーには、前のステップで参照した各メソッドが含まれます。

  • addBody()、新しい投稿を追加します。
  • showAll()、古い投稿を表示します。
  • remove()、現在のビューから投稿を削除します。
  • upvote()、賛成のアイコンを投稿に添付します。

app/javascript/controllersディレクトリにposts_controller.jsというファイルを作成します。

nano app/javascript/controllers/posts_controller.js

まず、ファイルの先頭で、Stimulusの組み込みControllerクラスを拡張します。

〜/sharkapp/app/javascript/controllers/posts_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
}

次に、次のターゲット定義をファイルに追加します。

〜/sharkapp/app/javascript/controllers/posts_controller.js

. . .
export default class extends Controller {
    static targets = ["body", "add", "show"]
}

このようにターゲットを定義すると、this.target-nameTargetプロパティを使用してメソッドでターゲットにアクセスできるようになり、最初に一致するターゲット要素が得られます。 したがって、たとえば、targets配列で定義されたbodyデータターゲットと一致させるには、this.bodyTargetを使用します。 このプロパティを使用すると、入力値やcssスタイルなどを操作できます。

次に、addBodyメソッドを定義できます。このメソッドは、ページ上の新しい投稿の表示を制御します。 このメソッドを定義するには、ターゲット定義の下に次のコードを追加します。

〜/sharkapp/app/javascript/controllers/posts_controller.js

. . .
export default class extends Controller {
    static targets = [ "body", "add", "show"]

    addBody() {
        let content = this.bodyTarget.value;
        this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
    }
}

このメソッドは、letキーワードを使用してcontent変数を定義し、ユーザーが投稿フォームに入力した投稿入力文字列と同じ値に設定します。 これは、フォームの<textarea>要素にアタッチしたbodyデータターゲットによって行われます。 this.bodyTargetを使用してこの要素を照合すると、その要素に関連付けられている valueプロパティを使用して、入力後のユーザーが入力したcontentの値を設定できます。

次に、このポスト入力を、sharks/postsパーシャルのフォームビルダーの下の<ul>要素に追加したaddターゲットに追加します。 これは、 Element.insertAdjacentHTML()メソッドを使用して行います。これにより、content変数で設定された新しい投稿のコンテンツが、addターゲット要素の前に挿入されます。 また、新しい投稿を<li>要素で囲んで、新しい投稿が箇条書きのアイテムとして表示されるようにしました。

次に、addBodyメソッドの下に、showAllメソッドを追加できます。これにより、ページ上の古い投稿の外観が制御されます。

〜/sharkapp/app/javascript/controllers/posts_controller.js

. . . 
export default class extends Controller {
. . .
    addBody() {
        let content = this.bodyTarget.value;
        this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
    }

    showAll() {
        this.showTarget.style.visibility = "visible";
    }

}

ここでも、this.target-nameTargetプロパティを使用して、<div>要素にsharks/allパーシャルでアタッチされているshowターゲットを一致させます。 デフォルトのスタイル"visibility:hidden"を指定したため、このメソッドでは、スタイルを"visible"に変更するだけです。 これにより、古い投稿を表示することを選択したユーザーに部分的に表示されます。

showAllの下に、次にupvoteメソッドを追加して、ユーザーが free Font Awesome check-circleを添付して、ページ上の投稿に「賛成」できるようにします。 ]特定の投稿へのアイコン。

このメソッドを定義するには、次のコードを追加します。

〜/sharkapp/app/javascript/controllers/posts_controller.js

. . . 
export default class extends Controller {
. . . 

    showAll() {
        this.showTarget.style.visibility = "visible";
    }

    upvote() {
        let post = event.target.closest(".post");
        post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
    }

}

ここでは、クラスpost(の各<li>要素にアタッチしたクラス)を持つ最も近い<li>要素をターゲットとするpost変数を作成しています。 sharks/allでのループ反復。 これにより、最も近い投稿がターゲットになり、check-circleアイコンが<li>要素のすぐ内側の最後の子の後に追加されます。

次に、同様の方法を使用してページ上の投稿を非表示にします。 upvoteメソッドの下に次のコードを追加して、removeメソッドを定義します。

〜/sharkapp/app/javascript/controllers/posts_controller.js

. . . 
export default class extends Controller {
. . . 

    upvote() {
        let post = event.target.closest(".post");
        post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
    }

    remove() {
        let post = event.target.closest(".post");
        post.style.visibility = "hidden";
    }
}

ここでも、post変数は、クラスpostを持つ最も近い<li>要素をターゲットにします。 次に、visibilityプロパティを"hidden"に設定して、ページ上の投稿を非表示にします。

完成したコントローラーファイルは次のようになります。

〜/sharkapp/app/javascript/controllers/posts_controller.js

import { Controller } from "stimulus"

export default class extends Controller {

    static targets = ["body", "add", "show"]

    addBody() {
        let content = this.bodyTarget.value;
        this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
    }

    showAll() {
        this.showTarget.style.visibility = "visible";
    }

    upvote() {
        let post = event.target.closest(".post");
        post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
    }

    remove() {
        let post = event.target.closest(".post");
        post.style.visibility = "hidden";
    }
} 

編集が終了したら、ファイルを保存して閉じます。

Stimulusコントローラーを配置したら、indexビューに最終的な変更を加えてアプリケーションをテストすることができます。

ステップ7—インデックスビューの変更とアプリケーションのテスト

sharks indexビューに最後の変更を加えると、アプリケーションをテストする準備が整います。 indexビューは、 How To Build Ruby on RailsApplicationステップ4で設定したアプリケーションのルートです。

ファイルを開きます。

nano app/views/sharks/index.html.erb

サメを表示して破壊するために自動生成されたlink_toヘルパーの代わりに、button_toヘルパーを使用します。 これにより、[でjavascript_include_tagjavascript_pack_tagに変更したときに、手順1で使用しなくなると指定したデフォルトのRailsJavaScriptアセットの代わりに生成されたHTMLコードを操作できます。 X195X]。

ファイル内の既存のlink_toヘルパーを次のbutton_toヘルパーに置き換えます。

〜/ sharkapp / app / views / sharks / index.html.erb

. . . 
  <tbody>
    <% @sharks.each do |shark| %>
      <tr>
        <td><%= shark.name %></td>
        <td><%= shark.facts %></td>
        <td><%= button_to 'Show', shark_path(:id => shark.id), :method => :get %></td>
        <td><%= button_to 'Edit', edit_shark_path(:id => shark.id), :method => :get %></td>
        <td><%= button_to 'Destroy', shark_path(:id => shark.id), :method => :delete %></td>
      </tr>
    <% end %>
  </tbody>
. . . 

これらのヘルパーは、対応するlink_toとほぼ同じことを実行しますが、Destroyヘルパーは、RailsのデフォルトのJavaScriptではなく生成されたHTMLに依存するようになりました。

編集が終了したら、ファイルを保存して閉じます。

これで、アプリケーションをテストする準備が整いました。

まず、データベースの移行を実行します。

rails db:migrate

次に、サーバーを起動します。 ローカルで作業している場合は、次のコマンドを使用してこれを行うことができます。

rails s

開発サーバーで作業している場合は、次のコマンドでアプリケーションを起動できます。

rails s --binding=your_server_ip

ブラウザでアプリケーションのランディングページに移動します。 ローカルで作業している場合、これはlocalhost:3000になり、サーバーで作業している場合はhttp://your_server_ip:3000になります。

次のランディングページが表示されます。

Show をクリックすると、このサメのshowビューに移動します。 ここに、投稿に記入するためのフォームが表示されます。

投稿フォームに「これらのサメは怖いです!」と入力します。

投稿の作成をクリックします。 このページに新しい投稿が表示されます。

![ページに新しい投稿が追加されました]( https://assets.digitalocean.com/articles/stimulus/stimulus_show_post.png .png)

必要に応じて、別の新しい投稿を追加できます。 今回は、「これらのサメは映画で誤って表現されることが多い」と入力し、投稿の作成をクリックします。

古い投稿を表示機能の機能をテストするには、このページを離れる必要があります。これは、現在、グレートホワイトには、追加したばかりの投稿よりも古い投稿がないためです。

戻るをクリックしてメインページに移動し、表示をクリックしてグレートホワイトのランディングページに戻ります。

古い投稿を表示をクリックすると、作成した投稿が表示されます。

[Upvote Post] ボタンをクリックして、投稿に賛成票を投じることができます。

同様に、 [投稿を削除]をクリックすると、投稿が非表示になります。

これで、Stimulusを使用してネストされた投稿リソースを個々のサメページに表示する方法を制御するRailsアプリケーションが機能していることを確認しました。 これは、Stimulusを使用した将来の開発と実験の出発点として使用できます。

結論

Stimulusは、 rails-ujsJQuery 、およびReactやVueなどのフレームワークを操作するための可能な代替手段を表しています。

はじめに説明したように、Stimulusは、サーバーによって生成されたHTMLを直接操作する必要がある場合に最も効果的です。 軽量で、コード、特にHTMLを可能な限り自明にすることを目的としています。 クライアント側で状態を管理する必要がない場合は、Stimulusが適しています。

Stimulusを統合せずにネストされたリソースを作成する方法に興味がある場合は、 RubyonRailsアプリケーションのネストされたリソースを作成する方法を参照してください。

ReactをRailsアプリケーションと統合する方法の詳細については、ReactFrontendを使用してRubyonRailsプロジェクトをセットアップする方法を参照してください。