分类
文章

如何向ruby on rails应用程序添加stimulus

介绍

如果您正在使用Ruby on Rails项目,则您的需求可能包括与视图模板生成的HTML的交互性。如果是这样,您将有几种选择来实现这种交互性。

例如,您可以实现一个JavaScript框架,例如ReactEmber 。如果您的需求包括在客户端上处理状态,或者您担心与服务器频繁查询相关的性能问题,那么选择其中一种框架可能是有意义的。许多单页应用程序(SPA)都采用这种方法。

但是,在实现用于管理客户端状态和频繁更新的框架时,需要牢记一些注意事项:

  1. 可能需要满足加载和转换要求(例如解析JavaScript以及将JSON提取并将其转换为HTML)来限制性能。
  2. 对框架的承诺可能涉及编写超出特定用例要求的代码,特别是在您正在寻找小规模JavaScript增强功能的情况下。
  3. 在客户端和服务器端管理的状态可能导致工作重复,并增加了出现错误的表面积。

作为替代方案, Basecamp的团队(与编写Rails的团队相同)创建了Stimulus.js ,他们将其描述为”用于现有HTML的适度JavaScript框架”。Stimulus旨在通过使用服务器端生成的HTML来增强现代的Rails应用程序。状态存在于文档对象模型(DOM)中 ,并且该框架提供了与DOM中的元素和事件进行交互的标准方式。它与Turbolink (默认情况下包含在Rails 5+中)的Turbolink并用,以限制代码的范围和明确定义的目的,从而提高性能和加载时间。

在本教程中,您将安装并使用Stimulus在现有的Rails应用程序上构建,该应用程序为读者提供有关鲨鱼的信息。该应用程序已经有一个用于处理鲨鱼数据的模型,但是您将为有关单个鲨鱼的帖子添加一个嵌套资源,从而允许用户建立有关鲨鱼的思想和观点。这段代码与如何为Ruby on Rails应用程序创建嵌套资源大致平行,除了我们将使用JavaScript来操纵页面上帖子的位置和外观外。我们还将采用略有不同的方法来构建邮政模型本身。

先决条件

要遵循本教程,您将需要:

第1步-创建嵌套模型

我们的第一步将是创建嵌套的Post 模型 ,并将其与现有的Shark模型关联。我们将通过在模型之间创建Active Record associations来做到这一点:帖子将属于特定的鲨鱼,每个鲨鱼可以有多个帖子。

首先,在先决条件中导航到为Rails项目创建的sharkapp目录:

  • cd sharkapp

为了创建Post模型,我们将在model生成器中使用rails generate命令。键入以下命令来创建模型:

  • rails generate model Post body:text shark:references

通过body:text ,我们告诉Rails在posts数据库表(映射到Post模型的表)中包含body字段。我们还包括:references关键字,它在SharkPost模型之间建立了关联。具体来说,这将确保将代表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模型, app/models/shark.rb使用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

值得考虑的一件事是,删除特定的鲨鱼后帖子会发生什么。我们可能不希望与删除的鲨鱼相关的帖子保留在数据库中。为确保在删除该鲨鱼后删除与该鲨鱼相关的所有帖子,我们可以在该关联中包括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+XY ,然后按ENTER

现在,您已经为帖子生成了一个模型,但是您还需要一个控制器来协调数据库中的数据与生成并呈现给用户的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类本身,有两个沿private方法: get_shark ,这将使我们引用一个特定的鲨鱼,和post_params ,这让我们用的方式获得用户提交的信息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键以及用户输入的用于创建帖子的数据,来获取与您的帖子相关联的特定鲨鱼实例的方法。这两个对象现在都可用于您定义的用于处理创建和销毁帖子的方法。

接下来,在private方法上方,将以下代码添加到文件中,以定义您的createdestroy方法:

~/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实例相关联,并使用当我们在鲨鱼和职位之间创建has_many关联时可用的收集方法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视图处理它们。因此,在此视图中,我们要解决的第一件事是如何接受用户对新帖子的输入,以及如何将帖子呈现给用户。

Note:有关此方法的替代方法,请参见如何为Ruby on Rails应用程序创建嵌套资源 ,该方法使用在posts控制器中定义的全部Create,Read,Update,Delete (CRUD)方法来设置发布视图。有关这些方法及其工作方式的讨论,请参见如何构建Ruby on Rails应用程序的 步骤3

与其将所有功能都构建到该视图中,我们将使用partials-用于特定功能的可重用模板。我们将为新帖子创建一个部分,而另一部分将控制如何将帖子显示回用户。在整个过程中,我们将考虑如何以及在何处使用Stimulus来操纵页面上帖子的外观,因为我们的目标是使用JavaScript控制帖子的显示。

首先,在shark事实下方,添加<h2>标头作为帖子,并添加一行以渲染部分名为sharks/posts :

~/sharkapp/app/views/sharks/show.html.erb
. . .
<p>
  <strong>Facts:</strong>
  <%= @shark.facts %>
</p>
<h2>Posts</h2>
<%= render 'sharks/posts' %>
. . .

这将使用表单构建器为新的post对象渲染部分对象。

接下来,在” Edit和” Back链接下方,我们将添加一个部分来控制页面上较旧帖子的显示。sharks/all添加到文件中以渲染名为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>元素将很有用。

完成这些编辑后,保存并关闭文件。通过在Rails方面所做的更改,您现在可以继续安装Stimulus并将其集成到您的应用程序中。

第4步-安装刺激

使用Stimulus的第一步是安装和配置我们的应用程序以使其与之配合使用。这将包括确保我们具有正确的依赖关系,包括Yarn程序包管理器和Webpacker ,这是使我们能够与JavaScript预处理程序和捆绑程序webpack一起使用的宝石 。有了这些依赖关系之后,我们将能够安装Stimulus并使用JavaScript来操纵DOM中的事件和元素。

让我们从安装Yarn开始。首先,更新您的包裹清单:

  • sudo apt update

接下来,为Debian Yarn存储库添加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 ,您可以继续将webpacker gem添加到您的项目中。

打开项目的Gemfile,其中列出了项目的gem依赖关系:

  • nano Gemfile

在文件内部,您将看到默认情况下启用了Turbolinks:

~/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>部分的内容,而JavaScript windowdocument对象以及<html>元素在渲染之间保持不变。这解决了页面加载时间缓慢的主要原因之一:CSS和JavaScript资源的重新加载。

默认情况下,我们在Gemfile中获得Turbolinks,但是我们将需要添加webpacker gem,以便我们可以安装和使用Stimulus。在turbolinks gem下方,添加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中:

  • bundle

这将生成一个新的Gemfile.lock文件-项目的gem和版本的Gemfile.lock记录。

接下来,使用以下bundle exec命令将gem安装在您的bundle的上下文中:

  • bundle exec rails webpacker:install

安装完成后,我们将需要对应用程序的内容安全文件进行一些小的调整。这是由于我们正在使用Rails 5.2+的事实,Rails 5.2+是受内容安全策略(CSP)限制的环境,这意味着应用程序中允许的唯一脚本必须来自受信任的来源。

打开config/initializers/content_security_policy.rb ,这是Rails为我们定义应用程序范围的安全策略提供的默认文件:

  • 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目录中创建了两个新目录,即您的主要应用程序代码所在的目录。新的父目录app/javascript将是项目的JavaScript代码所在的目录,并且具有以下结构:

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

app/javascript目录将包含两个子目录: app/javascript/packs (将具有您的webpack入口点)和app/javascript/controllers (将在其中定义Stimulus 控制器) 。我们刚刚使用的bundle exec命令将创建app/javascript/packs目录,但是我们需要安装Stimulus才能自动生成app/javascript/controllers目录。

安装了webpacker ,我们现在可以使用以下命令安装Stimulus:

  • bundle exec rails webpacker:install:stimulus

您将看到类似以下的输出,指示安装成功:

Output. . .
success Saved lockfile.
success Saved 5 new dependencies.
info Direct dependencies
└─ stimulus@1.1.1
info All dependencies
├─ @stimulus/core@1.1.1
├─ @stimulus/multimap@1.1.1
├─ @stimulus/mutation-observers@1.1.1
├─ @stimulus/webpack-helpers@1.1.1
└─ stimulus@1.1.1
Done in 8.30s.
Webpacker now supports Stimulus.js  

现在,我们已经安装了Stimulus,并需要使用它的主要目录。在继续编写任何代码之前,我们需要进行一些应用程序级调整以完成安装过程。

首先,我们需要对app/views/layouts/application.html.erb进行调整,以确保我们的JavaScript代码可用,并确保在我们的主要webpacker入口点app/javascript/packs/application.js定义的代码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目录中的app/javascript/controllers并加载此上下文以在您的应用程序中使用。

完成编辑后,保存并关闭文件。

现在,您已经安装了Stimulus,可以在您的应用程序中使用它了。接下来,我们将使用Stimulus controllerstargetsactions构建在”鲨鱼” show视图中引用的局部对象(” sharks/posts和” sharks/all

第5步—在Rails部分中使用刺激

我们的form_with sharks/posts部分将使用form_with表单助手创建一个新的post对象。它还将利用Stimulus的三个核心概念:控制器,目标和动作。这些概念的工作方式如下:

  • 控制器是在JavaScript模块中定义并作为模块的默认对象导出的JavaScript类。通过控制器,您可以访问特定的HTML元素和app/javascript/packs/application.js定义的Stimulus Application实例。
  • 目标允许您按名称引用特定的HTML元素,并且与特定的控制器相关联。
  • 动作控制控制器如何处理DOM事件,并且还与特定的控制器相关联。它们在与控制器关联的HTML元素,控制器中定义的方法以及DOM事件侦听器之间创建连接。

在本部分中,我们首先要像通常使用Rails一样构建一个表单。然后,我们将在表单中添加一个Stimulus控制器,动作和目标,以便使用JavaScript控制如何将新帖子添加到页面。

首先,为部分文件创建一个新文件:

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

在文件内部,添加以下代码以使用form_with helper创建新的post对象:

~/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模型定义的字段的post对象。因此,表单在post :body字段中具有一个字段,在该字段中添加了placeholder ,并提示您填写帖子。

此外,该表单的范围仅限于利用SharkPost模型之间的关联所附带的收集方法。在这种情况下,根据用户提交的数据创建的新post对象将属于与当前正在查看的鲨鱼相关的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>

Make sure you add the closing <div> tag以正确确定控制器的范围。

接下来,我们将对表单附加一个动作,该动作将由表单提交事件触发。此操作将控制用户输入在页面上的显示方式。它将引用一个addPost方法,该方法将在post Stimulus控制器中定义:

~/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一起使用,以将Stimulus操作作为附加HTML数据属性提交。动作本身具有一个称为action descriptor的值,该值由以下各项组成:

  • The DOM event to listen for 。在这里,我们使用与表单元素Submit关联的默认事件,因此我们不需要在描述符本身中指定事件。有关常见元素/事件对的更多信息,请参见” 激励”文档
  • The controller identifier ,在我们的例子中是posts
  • The method that the event should invoke 。在我们的例子中,这是我们将在控制器中定义的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" } %>
. . .

与动作描述符非常相似,激励目标具有target descriptors ,其中包括控制器标识符和目标名称。在这种情况下, posts是我们的控制者, body是目标本身。

最后一步,我们将为输入的body值添加一个数据目标,以便用户在提交后即可查看其帖子。

在表单下方和结束<div>上方添加以下<ul>元素和add目标:

~/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视图模板的两个部分之一。接下来,您将创建第二个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循环遍历与特定鲨鱼关联的post对象的集合中的每个post实例。

现在,我们可以向此部分添加一些刺激动作,以控制页面上帖子的外观。具体来说,我们将添加操作来控制投票以及页面上是否显示帖子

但是,在执行此操作之前,我们需要在项目中添加一个gem,以便我们可以使用Font Awesome图标,并将其用于注册upvotes。打开第二个终端窗口,然后导航到您的sharkapp项目目录。

打开您的Gemfile:

  • nano Gemfile

webpacker gem的下面,添加以下行以在项目中包括font-awesome-rails gem :

~/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

添加以下行以在项目中包括Font Awesome的样式:

~/sharkapp/app/assets/stylesheets/application.css
. . .
*
 *= require_tree .
 *= require_self
 *= require font-awesome
 */

保存并关闭文件。现在,您可以关闭第二个终端窗口。

回到您的app/views/sharks/_all.html.erb部分中,您现在可以添加两个带有关联的刺激动作的button_tags ,这些动作将在点击事件时触发。一个按钮将为用户提供对帖子进行投票的选项,而另一个按钮将为用户提供将其从页面视图中删除的选项。

将以下代码添加到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选项,因此我们添加了Stimulus控制器帖子和两个操作: removeupvote 。再一次,在动作描述符中,我们只需要定义我们的控制器和方法,因为与按钮元素关联的默认事件是click。单击这些按钮中的每个按钮将触发在我们的控制器中定义的相应的removeupvote方法。

完成编辑后,保存并关闭文件。

在继续定义控制器之前,我们要做的最后更改是设置数据目标和操作,以控制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>

接下来,添加一个按钮来控制部分在页面上的外观。此按钮将在我们的posts控制器中触发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方法-该操作将由click事件触发。

接下来,我们将添加一个数据目标。设置此目标的目的是控制页面上局部的外观。最终,我们希望用户仅在通过单击” Show Older Posts按钮选择加入后才看到Show Older Posts

因此,我们将一个名为show的数据目标附加到sharks/all部分,并将其默认样式设置为visibility:hidden 。这将隐藏部分内容,除非用户选择通过单击按钮来查看它。

在按钮下方和部分渲染语句上方,添加以下具有show目标和style定义的<div>元素:

~/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>

Be sure to add the closing </div> tag.

完成的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() ,将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-name Target在我们的方法中访问它们this. target-name Target属性,它为我们提供了第一个匹配的target元素。因此,例如,为了匹配在目标数组中定义的body数据目标,我们将使用this. body Target 。此属性使我们可以操纵输入值或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变量,并将其设置为等于用户输入到post表单中的post输入字符串。它依靠我们附加到表单中的<textarea>元素的body数据目标来实现。使用this.bodyTarget来匹配此元素,然后我们可以使用与该元素关联的value属性来设置用户输入的post输入content的值。

接下来,该方法将此post输入addadd目标,该add目标是我们在sharks/posts部分中的表单生成器下方添加到<ul>元素的。它使用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-name Target属性以匹配我们的show目标,该show目标与sharks/all部分附加到<div>元素。我们为其指定了默认样式"visibility:hidden" ,因此在此方法中,我们只需将样式更改为"visible" 。这将向选择查看较旧帖子的用户显示偏爱。

showAll下方,我们接下来将添加一个upvote方法,以允许用户通过将免费的 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变量,该变量将以class post目标,后者是最接近的<li>元素-我们在sharks/all中的循环迭代中附加到每个<li>元素的类。这将以最近的帖子为目标,并在<li>元素的最后一个子元素之后添加check-circle图标。

接下来,我们将使用类似的方法来隐藏页面上的帖子。在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变量将目标最接近的<li>与类元素post 。然后,它将可见性属性设置为"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视图是应用程序的根,您可以在如何构建Ruby on Rails应用程序的 步骤4中对其进行设置。

打开文件:

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

代替为我们自动生成以显示和销毁鲨鱼的link_to帮助器,我们将使用button_to帮助器。这将帮助我们生成的HTML代码,而不是默认的工作Rails的JavaScript的资产,这是我们指定的,我们会在第1步不再使用时,当我们改变javascript_include_tagjavascript_pack_tagapp/views/layouts/application.html.erb

将文件中现有的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同行的link_to ,但是Destroy助手现在依靠生成的HTML,而不是Rails的默认JavaScript。

完成编辑后,保存并关闭文件。

现在您可以测试您的应用程序了。

首先,运行数据库迁移:

  • rails db:migrate

接下来,启动服务器。如果您在本地工作,则可以使用以下命令执行此操作:

  • rails s

如果您在开发服务器上工作,则可以使用以下命令启动应用程序:

  • rails s --binding=your_server_ip

在浏览器中导航到应用程序登录页面。如果您在本地工作,则为localhost:3000 ,如果您在服务器上工作,则为http:// your_server_ip :3000

您将看到以下登录页面:

应用登陆页面

单击” Show将转到该鲨鱼的show视图。在这里,您将看到一个填写帖子的表格:

Shark显示页面

在帖子中,键入”这些鲨鱼很吓人!”:

填写帖子

单击Create Post 。您现在将在页面上看到新的帖子:

新帖子已添加到页面

您可以根据需要添加其他新帖子。这次,键入”这些鲨鱼在电影中经常被曲解”,然后单击” Create Post :

第二篇文章已添加到页面

为了测试” Show Older Posts功能的功能,我们将需要离开此页面,因为我们的”伟大的白人”目前没有比我们刚刚添加的帖子更早的帖子。

单击”上Back主页,然后单击Back Show以返回”大白”登陆页面:

Shark显示页面

现在,单击Show Older Posts将向您显示您创建的帖子:

显示较旧的帖子

现在,您可以通过单击” Upvote Post按钮来对Upvote Post进行Upvote Post :

顶帖

同样,单击” Remove Post将隐藏该帖子:

删除帖子

现在,您已经确认您有一个正常的Rails应用程序,该应用程序使用Stimulus来控制如何在单个鲨鱼页面上显示嵌套的帖子资源。您可以将其用作未来开发和使用Stimulus进行试验的起点。

结论

刺激代表了使用rails-ujsJQuery和React和Vue等框架的可能替代方案。

如引言中所述,当您需要直接使用服务器生成的HTML时,Stimulus最有意义。它是轻量级的,旨在最大程度地自我解释代码,尤其是HTML。如果您不需要在客户端上管理状态,则刺激可能是一个不错的选择。

如果您对如何在不进行Stimulus集成的情况下创建嵌套资源感兴趣,可以咨询如何为Ruby on Rails应用程序创建嵌套资源

有关如何将React与Rails应用程序集成的更多信息,请参见如何使用React Frontend设置Ruby on Rails项目

发表评论

电子邮件地址不会被公开。 必填项已用*标注