如何为 Rails 上的 Ruby 应用程序添加刺激因素

简介

如果您正在使用Ruby on rails项目),您的需求可能包括与您的查看templates.]生成的Html进行一些交互如果是这样的话,对于如何实现这种交互,您有一些选择。

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

但是,在实现管理客户端上的状态和频繁更新的框架时,有几个注意事项需要记住:

1.加载和转换要求--比如解析JavaScript、获取JSON并将其转换为HTML --可能会限制性能。 2.对框架的承诺可能涉及编写比特定用例所需更多的代码,特别是如果您正在寻找小规模的JavaScript增强。 3.在客户端和服务器端同时管理状态可能会导致重复工作,并增加错误的表面积。

作为替代方案,Basecamp(编写Rails的同一个团队)的团队已经创建了Stimulus.js,),他们将其描述为)](https://www.digitalocean.com/community/tutorial_series/understanding-the-dom-document-object-model),中,该框架提供了与DOM中的元素和事件交互的标准方法。它与Turbolinks(默认情况下包含在rails 5+中)并列工作,以改进性能并缩短代码加载时间,这些代码被限制在明确定义的目的范围内。

在本教程中,您将安装并使用SIMULATION来构建一个现有的Rails应用程序,该应用程序为读者提供有关鲨鱼的信息。这个应用程序已经有了一个处理鲨鱼数据的模型,但您将为有关单个鲨鱼的帖子添加一个嵌套的资源,允许用户构建关于鲨鱼的思想和观点的主体。这篇文章与How to Create Nest Resources for a Ruby on rails Application,]大致平行,只是我们将使用JAVASCRIPT来操作页面上帖子的位置和外观。我们还将采用略有不同的方法来构建POST模型本身。

前提条件

要遵循本教程,您需要:

  • 运行Ubuntu 18.04的本地机器或开发服务器。您的开发机器应该有一个拥有管理权限的非超级用户,并且防火墙配置了ufw。有关如何设置的说明,请参阅我们的使用Ubuntu 18.04的初始服务器设置》教程。
  • 本地机器或开发服务器上安装了node.jsnpm)。本教程使用Node.js版本10.16.3和NPM版本6.9.0。有关在Ubuntu18.04上安装node.js和npm的指导,请按照How to Install Node.js on Ubuntu 18.04.》(如何在Ubuntu上安装Node.js)的 使用PPA安装** 部分中的说明进行操作
  • ruby,rbenv,和rails安装在本地机器或开发服务器上,遵循如何在Ubuntu 18.04.上使用rbenv安装Ruby on rails》中的 步骤1-4** 本教程使用Ruby 2.5.1、rbenv 1.1.2和rails 5.2.3。
  • 安装了SQLite,并创建了一个基本的鲨鱼信息应用程序,遵循How to Build a Ruby on rails Application.]中的说明

第一步-创建嵌套模型

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

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

1cd sharkapp

要创建我们的Post模型,我们将使用带有generate](https://guides.rubyonrails.org/command_line.html# rails-generate)生成器的[rails模型命令。键入以下命令以创建模型:

1rails generate model Post body:text shark:references

通过body:ext,我们告诉Rails在posts数据库表--映射到post模型的表--中包含一个body字段。我们还加入了:References关键字,该关键字在SharkPost模型之间建立了关联。具体地说,这将确保将表示Sharks数据库中每个Shark条目的外来key]添加到posts数据库中。

运行该命令后,您将看到确认Rails为应用程序生成的资源的输出。在继续之前,您可以检查数据库迁移文件,以查看模型和数据库表之间现在存在的关系。使用以下命令查看文件的内容,确保用您自己的迁移文件上的时间戳替换此处所示的时间戳:

1cat db/migrate/20190805132506_create_posts.rb

您将看到以下输出:

 1[secondary_label Output]
 2class CreatePosts < ActiveRecord::Migration[5.2]
 3  def change
 4    create_table :posts do |t|
 5      t.text :body
 6      t.references :shark, foreign_key: true
 7
 8      t.timestamps
 9    end
10  end
11end

如您所见,该表包含一个用于Shark外键的列。该密钥的形式为Model_name_id,在我们的示例中为Shark_id

Rails也在其他地方建立了模型之间的关系。使用以下命令查看新生成的Post模型:

1cat app/models/post.rb
1[secondary_label Output]
2class Post < ApplicationRecord
3  belongs_to :shark
4end

在其中声明模型的单个实例属于命名模型的单个实例的模型之间建立关系。在我们的应用程序中,这意味着一个帖子属于一个鲨鱼。

虽然Rails已经在我们的Post模型中设置了belongs_to关联,但我们还需要在Shark模型中指定一个has_many关联,以便该关系正常工作。

要将has_many关联添加到Shark模型中,请使用Nan或您喜欢的编辑器打开app/Models/Shark.rb

1nano app/models/shark.rb

在文件中添加以下行,以建立鲨鱼和帖子之间的关系:

1[label ~/sharkapp/app/models/shark.rb]
2class Shark < ApplicationRecord
3  has_many :posts
4  validates :name, presence: true, uniqueness: true
5  validates :facts, presence: true
6end

这里值得思考的一件事是,一旦某条鲨鱼被删除,帖子会发生什么。我们可能不希望与已删除的鲨鱼相关的帖子在数据库中持续存在。为了确保在删除某条鲨鱼时删除与该鲨鱼相关的任何帖子,我们可以在关联中包含dependent选项。

将以下代码添加到文件中,以确保对给定Shark执行的delestroy操作删除所有关联的帖子:

1[label ~/sharkapp/app/models/shark.rb]
2class Shark < ApplicationRecord
3  has_many :posts, dependent: :destroy
4  validates :name, presence: true, uniqueness: true
5  validates :facts, presence: true
6end

完成这些更改后,保存并关闭该文件。如果您正在使用Nan,请按CTRL+XY,然后按Enter

现在您已经为您的帖子生成了一个模型,但是您还需要一个controller来协调数据库中的数据和生成并呈现给用户的HTML语言。

第二步-为嵌套资源创建控制器

创建POST控制器将涉及在应用程序的主路由文件中设置嵌套的资源路由,并创建控制器文件本身以指定我们希望与特定操作相关联的方法。

首先,打开config/routes.rb文件,建立资源路由之间的关系:

1nano config/routes.rb

目前,该文件如下所示:

1[label ~/sharkapp/config/routes.rb]
2Rails.application.routes.draw do
3  resources :sharks
4
5  root 'sharks#index'
6  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
7end

我们希望在Shark和帖子资源之间建立一种Dependent relationship]关系。为此,请更新您的路由声明,使:Sharks成为:posts的父级。更新文件中的代码,使其如下所示:

1[label ~/sharkapp/config/routes.rb]
2Rails.application.routes.draw do
3  resources :sharks do
4    resources :posts
5  end
6  root 'sharks#index'
7  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
8end

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

接下来,为控制器创建一个名为app/Controlters/POSTS_CONTROLER.rb的新文件:

1nano app/controllers/posts_controller.rb

在这个文件中,我们将定义用于创建和销毁单个帖子的方法。然而,由于这是一个嵌套模型,我们还需要创建一个本地实例变量@shark,我们可以使用它将特定的帖子与特定的鲨鱼相关联。

首先,我们可以创建PostsController类本身,以及两个Private方法:Get_Shark,它允许我们引用特定的鲨鱼;以及post_params,它允许我们通过Params method.]访问用户提交的信息

将以下代码添加到文件中:

 1[label ~/sharkapp/app/controllers/posts_controller.rb]
 2class PostsController < ApplicationController
 3  before_action :get_shark 
 4
 5  private
 6
 7  def get_shark
 8    @shark = Shark.find(params[:shark_id])
 9  end
10
11  def post_params
12    params.require(:post).permit(:body, :shark_id)
13  end
14end

现在,您已经有了方法来获取将与您的帖子相关联的特定Shark实例,使用:Shark_id键,以及用户输入的数据来创建帖子。这两个对象现在都可用于您将定义的处理创建和销毁帖子的方法。

接下来,在Priate方法之上,在文件中添加以下代码来定义您的createdelesty方法:

 1[label ~/sharkapp/app/controllers/posts_controller.rb]
 2. . .
 3  def create
 4    @post = @shark.posts.create(post_params)
 5  end
 6
 7  def destroy
 8    @post = @shark.posts.find(params[:id])
 9    @post.destroy   
10  end
11. . .

这些方法将@post实例与特定的@Shark实例相关联,并使用在Shark和Post之间创建has_many关联时可用的集合methods]。像findcreate这样的方法允许我们针对与特定鲨鱼相关的帖子集合。

完成的文件将如下所示:

 1[label ~/sharkapp/app/controllers/posts_controller.rb]
 2class PostsController < ApplicationController
 3  before_action :get_shark 
 4
 5  def create
 6    @post = @shark.posts.create(post_params)
 7  end
 8
 9  def destroy
10    @post = @shark.posts.find(params[:id])
11    @post.destroy   
12  end
13
14  private
15
16  def get_shark
17    @shark = Shark.find(params[:shark_id])
18  end
19
20  def post_params
21    params.require(:post).permit(:body, :shark_id)
22  end
23end

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

准备好控制器和模型后,您就可以开始考虑视图模板以及如何组织应用程序生成的HTML。

第三步-使用Partials重新组织视图

您已经创建了一个Post模型和控制器,所以从Rails的角度来看,最后要考虑的事情将是呈现并允许用户输入有关鲨鱼的信息的视图。观点也是你将有机会与刺激计划建立互动的地方。

在这一步中,你将画出你的观点和部分,这将是你处理刺激工作的起点。

将作为帖子和与帖子相关联的所有部分的基础的视图是Sharks/show视图。

打开文件:

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

目前,该文件如下所示:

 1[label ~/sharkapp/app/views/sharks/show.html.erb]
 2<p id="notice"><%= notice %></p>
 3
 4<p>
 5  <strong>Name:</strong>
 6  <%= @shark.name %>
 7</p>
 8
 9<p>
10  <strong>Facts:</strong>
11  <%= @shark.facts %>
12</p>
13
14<%= link_to 'Edit', edit_shark_path(@shark) %> |
15<%= link_to 'Back', sharks_path %>

当我们创建我们的Post‘模型时,我们选择不为我们的帖子生成视图,因为我们将通过Sharks/show`视图来处理它们。因此,在这个视图中,我们要解决的第一件事是如何接受用户对新帖子的输入,以及如何将帖子呈现回用户。

<$>[备注] 注意: 有关此方法的替代方法,请参阅如何为Ruby on rails Application,创建嵌套资源,该资源使用POSTS控制器中定义的所有创建、读取、更新、删除(CRUD)方法设置POST视图。有关这些方法及其工作原理的讨论,请参阅How to Build a Ruby on rails 3Application](https://andsky.com/tech/tutorials/how-to-build-a-ruby-on-rails-application).]的[步骤 <$>

我们将使用提供特定功能的部分可重用模板,而不是将所有功能构建到该视图中。我们将为新帖子创建一个部分内容,并创建另一个部分内容来控制如何将帖子显示回用户。自始至终,我们都在考虑如何以及在哪里使用刺激来操作页面上帖子的外观,因为我们的目标是用JavaScript控制帖子的呈现。

首先,在Shark事实下方,为POST添加一个<h2>头部,并添加一行以呈现名为Sharks/posts的部分内容:

 1[label ~/sharkapp/app/views/sharks/show.html.erb]
 2. . . 
 3<p>
 4  <strong>Facts:</strong>
 5  <%= @shark.facts %>
 6</p>
 7
 8<h2>Posts</h2>
 9<%= render 'sharks/posts' %>
10. . .

这将使用表单构建器为新的POST对象呈现部分内容。

接下来,在EditBack链接下,我们将添加一个部分来控制旧帖子在页面上的呈现。将以下几行添加到文件中,以渲染名为Sharks/all的部分内容:

1[label ~/sharkapp/app/views/sharks/show.html.erb]
2<%= link_to 'Edit', edit_shark_path(@shark) %> |
3<%= link_to 'Back', sharks_path %>
4
5<div>
6  <%= render 'sharks/all' %>
7</div>

当我们开始将刺激集成到该文件中时,<div>元素将非常有用。

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

第四步-安装刺激

使用刺激的第一步是安装和配置我们的应用程序以使用它。这将包括确保我们具有正确的依赖项,包括Yarn包管理器和允许我们使用Webpacker,预处理器和捆绑器的gemwebpack.有了这些依赖项后,我们将能够安装SIMULATION并使用JavaScript来操作DOM中的事件和元素。

让我们从安装Yarn开始。首先,更新您的套餐列表:

1sudo apt update

接下来,添加Debian Yarn存储库的GPG密钥:

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

将存储库添加到您的APT源代码中:

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

使用新添加的Yarn包更新包数据库:

1sudo apt update

最后,安装Yarn:

1sudo apt install yarn

安装了ya后,您可以继续将webpackgem添加到您的项目中。

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

1nano Gemfile

在该文件中,您将看到默认情况下启用Turbolinks:

1[label ~/sharkapp/Gemfile]
2. . . 
3# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
4gem 'turbolinks', '~> 5'
5. . .

Turbolinks旨在通过优化页面加载来提高性能:Turbolinks不是让链接点击导航到新页面,而是截获这些点击事件,并使用异步JAVASCRIPT和HTMLHtml(AJAX).]发出页面请求然后,它替换当前页面的正文并合并<head>部分的内容,而在两次呈现之间,JavaScriptwindowDocent对象以及<html>元素保持不变。这解决了页面加载速度慢的主要原因之一:重新加载CSS和JavaScript资源。

默认情况下,我们会在Gemfile中获得Turbolinks,但我们需要添加webpack‘gem,这样我们才能安装和使用刺激性文件。在turbolinksgem下面,添加webPacker`:

1[label ~/sharkapp/Gemfile]
2. . . 
3# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
4gem 'turbolinks', '~> 5'
5gem 'webpacker', '~> 4.x'
6. . .

完成后保存并关闭该文件。

接下来,使用bundle命令将gem添加到您的工程捆绑包中:

1bundle

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

接下来,使用以下Bundle exec命令在您的捆绑包的上下文中安装gem:

1bundle exec rails webpacker:install

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

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

1nano config/initializers/content_security_policy.rb

在文件底部添加以下几行,以允许webpack-dev-server-为我们的应用程序的webpack捆绑包提供服务的服务器-作为允许的来源:

1[label ~/sharkapp/config/initializers/content_security_policy.rb]
2. . . 
3Rails.application.config.content_security_policy do |policy|
4  policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035' if Rails.env.development?
5end

这将确保webpack-dev-server被识别为可信的资产来源。

完成此更改后,保存并关闭该文件。

通过安装webPacker,您在项目的app目录下创建了两个新目录,也就是您的主应用程序代码所在的目录。新的父目录app/javascript将是您的项目的JavaScript代码所在的位置,它将具有以下结构:

1[secondary_label Output]
2├── javascript
3│   ├── controllers
4│   │   ├── hello_controller.js
5│   │   └── index.js
6│   └── packs
7│       └── application.js

app/javascript目录将包含两个子目录:app/javascrip/Packs,其中包含您的webpack入口点;以及app/javascrip/[controllers](https://stimulusjs.org/reference/controllers).,您将在其中定义您的刺激包我们刚才使用的Bundle exec命令将创建app/javascript/Packs目录,但我们需要安装SIMULATION才能自动生成app/javascrip/Controlers`目录。

安装了webPacker后,我们现在可以使用以下命令安装刺激:

1bundle exec rails webpacker:install:stimulus

您将看到如下所示的输出,表明安装成功:

 1[secondary_label Output]
 2. . . 
 3success Saved lockfile.
 4success Saved 5 new dependencies.
 5info Direct dependencies
 6└─ [email protected]
 7info All dependencies
 8├─ @stimulus/[email protected]
 9├─ @stimulus/[email protected]
10├─ @stimulus/[email protected]
11├─ @stimulus/[email protected]
12└─ [email protected]
13Done in 8.30s.
14Webpacker now supports Stimulus.js 🎉

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

首先,我们需要调整app/view/layout/Applation.html.erb,以确保我们的JavaScript代码可用,并且在我们的主要webPacker入口点app/javascript/pack/appation.js中定义的代码在每次加载页面时都会运行。

打开该文件:

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

将下面的javascript_Include_tag标签修改为javascript_pack_tag,加载app/javascript/pack/applation.js

1[label ~/sharkapp/app/views/layouts/application.html.erb]
2. . .
3    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
4    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
5. . .

完成更改后,保存并关闭文件。

接下来,打开 app/javascript/packs/application.js

1nano app/javascript/packs/application.js

文件最初的样子是这样的

1[label ~/sharkapp/app/javascript/packs/application.js]
2. . . 
3console.log('Hello World from Webpacker')
4
5import "controllers"

删除其中的模板代码,然后添加以下代码,以加载 Stimulus 控制器文件并启动应用程序实例:

1[label ~/sharkapp/app/javascript/packs/application.js]
2. . . 
3import { Application } from "stimulus"
4import { definitionsFromContext } from "stimulus/webpack-helpers"
5
6const application = Application.start()
7const context = require.context("../controllers", true, /\.js$/)
8application.load(definitionsFromContext(context))

这段代码使用 webpack 辅助方法来要求使用 app/javascript/controllers 目录中的控制器,并加载此上下文供应用程序使用。

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

现在,您已经安装了 Stimulus,并已准备好在应用程序中使用。接下来,我们将使用 Stimulus 的 controllerstargetsactions 来构建我们在 sharks show 视图中引用的部分 - sharks/postssharks/all

第 5 步 - 在 Rails 部件中使用刺激源

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

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

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

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

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

在文件中添加以下代码,使用 form_with 助手创建新的帖子对象:

1[label ~/sharkapp/app/views/sharks/_posts.html.erb]
2        <%= form_with model: [@shark, @shark.posts.build] do |form| %>
3                <%= form.text_area :body, placeholder: "Your post here" %>
4                <br>
5                <%= form.submit %>
6        <% end %>

到目前为止,这个表单的行为就像一个典型的 Rails 表单,它使用 form_with 助手来创建一个带有为 Post 模型定义的字段的 post 对象。因此,表单中有一个帖子字段 :body,我们在其中添加了一个提示填写帖子的 placeholder 字段。

此外,该表单的作用域是利用 "鲨鱼 "和 "帖子 "模型之间关联的集合方法。在这种情况下,根据用户提交的数据创建的新帖子对象将属于与我们当前查看的鲨鱼相关联的帖子集合。

我们现在的目标是添加一些 "刺激 "控制器、事件和操作,以控制帖子数据如何显示在页面上。用户最终将提交帖子数据,并通过 Stimulus 操作将其发布到页面上。

首先,我们将在表单的 <div> 元素中添加一个名为 posts 的控制器:

1[label ~/sharkapp/app/views/sharks/_posts.html.erb]
2<div data-controller="posts">
3        <%= form_with model: [@shark, @shark.posts.build] do |form| %>
4                 <%= form.text_area :body, placeholder: "Your post here" %>
5                 <br>
6                 <%= form.submit %>
7        <% end %>
8</div>

确保添加了结尾的"

"标记 ,以正确作用域控制器。

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

1[label ~/sharkapp/app/views/sharks/_posts.html.erb]
2<div data-controller="posts">
3        <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody" } do |form| %>
4        . . . 
5                 <%= form.submit %>
6        <% end %>
7</div>

我们使用 :data选项和 form_with 来提交作为附加 HTML 数据属性的 Stimulus 操作。动作本身有一个称为 action descriptor 的值,由以下内容组成:

  • 要监听的 DOM 事件 。在这里,我们使用的是与表单元素相关的默认事件--提交,因此无需在描述符中指定事件本身。有关常见元素/事件对的更多信息,请参阅刺激文档
  • 控制器标识符 ,在我们的例子中为 "posts"。
  • 事件应调用的方法 。在我们的例子中,这是我们将在控制器中定义的 addBody 方法。

接下来,我们将为 :body``<textarea> 元素中定义的用户输入附加一个数据目标,因为我们将在 addBody 方法中使用该输入值。

:body```<textarea>元素中添加以下:data选项:

1[label ~/sharkapp/app/views/sharks/_posts.html.erb]
2<div data-controller="posts">
3        <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody" } do |form| %>
4                <%= form.text_area :body, placeholder: "Your post here", data: { target: "posts.body" } %>
5. . .

与动作描述符一样,刺激目标也有_目标描述符_,其中包括控制器标识符和目标名称。在本例中,"posts "是我们的控制器,而 "body "则是目标本身。

最后一步,我们将为输入的 "body "值添加一个数据目标,这样用户就能在提交帖子后立即看到帖子。

在表单下方和收尾的 <div> 上方添加以下 <ul> 目标元素 add

1[label ~/sharkapp/app/views/sharks/_posts.html.erb]
2. . .
3        <% end %>
4  <ul data-target="posts.add">
5  </ul>
6
7</div>

body 目标一样,我们的目标描述符包括控制器名称和目标(在本例中为 add)。

完成后的局部将是这样的

 1[label ~/sharkapp/app/views/sharks/_posts.html.erb]
 2<div data-controller="posts">
 3        <%= form_with model: [@shark, @shark.posts.build], data: { action: "posts#addBody"} do |form| %>
 4                <%= form.text_area :body, placeholder: "Your post here", data: { target: "posts.body" } %>
 5                <br>
 6                <%= form.submit %>
 7        <% end %>
 8  <ul data-target="posts.add">
 9  </ul>
10
11</div>

完成这些更改后,就可以保存并关闭文件了。

您现在已经创建了添加到 sharks/show 视图模板的两个部分中的一个。接下来,您将创建第二个部分,sharks/all,它将显示数据库中所有较旧的帖子。

app/views/sharks/ 目录中创建名为 _all.html.erb 的新文件:

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

在文件中添加以下代码,以遍历与所选鲨鱼相关的帖子集合:

 1[label ~/sharkapp/app/views/sharks/_all.html.erb]
 2<% for post in @shark.posts  %>
 3    <ul>
 4
 5        <li class="post">
 6            <%= post.body %>
 7        </li>
 8
 9    </ul>
10    <% end %>

这段代码使用 for 循环遍历与特定鲨鱼相关联的帖子对象集合中的每个帖子实例。

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

不过,在此之前,我们需要在项目中添加一个 gem,这样就可以使用 Font Awesome 图标,我们将用它来注册向上投票。打开第二个终端窗口,并导航到你的 sharkapp 项目目录。

打开你的 Gemfile:

1[environment second]
2nano Gemfile

在你的 "webpacker "gem下面,添加以下一行,以便在项目中包含font-awesome-railsgem

1[label ~/sharkapp/Gemfile]
2[environment second]
3. . . 
4gem 'webpacker', '~> 4.x'
5gem 'font-awesome-rails', '~>4.x'
6. . .

保存并关闭文件。

接下来,安装 gem:

1[environment second]
2bundle install

最后,打开应用程序的主样式表 app/assets/stylesheets/application.css

1[environment second]
2nano app/assets/stylesheets/application.css

添加以下一行,以便在项目中包含 Font Awesome 的样式:

1[label ~/sharkapp/app/assets/stylesheets/application.css]
2[environment second]
3. . . 
4*
5 *= require_tree .
6 *= require_self
7 *= require font-awesome
8 */

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

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

app/views/sharks/_all.html.erb 中添加以下代码:

 1[label ~/sharkapp/app/views/sharks/_all.html.erb]
 2<% for post in @shark.posts  %>
 3    <ul>
 4
 5        <li class="post">
 6            <%= post.body %>
 7            <%= button_tag "Remove Post", data: { controller: "posts", action: "posts#remove" } %>
 8            <%= button_tag "Upvote Post", data: { controller: "posts", action: "posts#upvote" } %>
 9        </li>
10
11    </ul>
12    <% end %>

按钮标记还包含:data选项,因此我们添加了帖子刺激控制器和两个操作:删除 "和 "向上投票"。同样,在动作描述符中,我们只需定义控制器和方法,因为与按钮元素相关的默认事件是点击。点击每个按钮都将触发控制器中定义的相应的 removeupvote 方法。

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

在继续定义控制器之前,我们要做的最后一项更改是设置数据目标和操作,以控制显示 sharks/all 部分的方式和时间。

再次打开 "show "模板,其中定义了渲染 "sharks/all "的初始调用:

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

在文件底部,我们有一个 <div> 元素,目前看起来是这样的:

1[label ~/sharkapp/app/views/sharks/show.html.erb]
2. . . 
3<div>
4  <%= render 'sharks/all' %>
5</div>

首先,为该 <div> 元素添加一个控制器,以对动作和目标进行范围划分:

1[label ~/sharkapp/app/views/sharks/show.html.erb]
2. . . 
3<div data-controller="posts">
4  <%= render 'sharks/all' %>
5</div>

接下来,添加一个按钮来控制部分内容在页面上的显示。该按钮将触发帖子控制器中的 showAll 方法。

<div> 元素下方和 render 语句上方添加按钮:

1[label ~/sharkapp/app/views/sharks/show.html.erb]
2. . . 
3<div data-controller="posts">
4
5<button data-action="posts#showAll">Show Older Posts</button>
6
7  <%= render 'sharks/all' %>

同样,我们只需在此处标识我们的 posts 控制器和 showAll 方法,点击事件将触发该操作。

接下来,我们将添加一个数据目标。设置此目标的目的是控制页面上部分内容的外观。最终,我们希望用户只有通过点击 "显示较旧文章 "按钮选择查看较旧文章时,才能看到较旧文章。

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

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

1[label ~/sharkapp/app/views/sharks/show.html.erb]
2. . . 
3<div data-controller="posts">
4
5<button data-action="posts#showAll">Show Older Posts</button>
6
7<div data-target="posts.show" style="visibility:hidden">
8  <%= render 'sharks/all' %>
9</div>

**请务必添加结尾的 </div> 标记。

完成后的 show 模板将如下所示:

 1[label ~/sharkapp/app/views/sharks/show.html.erb]
 2<p id="notice"><%= notice %></p>
 3
 4<p>
 5  <strong>Name:</strong>
 6  <%= @shark.name %>
 7</p>
 8
 9<p>
10  <strong>Facts:</strong>
11  <%= @shark.facts %>
12</p>
13
14<h2>Posts</h2>
15
16<%= render 'sharks/posts' %>
17
18<%= link_to 'Edit', edit_shark_path(@shark) %> |
19<%= link_to 'Back', sharks_path %>
20
21<div data-controller="posts">
22
23<button data-action="posts#showAll">Show Older Posts</button>
24
25<div data-target="posts.show" style="visibility:hidden">
26  <%= render 'sharks/all' %>
27</div>
28</div>

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

完成该模板及其相关部分后,就可以使用这些文件中引用的方法创建控制器了。

第 6 步 - 创建刺激控制器

安装 Stimulus 时创建了 app/javascript/controllers 目录,这是 webpack 加载应用程序上下文的位置,因此我们将在此目录下创建 posts 控制器。该控制器将包含我们在上一步中引用的每个方法:

  • addBody() 添加新帖子。
  • showAll() 显示旧文章。
  • remove() 从当前视图中删除帖子。
  • 向上投票()",为帖子附加向上投票图标。

app/javascript/controllers 目录中创建名为 posts_controller.js 的文件:

1nano app/javascript/controllers/posts_controller.js

首先,在文件顶部,扩展 Stimulus 内置的 Controller 类:

1[label ~/sharkapp/app/javascript/controllers/posts_controller.js]
2import { Controller } from "stimulus"
3
4export default class extends Controller {
5}

接下来,在文件中添加以下目标定义:

1[label ~/sharkapp/app/javascript/controllers/posts_controller.js]
2. . .
3export default class extends Controller {
4    static targets = ["body", "add", "show"]
5}

通过这种方式定义目标,我们就可以在我们的方法中使用 this.target-nameTarget 属性访问它们,该属性会给出第一个匹配的目标元素。例如,要匹配目标数组中定义的 body 数据目标,我们可以使用 this.bodyTarget。该属性允许我们对输入值或 css 样式等进行操作。

接下来,我们可以定义 addBody 方法,该方法将控制新帖子在页面上的显示。在目标定义下面添加以下代码来定义该方法:

 1[label ~/sharkapp/app/javascript/controllers/posts_controller.js]
 2. . .
 3export default class extends Controller {
 4    static targets = [ "body", "add", "show"]
 5
 6    addBody() {
 7        let content = this.bodyTarget.value;
 8        this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
 9    }
10}

该方法使用 let 关键字 定义了一个 content 变量,并将其设置为等于用户在帖子表单中输入的帖子输入字符串。它是通过将 body 数据目标附加到表单中的 <textarea> 元素来实现这一功能的。使用 this.bodyTarget 与该元素匹配后,我们就可以使用与该元素关联的 value 属性,将 content 的值设置为用户输入的帖子输入内容。

接下来,该方法会将帖子输入添加到我们添加到 sharks/posts 部分中表单生成器下方的 <ul> 元素的 add 目标中。该过程使用 Element.insertAdjacentHTML() 方法,它将在 add 目标元素之前插入在 content 变量中设置的新帖内容。我们还将新文章括在一个 <li> 元素中,这样新文章就会以列表形式出现。

接下来,在 addBody 方法下面,我们可以添加 showAll 方法,该方法将控制页面上旧文章的外观:

 1[label ~/sharkapp/app/javascript/controllers/posts_controller.js]
 2. . . 
 3export default class extends Controller {
 4. . .
 5    addBody() {
 6        let content = this.bodyTarget.value;
 7        this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
 8    }
 9
10    showAll() {
11        this.showTarget.style.visibility = "visible";
12    }
13
14}

在这里,我们再次使用 this.target-nameTarget 属性来匹配我们的 show 目标,它与 sharks/all 部分连接到 <div> 元素。我们给了它一个默认样式 "visibility:hidden",因此在此方法中,我们只需将样式更改为 "visible"。这将向选择查看旧文章的用户显示该部分。

在 "showAll "下面,我们将添加一个 "upvote "方法,允许用户通过将free Font Awesome 的 "check-circle "图标附加到特定帖子,对页面上的帖子进行 "upvote"。

添加以下代码来定义此方法:

 1[label ~/sharkapp/app/javascript/controllers/posts_controller.js]
 2. . . 
 3export default class extends Controller {
 4. . . 
 5
 6    showAll() {
 7        this.showTarget.style.visibility = "visible";
 8    }
 9
10    upvote() {
11        let post = event.target.closest(".post");
12        post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
13    }
14
15}

在这里,我们将创建一个 post 变量,该变量的目标是最靠近的带有 post 类的 <li> 元素,也就是我们在 sharks/all 中循环迭代时附加给每个 <li> 元素的类。这将针对最近的帖子,并在 <li> 元素内的最后一个子元素后添加 check-circle 图标。

接下来,我们将使用类似的方法来隐藏页面上的帖子。在 upvote 方法下面添加以下代码,定义一个 remove 方法:

 1[label ~/sharkapp/app/javascript/controllers/posts_controller.js]
 2. . . 
 3export default class extends Controller {
 4. . . 
 5
 6    upvote() {
 7        let post = event.target.closest(".post");
 8        post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
 9    }
10
11    remove() {
12        let post = event.target.closest(".post");
13        post.style.visibility = "hidden";
14    }
15}

同样,我们的 post 变量将以最接近的类为 post<li> 元素为目标。然后,它会将 visibility 属性设置为 `"hidden",以隐藏页面上的帖子。

完成后的控制器文件将如下所示:

 1[label ~/sharkapp/app/javascript/controllers/posts_controller.js]
 2import { Controller } from "stimulus"
 3
 4export default class extends Controller {
 5
 6    static targets = ["body", "add", "show"]
 7
 8    addBody() {
 9        let content = this.bodyTarget.value;
10        this.addTarget.insertAdjacentHTML('beforebegin', "<li>" + content + "</li>");
11    }
12
13    showAll() {
14        this.showTarget.style.visibility = "visible";
15    }
16
17    upvote() {
18        let post = event.target.closest(".post");
19        post.insertAdjacentHTML('beforeend', '<i class="fa fa-check-circle"></i>');
20    }
21
22    remove() {
23        let post = event.target.closest(".post");
24        post.style.visibility = "hidden";
25    }
26}

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

有了 Stimulus 控制器,您就可以对 index 视图进行最后的修改并测试应用程序了。

第 7 步 - 修改索引视图并测试应用程序

最后修改一下鲨鱼的 index 视图,就可以测试应用程序了。index "视图是应用程序的根目录,在如何在 Rails 上创建 Ruby 应用程序步骤 4中进行了设置。

打开文件:

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

我们将使用 button_to 助手来代替为显示和销毁鲨鱼而自动生成的 link_to 助手。这将帮助我们使用生成的 HTML 代码,而不是默认的 Rails JavaScript 资产。在步骤 1 中,我们将 app/views/layouts/application.html.erb 中的 javascript_include_tag 更改为 javascript_pack_tag,并指定不再使用默认的 Rails JavaScript 资产。

用下面的 button_to 助手替换文件中现有的 link_to 助手:

 1[label ~/sharkapp/app/views/sharks/index.html.erb]
 2. . . 
 3  <tbody>
 4    <% @sharks.each do |shark| %>
 5      <tr>
 6        <td><%= shark.name %></td>
 7        <td><%= shark.facts %></td>
 8        <td><%= button_to 'Show', shark_path(:id => shark.id), :method => :get %></td>
 9        <td><%= button_to 'Edit', edit_shark_path(:id => shark.id), :method => :get %></td>
10        <td><%= button_to 'Destroy', shark_path(:id => shark.id), :method => :delete %></td>
11      </tr>
12    <% end %>
13  </tbody>
14. . .

这些辅助工具的功能与 link_to 类似,但 Destroy 辅助工具现在依赖于生成的 HTML,而不是 Rails 的默认 JavaScript。

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

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

首先,运行数据库迁移:

1rails db:migrate

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

1rails s

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

1rails s --binding=your_server_ip

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

您将看到以下登陆页面:

申请登陆页面](https://assets.digitalocean.com/articles/stimulus/stimulus_landing.png)

单击显示 将进入此鲨鱼的 "显示 "视图。在这里你会看到一个填写帖子的表格:

Shark Show Page

在帖子表格中输入 "这些鲨鱼好可怕!":

填写职位

点击创建帖子 。现在您将在页面上看到新帖子:

![新帖已添加到页面](https://cdn.jsdelivr.net/gh/andsky/tutorials-images/assets/stimulus/stimulus_show_post.png .png)

如果您愿意,可以再添加一个新帖。这次,请输入 "这些鲨鱼经常在电影中被歪曲",然后点击创建帖子

Second Post Added to Page

为了测试 "显示较旧文章 "**功能,我们需要离开本页面,因为我们的大白菜论坛免费彩金目前没有比刚刚添加的文章更旧的文章。

点击 Back 返回主页,然后点击** Show** 返回大白登陆页面:

Shark Show Page

点击 "显示以前的帖子 ",就会显示您创建的帖子:

Show Older Posts

现在,您可以点击为帖子 投票按钮,为帖子进行向上投票:

Upvote a Post

同样,点击删除帖子 将隐藏帖子:

删除帖子

现在,您已经确认自己拥有了一个可以正常运行的 Rails 应用程序,它可以使用 Stimulus 来控制嵌套的帖子资源在单个鲨鱼页面上的显示方式。您可以以此为起点,在未来使用 Stimulus 进行开发和实验。

结论

除了使用 rails-ujsJQuery 以及 React 和 Vue 等框架之外,Stimulus 还是一种可行的替代方案。

正如前言所述,当您需要直接处理服务器生成的 HTML 时,Stimulus 是最合适的选择。它是轻量级的,旨在使代码(尤其是 HTML)尽可能具有自解释性。如果您不需要在客户端管理状态,那么 Stimulus 可能是一个不错的选择。

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

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

Published At
Categories with 技术
comments powered by Disqus