PJCHENder 未整理筆記

[Note] Stimulus

2018-04-30

[Note] Stimulus 筆記

keywords: javascript, rails

@(其他-程式力)

本篇內容擷取自:Stimulus 手冊 - 繁體中文 @ Andyyou

目錄

[TOC]

基本使用

1
2
3
4
5
<!-- snippets -->
<div data-controller="hello">
<input data-target="hello.name" type="text">
<button data-action="click->hello#greet">Greet</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// snippets
// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
static targets = [ "name" ] // 將 data-target 的名稱加到定義列表中

// 當 Controller 和 DOM 綁定時會觸發 connect
connect () {
this.element // 取得被綁定的 DOM 元素
}

// 透過 DOM 上的 data-action="click->hello#greet" 觸發
greet() {
this.nameTarget // Object, 取得第一個符合的 target
this.nameTargets // Array, 取得所有符合的 targets
this.hasNameTarget // Boolean, 檢查是否有符合的 target
}
}
  • data-controller :主要用來設定繫結或停止繫結 Stimulus 的 controllers。
  • data-action :用來設定事件(events),即元素該觸發 controller 裡的哪個方法(action)。
  • data-target :則是協助我們在 controller 影響的範圍內方便的操作特定元素。

定義 controller 並檢查是否成功綁定到 HTML 元素

keywords: data-controller
1
2
3
4
5
6
<!-- public/index.html -->
<!-- data-controller -->
<div data-controller="hello">
<input type="text">
<button>Greet</button>
</div>

connect() 這個方法會在 Stimulus 完成 controller 和 DOM 綁定的時候被呼叫

1
2
3
4
5
6
7
8
// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
connect() {
console.log("Hello, Stimulus!", this.element)
}
}

action 回應 DOM 事件

keywords: data-action

在 Stimulus 裡,controller 的方法我們叫做 action

1
2
3
4
5
6
7
8
9
<!-- public/index.html -->
<!-- data-action -->
<div data-controller="hello">
<input type="text">
<button data-action="click->hello#greet">Greet</button>

<!-- multiple data-action if needed -->
<div data-action="mouseover->monkey#mouseOver mouseout->monkey#mouseOut">
</div>

其中 data-action 的值 click->hello#greet 稱為動作描述子(action descriptor)。其實就是一個設定格式。下面是格式的說明:

  • click 為綁定的事件
  • hello 為 controller 識別名稱
  • greet 為調用的方法(action)

當我們 click 時,即會出發 hello controller 裡的 greet action。

1
2
3
4
5
6
7
8
// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
greet() {
console.log("Hello, Stimulus!", this.element)
}
}

Action 預設事件可省略

上述 button 的 action 也可以省略寫成 <button data-action="hello#greet">Greet</button>,這是因為 Stimulus 定義了 click<button> 的預設事件。

其他特定的元素也有預設事件,下列是完整的列表:

元素 預設事件
a click
button click
form submit
input change
input type=submit click
select change
textarea change

target 為重要元素建立 controller 的參考屬性

keywords: data-target, this.nameTarget, this.nameTargets, this.hasNameTarget
1
2
3
4
5
6
<!-- public/index.html -->
<!-- data-target -->
<div data-controller="hello">
<input data-target="hello.name" type="text">
<button data-action="click->hello#greet">Greet</button>
</div>

觀察到 data-target 的值為 hello.name 又稱為目標元素描述子(target descriptor)。其實就是一個設定格式。下面是格式的說明:

  • hello 為 controller 是識別名稱
  • name 為 target 名稱

接著透過 static targets = ['name'] ,把 name 加入到 controller 目標元素的定義列表中,Stimulus 會自動建立一個 this.nameTarget 屬性,這個屬性會參考到符合條件的第一個目標元素。我們就可以直接使用這個屬性來讀取元素的值,用它來產生我們的問候句。

1
2
3
4
5
6
7
8
9
10
11
12
// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
static targets = [ "name" ] // 將 data-target 的名稱加到定義列表中

greet() {
const element = this.nameTarget
const name = element.value
console.log(`Hello, ${name}!`)
}
}

如上面的範例我們的 "name" target 名稱會產生下面這些屬性:

  • this.nameTarget 等於在 controller 影響的範圍內找到符合 source 的第一個元素,如果沒有任何元素符合,當我們存取屬性會產生錯誤。
  • this.nameTargets 所有在 controller 範圍內符合 source 的元素
  • this.hasNameTarget 判斷是否有符合 name 元素,如果有就是 true ,沒有則是 false

狀態管理與 Data API

keywords: data-<controller-name>-<attribute-name>, this.data.get(), this.data.has(), this.data.set()

一個 Stimulus 應用程式的狀態基本上會存在 DOM 的屬性上;controller 基本上不管理狀態。命名的方式是使用 data-<controller-name>-<attribute-name>

1
2
3
<!-- 直接在 DOM 上面透過 data 屬性帶入狀態 -->
<!-- data-<controller-name>-<attribute-name> -->
<div data-controller="slideshow" data-slideshow-index="1">
注意
  • data-slideshow-index 必須要放在 controller 的根元素上。
  • 因為 data-slide-show-index 是 HTML 的 attribute 所以只能是字串,若要存成陣列,則需透過 split()join() 處理;若是物件則需要先透過 JSON.stringify() 等方法。

由於 data 屬性太常被使用,因此若按上面慣例命名的話,除了過去使用 el.datasetel.getAttribute 的做法外,在 Stimulus 中可以直接使用 this.data.get('<attribute-name>')

1
2
3
4
5
6
7
// ./slideshow_controller
initialize() {
// const index = parseInt(this.element.dataset.slideshowIndex)
// const index = parseInt(this.element.getAttribute('data-slideshow-index'))
const index = parseInt(this.data.get("index"))
this.showSlide(index)
}

使 controller 的狀態與 DOM 上的狀態保持一致

  • this.data.has('<attribute-name>') 假如 controller 根元素有 data-slideshow-index 屬性的話,會回傳 true
  • this.data.get('<attribute-name>') 取得根元素上 data-slideshow-index 屬性值。
  • this.data.set('<attribute-name>', <value>)index 的值設定到根元素的 data-slideshow-index 屬性上。
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
30
31
32
33
// ./slideshow_controller.js
import { Controller } from "stimulus"

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

initialize () {
this.showCurrentSlide()
}

previous () {
this.index--
}

next () {
this.index++
}

showCurrentSlide() {
this.slideTargets.forEach((el, i) => {
el.classList.toggle("slide--current", this.index == i)
})
}

get index () {
return parseInt(this.data.get("index"))
}

set index (value) {
this.data.set("index", value)
this.showCurrentSlide()
}
}

生命週期

keywords: initialize, connect, disconnect

在 Stimulus 中 controller 從初始化到解構有其生命週期,每個階段有對應的生命週期回調,這些方法通常可以協助我們設定狀態或解除繫結。

回調 由 Stimulus 自動調用
initialize 僅執行一次,當 controller 建立物件實例的時候
connect 當 controller 和 DOM 完成繫結的時候
disconnect 當 controller 和 DOM 解除繫結的時候

處理外部資源

keywords: content-loader, AJAX

有時候我們的 controller 需要追蹤來自外部的資料,這裡我們說的**「外部」指的是任何只要不是存在 DOM 或者 Stimulus 中的資料**。

1.建立 content-loader controller 和 data-content-loader-url

1
2
3
<!-- public/index.html -->
<div data-controller="content-loader"
data-content-loader-url="/messages.html"></div>

2.新增要外部載入的檔案

1
2
3
4
5
<!-- public/messages.html -->
<ol>
<li>New Message: Stimulus Launch Party</li>
<li>Overdue: Finish Stimulus 1.0</li>
</ol>

3.透過 controller 載入外部資源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/controllers/content_loader_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
connect() {
this.load()
}

load() {
fetch(this.data.get("url"))
.then(response => response.text())
.then(html => {
this.element.innerHTML = html
})
}
}

Working with external resources @ Andyyou’s Gitbook

命名慣例

檔名 識別名稱
clipboard_controller.js clipboard
date_picker_controller.js date-picker
users/list_item_controller.js users–list-item
local-time-controller.js local-time

controller 檔案名稱與對應的識別名稱(identifier) @ Andyyou’s Gitbook

資料來源

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