跳至主要内容

[note] stimulus

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

目錄

[TOC]

基本使用

<!-- snippets -->
<div data-controller="hello">
<input data-target="hello.name" type="text" />
<button data-action="click->hello#greet">Greet</button>
</div>
// 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
<!-- public/index.html -->
<!-- data-controller -->
<div data-controller="hello">
<input type="text" />
<button>Greet</button>
</div>

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

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

<!-- 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>
</div>

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

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

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

// 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> 的預設事件。

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

元素預設事件
aclick
buttonclick
formsubmit
inputchange
input type=submitclick
selectchange
textareachange

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

keywords: data-target, this.nameTarget, this.nameTargets, this.hasNameTarget
<!-- 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 屬性,這個屬性會參考到符合條件的第一個目標元素。我們就可以直接使用這個屬性來讀取元素的值,用它來產生我們的問候句。

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

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

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

// ./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 屬性上。
// ./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

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

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

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

3.透過 controller 載入外部資源

// 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.jsclipboard
date_picker_controller.jsdate-picker
users/list_item_controller.jsusers--list-item
local-time-controller.jslocal-time

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

資料來源