React.js
, Vue.js
, Angular.js
為近期很流行的框架,改變了前端生活圈的生態。不過除了這些主流框架以外,Basecamp出了一款基於Rails
的微型框架Stimulus
Stimulus為Basecamp出品的基於SSR框架,宗旨為Rails的後端工程師不用瞭解更多的Javascript概念而設計出來的框架。在 Rails 圈已經紅了一段時間,而我自己本身,是聽 Drifting Ruby 的課程才了解 Stimulus
目前Stimulus已經發展到 v2.0!它已經問世了一段時間,並且已經進行了1個大版本的更動。
首先我們使用以下指令安裝框架
yarn add stimulus
Rails 進入點的撰寫位置會放在 config/webpacker.yml
,
在 app/javascript/packs/application.js
中,我們可以看到許多被 require
進來的套件或資料夾,這些都是使用者請 webpack 幫忙編譯與打包的東西。
我們在app/javascript/packs/application.js
寫下import 'controllers'
,就可以讀取controllers
資料夾裡面的檔案。
import "controllers"
import Rails from "@rails/ujs"
import * as ActiveStorage from "@rails/activestorage"
import "channels"
Rails.start()
ActiveStorage.start()
在使用Stimulus
前,我們必須要了解Webpack
的使用。
Webpack 預設會先讀取 index.js
的檔案,所以我們會先在app/javascript
底下創建一個資料夾名controllers
,並且在app/javascript/controllers
創一個檔案index.js
。假設我們要引入一個han_theme.scss
,我們可以在index.js
寫下
import 'han_theme.scss';
Webpack 一層一層讀取,首先由Layout寫下javascript_include_tag
➡️ 讀取app/javascript/packs/application.js
➡️ 讀取 require("styles")
➡️ 讀取 ./styles/index.js
➡️ 讀取import '
han_theme.scss';
✅ Webpack 就會知道讀取han_theme.scss
。
若我們使用 Stimulus,要在index.js
寫下
// Load all the controllers within this directory and all subdirectories.
// Controller files must be named *_controller.js or *_controller.ts.
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"
const application = Application.start()
// 動態讀取結尾為 _controller 的檔案
const context = require.context("controllers", true, /_controller\.(js|ts)$/)
application.load(definitionsFromContext(context))
接著開始要介紹 Stimulus
的基本用法。
Stimulus
一共有三個主體(引言出自Hotwire.dev)
字串
, 數字
, 陣列
...event.target
為同一角色)actions, which connect controller methods to DOM events using
data-action
attributestargets, which locate elements of significance within a controller
values, which read, write, and observe data attributes on the controller’s element
為了部落格列表使用Stimulus,我們先創建1檔案:app/javascript/controllers/admin/blogs_controller.js
,並且在檔案裡頭寫下一段基本的用法
import { Controller } from 'stimulus';
export default class extends Controller {
connect() {
console.log('部落格列表')
}
}
Stimulus 是用Javascript
假物件導向的語法糖的語法寫,但雖然如此,Rails
的精神就是慣例優於設定,除非真的很好奇,不然我們不需要懂其中的內涵。
import { Controller } from 'stimulus';
export default class extends Controller {
constructor() {
super();
// 自定義邏輯
}
connect() {
console.log('部落格列表')
}
}
之前做了一個破壞慣例的動作,在內部class
加入建構子Constructor
,之後便出現了問題。
connect()
為位於生命週期中的畫面載入階段。若重整畫面後,就會馬上執行connect()
方法。個人習慣在connect()
寫下一段簡單的console.log
,檢查該頁面是否執行Stimulus Code。
接著我們對畫面進行改寫
= title '部落格列表'
= tag.div data: { controller: 'admin--blogs' }
/ 卡片內容
= card do
/ ......
/ 彈跳視窗
= modal(id: 'new-blog-modal', confirm_wording: '送出文章',
confirm_form: 'new_modal', title: '新增文章') do
/ ......
最後呈現的結果如下 ⬇️
我們來實作官方首頁的程式碼吧!
= tag.div data: { controller: 'admin--blogs' }
/ Stimulus 卡片內容
= card do
= card_header(title: 'Stimulus')
= card_body do
= tag.input type: 'text', data: { 'admin--blogs-target': 'name' }
= button_tag '輸出', type: 'button', data: { action: 'click->admin--blogs#greet' },
class: 'btn btn-primary mx-2'
= tag.span data: { 'admin--blogs-target': 'output' }
import { Controller } from 'stimulus';
export default class extends Controller {
static targets = [ "name", "output" ]
connect() {
console.log('部落格列表')
}
greet() {
this.outputTarget.textContent =
this.nameTarget.value === '' ? 'Hello, 輸入框沒有填入值' : `Hello, ${this.nameTarget.value}!`
}
}
我們將官方的範例稍微做改寫,若沒有值,則會回傳輸入框沒有填入值
。操作完官網上的範例後,接下來我們講講admin--blogs
的命名規則怎麼來。
下列為官網提到的 controller 的命名規則,因此我們可以推敲,當我們把檔案在javascript/controllers/admin/blogs_controller
,controller
的名稱為admin--blogs
If your controller file is named… | its identifier will be… |
---|---|
clipboard_controller.js | clipboard |
date_picker_controller.js | date-picker |
users/list_item_controller.js | users--list-item |
local-time-controller.js | local-time |
接著我們再講click->admin--blogs#greet
是什麼?
click->admin--blogs#greet
為點擊動作時,觸發admin--blogs
的 greet
動作,而這邊可以省略成admin--blogs#greet
。
以下為官網提到的預設事件
Element | Default Event |
---|---|
a | click |
button | click |
form | submit |
input | input |
input type=submit | click |
select | change |
textarea | input |
若要實現getter
, setter
,我們可以使用value
作為兩個值之間的媒介。
import { Controller } from 'stimulus';
export default class extends Controller {
static targets = [ "name", "output", "searchedContent"]
static values = { testId: Number }
connect() {
console.log('部落格列表')
this.testIdValue = 1
}
greet() {
this.testIdValue += 10
this.outputTarget.textContent =
this.nameTarget.value === '' ?
`Hello, 輸入框沒有填入值 ${this.testIdValue}` :
`Hello, ${this.nameTarget.value}! ${this.testIdValue}`
}
}
上述的例子為,將this.testIdValue
設定初始值=1
,並且每按一次,this.testIdValue
就會 +10
。
除了當作getter
, setter
外,還可以在畫面上綁定Value。我們將指定 controller 的位置指定value
,並且在connect()
內部將值印出來
= tag.div data: { controller: 'admin--blogs', 'admin--blogs-view-id-value': 40 }
/! 內容
import { Controller } from 'stimulus';
export default class extends Controller {
static values = { viewId: Number }
connect() {
console.log('部落格列表')
console.log('this.viewIdValue', this.viewIdValue)
}
}
我們也可以帶入Rails
物件進去,如params
等。
= tag.div data: { controller: 'admin--blogs',
'admin--blogs-view-id-value': 40,
'admin--blogs-reload_at-value': Time.current.strftime('%F %T') }
import { Controller } from 'stimulus';
export default class extends Controller {
static values = { viewId: Number, reloadAt: String }
connect() {
console.log('部落格列表')
console.log('this.viewIdValue', this.viewIdValue)
console.log('this.reloadAtValue', this.reloadAtValue)
}
}
Day26-28
為Stimulus
的系列文
Stimulus
的設定、三大元素target
, value
, action
。Stimulus
如何搭配Ajax
使用Datatable
這系列的文章,會需要比較熟悉Javascript
的基本用法,如果有什麼想詢問的話,歡迎在下方的留言區聯絡。