核心思路: 定義了3個(gè)字段.
todo
是當(dāng)前已有的數(shù)據(jù), 會(huì)通過(guò) b-template
進(jìn)行渲染. todoText
搜索框的默認(rèn)文本, 如果空則顯示placeholder
, 通過(guò) b-model
進(jìn)行關(guān)聯(lián).nextTodoId
這個(gè)是數(shù)據(jù)的自增字段, 不是必須, 可以使用唯一id來(lái)處理.
定義了一個(gè)方法, 通過(guò) b-click
進(jìn)行綁定. 點(diǎn)擊的時(shí)候, 檢驗(yàn)字段是否為空, 不為空則增加一條待辦.
模板里面還通過(guò)b-click
綁定了一個(gè)removeTodo
刪除的方法, 通過(guò)索引刪除, 這里用到一個(gè)動(dòng)態(tài)索引, 這個(gè)是內(nèi)置的, 前面在事件綁定章節(jié)已經(jīng)講過(guò). 有增刪改的數(shù)據(jù), i
拿到的是固定的值.
var bs = bui.store({
scope: "page",
data: {
todo: [{
id: 1,
title: "參加項(xiàng)目會(huì)議"
},{
id: 2,
title: "項(xiàng)目啟動(dòng)會(huì)"
},{
id: 3,
title: "看電影"
}],
todoText: "",
nextTodoId: 4
},
methods: {
addTodo: function (e) {
if( this.todoText ){
this.todo.push({
id: this.nextTodoId++,
title:this.todoText
})
this.todoText = "";
}else{
bui.hint("請(qǐng)?zhí)顚?xiě)待辦事項(xiàng)")
}
},
removeTodo: function (index) {
// 方法1: 通過(guò)索引刪除
this.todo.splice(index,1);
this.todoText = "";
}
},
templates: {
tplTodo: function (data) {
var _self = this;
var html = "";
data.forEach(function (item,i) {
html += `<li id="${item.id}" class="bui-btn bui-box">
<div class="span1">${item.title}</div>
<i b-click="page.removeTodo($parentIndex)" class="icon-remove large"></i>
</li>`;
})
return html;
}
}
})
核心html
<!-- 搜索條控件結(jié)構(gòu) -->
<div class="bui-searchbar bui-box">
<div class="span1">
<div class="bui-input">
<i class="icon-search"></i>
<input type="text" value="" placeholder="請(qǐng)輸入待辦" b-model="page.todoText" />
<div class="bui-btn" b-click="page.addTodo">添加</div>
</div>
</div>
</div>
<!-- 數(shù)組todo lendth 改變的時(shí)候,不會(huì)自動(dòng)觸發(fā),需要監(jiān)聽(tīng) page.todo 數(shù)組改變 -->
<div class="section-title">待辦事項(xiàng): <b b-text="page.todo.length"></b></div>
<ul b-template="page.tplTodo(page.todo)" class="bui-list"></ul>
如果數(shù)據(jù)一開(kāi)始有值,還需要把值跟模板里的數(shù)據(jù)進(jìn)行比對(duì),處理成選中狀態(tài).
var bs = bui.store({
scope: "page", // 用于區(qū)分公共數(shù)據(jù)及當(dāng)前數(shù)據(jù)的唯一值
data: {
items: [{
id: "guangzhou",
name: "廣州",
}, {
id: "shenzhen",
name: "深圳",
}, {
id: "dongguan",
name: "東莞",
}],
checked: ["shenzhen"], //緩存選中的值, 默認(rèn)選中深圳
checkedObj: [{
id: "shenzhen",
name: "深圳",
}],
},
methods: {
open: function() {
this.uiDialog.open();
}
},
watch: {
checked: function(val) {
var _self = this;
// 獲取的使用 this.$data.xxx
var items = bui.array.getAll(_self.$data.items, val, "id");
// 替換新的值 this.xxx
bui.array.replace(this.checkedObj, items);
}
},
computed: {},
templates: {
tplItem: function(data) {
var html = "";
data.forEach(function(item, i) {
html += `<li class="bui-btn" id="${item.id}">${item.name}</li>`
})
return html;
},
tplCity: function(data) {
var html = "";
var _self = this;
data.forEach(function(item, i) {
// 渲染已經(jīng)選擇的城市
var hasChoose = _self.checkedObj && bui.array.compare(_self.checkedObj, item.id, "id");
var hasChecked = hasChoose ? "checked" : "";
html += `<li class="bui-btn bui-box bui-btn-line">
<div class="span1">
<label for="interest+${i}">${item.name}</label>
</div>
<input id="interest+${i}" type="checkbox" class="bui-choose" name="interest" value="${item.id}" text="" ${hasChecked} b-model="page.checked">
</li>`
})
return html;
}
},
mounted: function() {
// 加載后執(zhí)行
this.uiDialog = bui.dialog({
id: "#uiDialog"
});
}
})
注意:
checkedObj: null
數(shù)組如果需要通過(guò)this.checkedObj = []
賦值操作, 先設(shè)置為空; 如果初始值是數(shù)組, 則需要通過(guò)bui.array.xxx
去操作才會(huì)觸發(fā)界面響應(yīng).
<div class="bui-page page-store">
<main>
<div class="bui-btn" b-click="page.open()">點(diǎn)擊選擇喜歡的城市</div>
<div class="subtitle">您已選擇:</div>
<!-- 列表控件 html 對(duì)應(yīng)的結(jié)構(gòu): -->
<ul class="bui-list" b-template="page.tplItem(page.checkedObj)"></ul>
</main>
<!-- 對(duì)話(huà)框需要在 bui-page 里面, 這樣默認(rèn)才會(huì)解析 b- 行為屬性的值 page.xxx -->
<div id="uiDialog" class="bui-dialog">
<div class="bui-dialog-head">選擇喜歡的城市</div>
<div class="bui-dialog-main">
<ul class="bui-list" b-template="page.tplCity(page.items)"></ul>
</div>
<div class="bui-dialog-close"><i class="icon-close"></i></div>
</div>
</div>
在data設(shè)計(jì)了4個(gè)字段, 分別是:
selectA
A的數(shù)據(jù)源selectB
B的數(shù)據(jù)源selectAChecked
A的選中暫存區(qū)selectBChecked
B的選中暫存區(qū)定義了4個(gè)方法:
modifyStatusA
點(diǎn)擊以后修改A的激活狀態(tài), 并把數(shù)據(jù)存到A暫存區(qū)modifyStatusB
點(diǎn)擊以后修改B的激活狀態(tài), 并把數(shù)據(jù)存到B暫存區(qū)addToB
合并A的選中數(shù)據(jù)到B的數(shù)據(jù)源里面, 數(shù)據(jù)改變會(huì)自動(dòng)渲染到視圖addToA
合并B的選中數(shù)據(jù)到A的數(shù)據(jù)源里面, 數(shù)據(jù)改變會(huì)自動(dòng)渲染到視圖操作數(shù)據(jù)便會(huì)更新視圖. 代碼有點(diǎn)多, 但是理清了思路,我們后面再看優(yōu)化版.
var bs = bui.store({
scope: "page",
data: {
selectAChecked: [], // A區(qū)選中暫存區(qū)
selectBChecked: [], // B區(qū)選中暫存區(qū)
selectA: [ // 聯(lián)動(dòng)select的數(shù)據(jù)源
{ text: 'One', value: 'A', selected: false },
{ text: 'Two', value: 'B', selected: false },
{ text: 'Three', value: 'C', selected: false }
],
selectB: [],
},
methods: {
modifyStatusA: function (index) {
var selectedItem = this.selectA[index],
selecteds = this.selectAChecked,
// 判斷是否唯一
indexs = bui.array.index(this.selectA[index].value,selecteds,"value");
// 選中暫存區(qū)的增加或減少
if( indexs > -1 ){
selecteds.splice(indexs,1);
}else{
selecteds.push(selectedItem);
}
// 修改選中狀態(tài)
this.selectA[index].selected = !this.selectA[index].selected;
// 替換整條數(shù)據(jù)并觸發(fā)數(shù)據(jù)變更
bui.array.set(this.selectA,index,this.selectA[index]);
},
modifyStatusB: function (index) {
var selectedItem = this.selectB[index],
selecteds = this.selectBChecked,
indexs = bui.array.index(this.selectB[index].value,selecteds,"value");
// 選中暫存區(qū)的增加或減少
if( indexs > -1 ){
selecteds.splice(indexs,1);
}else{
selecteds.push(selectedItem);
}
// 更新字段
this.selectB[index].selected = !this.selectB[index].selected;
// 替換整條數(shù)據(jù)并觸發(fā)數(shù)據(jù)變更
bui.array.set(this.selectB,index,this.selectB[index]);
},
addToB: function (e) {
// 刪除選中狀態(tài)
this.selectAChecked.forEach(function(item,i){
item.selected = false;
})
// 合并并觸發(fā) this.selectB
bui.array.merge(this.selectB,this.selectAChecked);
// 刪除this.selectA選中數(shù)據(jù),通過(guò)value字段比對(duì),支持多個(gè)
bui.array.remove(this.selectA,this.selectAChecked,"value")
// 清空A暫存區(qū)數(shù)據(jù)
bui.array.empty(this.selectAChecked);
},
addToA: function (e) {
// 刪除選中狀態(tài)
this.selectBChecked.forEach(function(item,i){
item.selected = false;
})
// 合并并觸發(fā) this.selectA
bui.array.merge(this.selectA,this.selectBChecked);
// 刪除選中數(shù)據(jù),通過(guò)value字段比對(duì)
bui.array.remove(this.selectB,this.selectBChecked,"value")
// 清空B暫存區(qū)數(shù)據(jù)
bui.array.empty(this.selectBChecked);
},
},
templates: {
// 聯(lián)動(dòng)的示例,增加了事件綁定
tplSelectA: function (data,te) {
var html ='';
data.forEach(function (item,i) {
var active = item.selected ? "active" : "";
html +=`<li b-click='page.modifyStatusA($index)' class="bui-btn ${active}">${item.text}</li>`;
})
return html;
},
tplSelectB: function (data) {
var html ='';
data.forEach(function (item,i) {
var active = item.selected ? "active" : "";
html +=`<li b-click='page.modifyStatusB($index)' class="bui-btn ${active}">${item.text}</li>`
})
return html;
}
}
})
核心的html綁定
<style type="text/css">
.bui-select .active {
color: red;
}
</style>
<div class="bui-box">
<div class ="span1">
<h2 class="bui-box"><b b-text="page.selectAChecked.length"></b>/<b b-text="page.selectA.length"></b></h2>
<div class="bui-select" b-template="page.tplSelectA(page.selectA)">
</div>
</div>
<div style="width: 100px">
<div class="bui-btn" b-click="page.addToB">添加到B</div>
<div class="bui-btn" b-click="page.addToA">添加到A</div>
</div>
<div class="span1">
<h2 class="bui-box"><div class="span1">列表2</div><b b-text="page.selectBChecked.length"></b>/<b b-text="page.selectB.length"></b></h2>
<div class="bui-select" b-template="page.tplSelectB(page.selectB)">
</div>
</div>
</div>
優(yōu)化了操作流程, 在原來(lái)的data增加多了2個(gè)狀態(tài): 這2個(gè)狀態(tài)都是一個(gè)對(duì)象, 因?yàn)閷?shí)際上想要的是 disabled
這個(gè)樣式名, 如果你的樣式上, 是設(shè)計(jì)的 canAdd
, canDel
作為樣式名, 只需要布爾值就行.
原先的4個(gè)方法, 優(yōu)化成了2個(gè), 一個(gè)點(diǎn)擊的時(shí)候修改狀態(tài), 一個(gè)是合并數(shù)據(jù).
原先的2個(gè)模板方法, 優(yōu)化成了1個(gè), 通過(guò)傳進(jìn)來(lái)的不同字段進(jìn)行處理就行. 這里要理解,b-click='page.setStatus(${target},$index,${targetChecked})'
${target}
跟 $index
的區(qū)別.
注意:
b-template
傳過(guò)去的第一個(gè)值會(huì)被解析成數(shù)據(jù), 其它參數(shù)傳什么就是什么.
/**
* 設(shè)計(jì)思路說(shuō)明:
* 左邊列表A: selectA
* 右邊列表B: selectB
* 左邊選中列表暫存區(qū)A: selectAChecked
* 右邊選中列表暫存區(qū)B: selectBChecked
* 點(diǎn)擊列表, 往暫存區(qū)存放對(duì)應(yīng)的數(shù)據(jù), 并且通過(guò)watch 暫存區(qū)的數(shù)據(jù)變更,把按鈕的狀態(tài)變成能夠操作.
* 點(diǎn)擊按鈕添加到B, 則把右邊列表數(shù)據(jù),合并A選中的暫存區(qū), 并刪除選中狀態(tài), 清空A暫存區(qū)數(shù)據(jù)
* 點(diǎn)擊按鈕添加到A, 則把左邊列表數(shù)據(jù),合并B選中的暫存區(qū), 并刪除選中狀態(tài), 清空B暫存區(qū)數(shù)據(jù)
*/
var bs = bui.store({
scope: "page",
data: {
canAdd: {
disabled: true
},
canDel: {
disabled: true
},
selectAChecked: [], // A區(qū)選中暫存區(qū)
selectBChecked: [], // B區(qū)選中暫存區(qū)
selectA: [
{ text: 'One', value: 'A', selected: false },
{ text: 'Two', value: 'B', selected: false },
{ text: 'Three', value: 'C', selected: false }
],
selectB: [],
},
methods: {
setStatus: function (target,index,checked) {
var selectedItem = this[target][index],
selecteds = this[checked],
// 判斷是否唯一
indexs = bui.array.index(this[target][index].value,selecteds,"value");
// 選中暫存區(qū)的增加或減少
if( indexs > -1 ){
selecteds.splice(indexs,1);
}else{
selecteds.push(selectedItem);
}
// 修改選中狀態(tài)
this[target][index].selected = !this[target][index].selected;
// 替換第幾條數(shù)據(jù)并觸發(fā)數(shù)據(jù)this[target] 的dom變更
bui.array.set(this[target],index,this[target][index]);
},
moveSelect: function (target,checked,targetB) {
// 修改按鈕狀態(tài)
var btn = checked == "selectBChecked" ? "canDel" : "canAdd";
if( this[btn].disabled ){ return; }
// 移動(dòng)過(guò)去以后,不需要選中狀態(tài)
this[checked].forEach(function (item) {
item.selected = false;
})
// 合并并觸發(fā) this.selectB
bui.array.merge(this[target],this[checked]);
// 刪除this.selectA選中數(shù)據(jù),通過(guò)value字段比對(duì),支持多個(gè)
bui.array.delete(this[targetB],this[checked],"value");
// 清空暫存區(qū)數(shù)據(jù)
bui.array.empty(this[checked]);
},
},
watch: {
selectAChecked: function (data) {
// 修改添加按鈕狀態(tài)
this.canAdd.disabled = !data.length;
},
selectBChecked: function (data) {
// 修改刪除按鈕狀態(tài)
this.canDel.disabled = !data.length;
}
},
templates: {
// 聯(lián)動(dòng)的示例,增加了事件綁定
tplSelect: function (data,target,targetChecked) {
var html ='';
data.forEach(function (item,i) {
var active = item.selected ? "active" : "";
// $index 為內(nèi)置的動(dòng)態(tài)索引, i 不一定等于 $index
html +=`<li b-click='page.setStatus(${target},$index,${targetChecked})' class="bui-btn ${active}">${item.text}</li>`;
})
return html;
}
}
})
<style type="text/css">
.bui-select .active {
color: red;
}
.btn-controls {
width: 1rem;
margin:0 .1rem;
}
.btn-controls .bui-btn{
margin-top:.1rem;
}
</style>
<div class="bui-box">
<div class ="span1">
<div class="subtitle bui-box">
<div class="span1">列表1</div>
<b b-text="page.selectAChecked.length"></b>/<b b-text="page.selectA.length"></b>
</div>
<div b-template="page.tplSelectA(page.selectA)" class="bui-list">
</div>
</div>
<div class="btn-controls">
<!-- 傳多個(gè)數(shù)據(jù)源字段 -->
<div b-click="page.moveSelect(selectB,selectAChecked,selectA)" b-class="page.canAdd" class="bui-btn round">>></div>
<div b-click="page.moveSelect(selectA,selectBChecked,selectB)" b-class="page.canDel" class="bui-btn round"><<</div>
</div>
<div class="span1">
<div class="subtitle bui-box">
<div class="span1">列表2</div>
<b b-text="page.selectBChecked.length"></b>/<b b-text="page.selectB.length"></b>
</div>
<div b-template="page.tplSelectB(page.selectB)" class="bui-list">
</div>
</div>
</div>
<div class="bui-page page-store page-input">
<header>
<div lass="bui-bar">
<div class="bui-bar-left">
<a class="bui-btn" onclick="bui.back();"><i class="icon-back"></i></a>
</div>
<div class="bui-bar-main">動(dòng)態(tài)表單</div>
<div class="bui-bar-right">
</div>
</div>
</header>
<main>
<div class="bui-btn" b-click="page.add"><i class="icon-plus"></i>添加</div>
<div b-template="page.tplUser(page.users)">
</div>
<div class="container-xy">
<button class="bui-btn primary round" b-click="page.submitForm">提交</button>
</div>
</main>
</div>
點(diǎn)擊添加會(huì)新增一個(gè)動(dòng)態(tài)表單, 注意
$index
的指向是指向b-target="ul"
, 獲得的索引,才是跟數(shù)據(jù)一一對(duì)應(yīng)的.
var bs = bui.store({
scope: "page", // 用于區(qū)分公共數(shù)據(jù)及當(dāng)前數(shù)據(jù)的唯一值
data: {
users: [{
name: "",
id: ""
}],
},
methods: {
submitForm: function(e) {
console.log(this.users)
return false;
},
add: function() {
this.users.push({
name: "",
id: ""
})
},
remove: function(index) {
console.log(index)
bui.array.deleteIndex(this.users, index);
}
},
watch: {},
computed: {},
templates: {
tplUser: function(data) {
var html = `<ul class="bui-list">`;
var that = this;
data.forEach(function(item, index) {
let length = that.users.length - 1;
html += `<li class="bui-btn-title bui-box clearactive">表單${length}<div class="span1"></div><div class="bui-btn" b-click="page.remove($index)" b-target="ul"><i class="icon-remove"></i></div></li><li class="bui-btn bui-box clearactive">
<label class="bui-label">姓名</label>
<div class="span1">
<div class="bui-value"><input type="text" name="fname" b-model="page.$index.name" b-target="ul"></div>
</div>
</li>
<li class="bui-btn bui-box clearactive">
<label class="bui-label">身份證</label>
<div class="span1">
<div class="bui-value"><input type="text" name="fname" b-model="page.$index.id" b-target="ul"></div>
</div>
</li>`
})
html += `</ul>`
return html;
}
},
mounted: function() {
// 加載后執(zhí)行
}
})
數(shù)據(jù)驅(qū)動(dòng)的改變?cè)谟谒悸返霓D(zhuǎn)變, 合理的使用,可以大大的減少手動(dòng)操作dom的代碼. 數(shù)據(jù)驅(qū)動(dòng)不比dom操作, 有可能需要去理解核心的業(yè)務(wù)以后才能上手, 所以在代碼的設(shè)計(jì)數(shù)據(jù)上, 寫(xiě)上自己的設(shè)計(jì)思路, 這對(duì)于后面的維護(hù)會(huì)很有幫助.
bui.store
并不是必須用到的, 使用單頁(yè)及模塊化已經(jīng)可以很好的處理各種問(wèn)題. 只是在一些表單聯(lián)動(dòng)上, 你會(huì)發(fā)現(xiàn)這個(gè)真的很有用!
更多建議: