將使用者介面狀態暫存於響應式字典(Reactive Dictionary)
在這個章節,我們將在使用者介面加入篩選功能,讓使用者可以篩選出未完成的待辦事項。我們將學習如何使用響應式字典(ReactiveDict
)來暫存客戶端的響應式狀態。ReactiveDict
就像一般的JavaScript物件,有鍵(Key)與對應的值(Value),除此之外,還內建有響應式的特性。
首先,我們在HTML檔裡加入一個勾選框:
<header>
<h1>Todo List</h1>
<label class="hide-completed">
<input type="checkbox" />Hide Completed Tasks
</label>
<form class="new-task">
<input type="text" name="text" placeholder="Type to add new tasks" />
</form>
再來我們需要加入reactive-dict
套件:
meteor add reactive-dict
然後設置一個新的ReactiveDict
並且在body模板物件第一次被建立的時候附加上去,我們將會把勾選框的狀態存在這個ReactiveDict
中:
import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict';
import { Tasks } from '../api/tasks.js';
import './task.js';
import './body.html';
Template.body.onCreated(functionbodyOnCreated() {
this.state = new ReactiveDict();
});
Template.body.helpers({
tasks() {
// Show newest tasks at the top
之後我們需要一個事件處理器(event handler),當勾選框被勾選或取消勾選的時候來更新ReactiveDict
變數。一個事件處理器接收兩個參數,其中第二個參數和onCreated
函數中的this
所代表的是同樣的模板物件:
// Clear form
target.text.value = '';
},
'change .hide-completed input'(event, instance) {
instance.state.set('hideCompleted', event.target.checked);
},
});
接著我們需要修改一下Template.body.helpers
,以下的程式碼中新增了一個if區塊,當勾選框被勾選的時候便會篩選待辦事項:
Template.body.helpers({
tasks() {
const instance = Template.instance();
if(instance.state.get('hideCompleted')) {
// If hide completed is checked, filter tasks
return Tasks.find({ checked: { $ne: true } }, { sort: { createdAt: -1 } });
}
// Otherwise, return all of the tasks
return Tasks.find({}, { sort: { createdAt: -1 } });
},
});
現在只要我們點擊勾選框,待辦事項清單就只會顯示未完的待辦事項囉!
響應式字典
到目前為止,我們將資料儲存於集合(collections)中,當我們修改集合中的資料時,網頁畫面就會自動更新。這是因為Mongo的集合為響應式的資料庫,也就是說只要資料有變動Meteor就會知道。ReactiveDict
也是同樣的原理,但和集合不同的地方在於ReactiveDict
並非像集合一樣與伺服器端同步,這讓ReactiveDict
本身擁有可以暫時儲存使用者介面狀態的優點,就好比我們剛剛使用它來儲存勾選框的狀態,同時它也擁有像集合一樣的優點,我們不需要再寫額外的程式碼來當ReactiveDict
的變數改變時主動去更新模板,我們只需要在輔助器(helper)中呼叫instance.state.get(...)
即可。
如果想更深入了解Meteor的設計模式,可以參考Blaze article。
顯示未完成待辦事項的數量
我們剛寫好篩選未完成待辦事項的功能,再來我們可以用類似的方式來取得未完成待辦事項的數量,只要多加一個輔助器(helper)並且修改其中一行即可。
// Otherwise, return all of the tasks
return Tasks.find({}, { sort: { createdAt: -1 } });
},
incompleteCount() {
return Tasks.find({ checked: { $ne: true } }).count();
},
});
Template.body.events({
<body>
<div class="container">
<header>
<h1>Todo List ({{incompleteCount}})</h1>
<label class="hide-completed">
<input type="checkbox" />