PJCHENder 未整理筆記

[Rails] Rails with Stimulus 起手式

2018-04-30

[Rails] Rails with Stimulus 起手式

@(Ruby on Rails)[gem, rvm, 指令, cli, stimulus, bootstrap, jquery]

確認 Ruby 環境與 Rails 版本

1
2
3
4
5
6
7
8
9
# 建立並確定 Ruby 版本
$ rvm list known # 找出最新版本的 Ruby
$ rvm install 2.5 # 安裝特定版本的 Ruby
$ rvm --default use 2.5 # 使用特定版本的 Ruby 並設為預設值

# 確認 RubyGems 版本和套件版本
$ gem update --system # 若版本不夠新,則升級 RubyGems 的版本(不是 gems)
$ gem list | grep rails # 確認當前 Rails 版本
$ gem update rails # 若版本不夠新,則將 Rails 更新至最新版

建立 Rails + Stimulus 專案

建立專案

1
2
3
4
# 建立 Rails + Stimulus 專案
$ rails new <project_name> --webpack=stimulus --database=postgresql --skip-coffee --skip-test
$ cd <project_name>
$ rails db:migrate

測試使用 Stimulus

1
2
# 測試 Rails + Stimulus 能否使用
$ rails g controller pages index # http://localhost:3000/pages/index

javascript/packs/application.js 掛入 layout 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
...
<%= javascript_pack_tag 'application' %> <!-- 將 webpack entry 掛入 -->
...
</head>

<body>
<%= yield %>
</body>
</html>

把內建的範例複製到 app/views/pages/index

1
2
3
4
<!-- app/views/pages/index.html.erb -->
<div data-controller="hello">
<h1 data-target="hello.output"></h1>
</div>

若於頁面上看到 Hello, Stimulus!! 即表示已完成 Stimulus 安裝。

設定 CSS 檔

1
2
3
4
# Using webpacker for stylesheets
$ mkdir app/javascript/stylesheets
$ touch app/javascript/stylesheets/application.scss
$ touch app/javascript/stylesheets/_variables.scss

Import CSS file in app/javascript/pack/application.js

1
2
// layout file, like app/views/layouts/application.html.erb
import 'stylesheets/application'

Import CSS file by stylesheet_pack_tag in app/views/layout/application.html.erb :

1
2
3
4
5
6
7
8
9
10
<!-- app/views/layouts/application.html.erb -->
<!DOCTYPE html>
<html>
<head>
...
<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>
...
</head>
</html>

現在你可以在開始撰寫 CSS 了:

1
2
// app/javascript/stylesheets/application.scss
@import 'variables';
1
2
3
4
5
// app/javascript/stylesheets/_variables.scss
$colors: (
major: #00D252,
minor: #2F3B59
);

安裝 Bootstrap

1
2
# https://getbootstrap.com/docs/4.1/getting-started/webpack/
$ yarn add jquery popper.js bootstrap

app/javascript/stylesheets/application.scss

1
2
3
// app/javascript/stylesheets/application.scss
@import '~bootstrap/scss/bootstrap';
...

app/javascript/pack/application.js

1
2
3
// app/javascript/pack/application.js
import 'bootstrap'
...

Add configuration to config/webpack/environment.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// config/webpack/environment.js

const { environment } = require('@rails/webpacker')
const webpack = require('webpack')
/**
* Automatically load modules instead of having to import or require them everywhere.
* Support by webpack. To get more information:
*
* https://webpack.js.org/plugins/provide-plugin/
* http://j.mp/2JzG1Dm
*/
environment.plugins.prepend(
'Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
jquery: 'jquery',
'window.jQuery': 'jquery',
Popper: ['popper.js', 'default']
})
)
module.exports = environment

測試 Bootstrap 的使用

app/views/pages/index.html.erb 中使用 Bootstrap 模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- app/views/pages/index.html.erb -->
<h1>Pages#index</h1>
<p>Find me in app/views/pages/index.html.erb</p>

<div data-controller="hello">
<h1 data-target="hello.output"></h1>

<div class="alert alert-primary" role="alert">
This is a primary alert—check it out!
</div>

<div class="alert alert-secondary" role="alert">
This is a secondary alert—check it out!
</div>

<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" title="Tooltip on top">
Tooltip on top
</button>
</div>

此時因為 jQuery 並沒有載入到全域,因此要在 app/javascript/ 內才可以使用 jQuery:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/javascript/packs/application.js

import 'bootstrap'
import 'stylesheets/application'
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))

$(function () {
$('[data-toggle="tooltip"]').tooltip()
})

若有看到 Bootstrap alert 樣式,另外 tooltip 能夠使用的話,即表示正常運作。

其他

將 jQuery 載入到全域

在上面的範例中,並沒有將 jQuery 載入到全域,若有需要在全域的環境下使用 jQuery(例如在 template 中透過 <script> 使用),則需要:

1
2
# https://webpack.js.org/loaders/expose-loader/
$ yarn add expose-loader -D

Add configuration to config/webpack/environment.js

1
2
3
4
5
6
7
8
9
// config/webpack/environment.js

environment.loaders.append('expose', {
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: '$'
}]
})

如此將可以在全域(window)底下使用到 jQuery。

More example: clipboard_controller.js

1
$ touch app/javascript/controllers/clipboard_controller.js

Provide an example of clipboard controller in app/views/pages/index.html.erb

1
2
3
4
5
6
<!-- app/views/pages/index.html.erb -->
<div data-controller="clipboard">
PIN:
<input type="text" value="1234" data-target="clipboard.source" readonly>
<button data-action="click->clipboard#copy" class="clipboard-button">Copy to Clipboard</button>
</div>

Add app/javascript/controllers/clipboard_controller.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// app/javascript/controllers/clipboard_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
static targets = ['source']

connect () {
if (document.queryCommandSupported('copy')) {
console.log('clipboard-supported')
this.element.classList.add('clipboard--supported')
}
}

copy() {
this.sourceTarget.select()
document.execCommand("copy")
console.log(`text "${this.sourceTarget.value}" has been copied into your clipboard`)
}
}

讓透過 generator 產生的 Model 有預設內容

1
2
$ mkdir -p lib/templates/active_record/model
$ touch lib/templates/active_record/model/model.rb

lib/templates/active_record/model/model.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!-- lib/templates/active_record/model/model.rb -->
<% module_namespacing do -%>
class <%= class_name %> < <%= parent_class_name.classify %>
# scope macros

# Concerns macros

# Constants

# Attributes related macros
<% if attributes.any?(&:password_digest?) -%>
has_secure_password
<% end -%>

# association macros
<% attributes.select(&:reference?).each do |attribute| -%>
belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %>
<% end -%>

# validation macros

# callbacks

# other

private
# callback methods
end
<% end -%>

資料來源

掃描二維條碼,分享此文章