模板渲染

1. ES6模板

b-template 的值的字段在 templates 里面定义. 注意: 子集的内容必须有标签包住. 例如模板里面的li标签.

数据支持数组

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. list: ["我是列表1","我是列表2"],
  5. },
  6. templates: {
  7. tplList: function (data) {
  8. let html = "";
  9. data.forEach(function (item,i) {
  10. html += `<li class="bui-btn">${item}</li>`;
  11. })
  12. return html;
  13. }
  14. }
  15. })

html:

  1. <ul b-template="page.tplList(page.list)" class="bui-list"></ul>

数据支持对象

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. obj: {
  5. title: "我的对象的标题",
  6. content: "<p>我是内容,支持html</p><p>我是内容,支持html</p>"
  7. }
  8. },
  9. templates: {
  10. tplObject: function (data) {
  11. let html = "";
  12. for( let key in data ){
  13. html += `<div class="bui-btn" >${data[key]}</div>`;
  14. }
  15. return html;
  16. }
  17. }
  18. })

渲染模板,数据是对象时,默认不会动态联动

html:

  1. <div b-template="page.tplObject(page.obj)"></div>

如果对象需要动态联动, 有2种方法:

方法1: obj 为数据源

  1. // 改变数据
  2. bs.obj.title = "我的对象的标题2";
  3. // 告诉使用obj的模板,数据变更需要重新渲染
  4. bs.trigger("obj",{value:bs.obj});

方法2: obj 为数据源

  1. // 改变数据
  2. bs.obj.title = "我的对象的标题2"
  3. // 告诉使用obj的模板,数据变更需要重新渲染
  4. bs.set("obj",bs.obj);

方法1 跟 方法2的区别在于, 方法1只是变更并重新触发模板渲染, 方法2, 会对数据的所有键值重新赋值并触发模板渲染.

拆分对象数据,监听改变

html:

  1. <div >
  2. <div b-text="page.obj.title"></div>
  3. <div b-html="page.obj.content"></div>
  4. </div>

复杂对象

支持数据是一个对象, 那就可以更好的设计这个数据了, 但不建议把数据层级设计得太深.

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. objList: {
  5. title: "我是标题",
  6. data: ["我是复杂数据列表1"]
  7. }
  8. },
  9. templates: {
  10. tplObjectList: function (data,e) {
  11. var html = "";
  12. data.forEach(function (item,i) {
  13. html += `<li class="bui-btn">${item}</li>`;
  14. })
  15. return html;
  16. }
  17. }
  18. })
  1. <h2 b-text="page.objList.title"></h2>
  2. <ul b-template="page.tplObjectList(page.objList.data)" class="bui-list"></ul>

如果 h2 是在 ul 里面, 那么默认第一次渲染数据, h2 就会被替换, 这时可以通过 b-command 属性,告诉模板第一次渲染采用什么方式. :) 当然这里ul标签里面放h2标签是不符合w3c标准的. 我们改成li标签.

  • html 替换模板
  • append 在模板后面增加
  • prepend 在模板前面增加
  1. <ul b-template="page.tplObjectList(page.objList.data)" b-command="append" class="bui-list">
  2. <li b-text="page.objList.title"></li>
  3. </ul>

如果 ul 的子集不止有li标签元素? b-children 就可以派上用场, 代表子集的重复元素是哪个选择器?

  • 可以是标签
  • 可以是类名

比方:

  1. <ul b-template="page.tplObjectList(page.objList.data)" b-children=".bui-btn" class="bui-list">
  2. </ul>

这个生成的模板可能是这样的. 如果你使用 bui.array.set 修改数据的时候,变成新增, 这个时候你就要怀疑是不是需要设置 b-children.

  1. <ul b-template="page.tplObjectList(page.objList.data)" b-children=".bui-btn" class="bui-list">
  2. <li class="section-title">我是二级标题</li>
  3. <li class="bui-btn">我是内容0,索引0</li>
  4. <li class="section-title">我是二级标题</li>
  5. <li class="bui-btn">我是内容1,索引1</li>
  6. <li class="section-title">我是二级标题</li>
  7. <li class="bui-btn">我是内容2,索引2</li>
  8. </ul>

2. 数据的增删改

通过b-template的绑定, 我们可以通过操作数组,便能得到页面的及时响应.

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. list: ["我是列表1","我是列表2"],
  5. },
  6. templates: {
  7. tplList: function (data) {
  8. let html = "";
  9. data.forEach(function (item,i) {
  10. html += `<li class="bui-btn">${item}</li>`;
  11. })
  12. return html;
  13. }
  14. }
  15. })

html:

  1. <ul b-template="page.tplList(page.list)" class="bui-list"></ul>

这样绑定以后, 通过脚本操控 bs.list.push("我是列表3") , 页面便能及时渲染新的数据.

不过并非数组的所有操作都能得到及时响应, 目前我们可以监听到以下几种方法:

  • push 在后面增加数据
  • unshift 在前面增加数据
  • shift 删除第1条数据
  • pop 删除最后一条数据
  • splice 删除或者插入新的数据, 具体可以查看数组的splice用法
  • sort 排序
  • reverse 反序
  • length 获取长度

为了更方便的操作数据视图, 我们还提供了几个命令式的方法, 可以方便的对数组进行操作响应. 具体可以查看对应的 bui.array API 使用说明, 在综合案例里面, 我们会频繁的用到.

  • bui.array.empty 清空数组,并触发第1个数组的视图变更
  • bui.array.replace 替换数组,并触发第1个数组的视图变更
  • bui.array.merge 合并数组,并触发第1个数组的视图变更
  • bui.array.set 修改数组的某个值,支持对象
  • bui.array.delete 删除数组的某个值,支持对象

值得注意的是, 如果数组里面是一个对象, 对象的某个字段变更是不会反馈到视图的, 这种时候就可以使用 bui.array.set 来替换整条数据, 达到刷新视图的目的. 这个可以查看 综合案例章节的多选联动的 setStatus 方法, 会修改到数组对象的状态.

1.5.3 以后, 上面那些方法, 可以有更方便的使用方式, 比方:

1.5.4修正: 只有通过 bui.store 初始化劫持的数组,才会有 $方法操作

  1. 清空数组 [].$empty()

var bs = bui.store({ data: { arr:[“hello”,”bui”,”hi”,”easybui”] } }) bui.array.empty( bs.arr );

// 1.5.4 版本以后可以这样 bs.arr.$empty();

  1. 2. 替换数组 [].$replace()
  2. ```js
  3. var bs = bui.store({
  4. data: {
  5. arr:["hello","bui","hi","easybui"]
  6. }
  7. })
  8. bui.array.replace( bs.arr, ["new","bui"]);
  9. // 1.5.4 版本以后可以这样
  10. bs.arr.$replace(["new","bui"]);
  1. 合并数组 [].$merge()

    1. var bs = bui.store({
    2. data: {
    3. arr:["hello","bui","hi","easybui"]
    4. }
    5. })
    6. bui.array.merge( bs.arr, ["new","bui"]);

// 1.5.4 版本以后可以这样 bs.arr.$merge([“new”,”bui”],[“easy”]);

  1. 4. 修改数组 [].$set()
  2. ```js
  3. // 例子1: 修改第几个
  4. var bs = bui.store({
  5. data: {
  6. arr:["hello","bui","easybui"]
  7. }
  8. })
  9. bui.array.set( bs.arr, 1, "new hi");
  10. // ["hello","new hi","easybui"]
  11. // 1.5.4 版本以后可以这样
  12. bs.arr.$set(1, "new hi");
  13. // arr 结果: ["hello","new hi","easybui"]
  1. // 例子2: 修改值等于 bui 为新值 new bui
  2. var bs = bui.store({
  3. data: {
  4. arr:["hello","bui","easybui"]
  5. }
  6. })
  7. bui.array.set( bs.arr, "bui", "new bui");
  8. // ["hello","new bui","easybui"]
  9. // 1.5.4 版本以后可以这样
  10. bs.arr.$set("bui", "new bui");
  11. // arr 结果: ["hello","new bui","easybui"]
  1. // 例子3: 修改对象值
  2. var bs = bui.store({
  3. data: {
  4. arr:[{name:"hello"},{name:"hi"},{name:"easybui"}]
  5. }
  6. })
  7. bui.array.set( bs.arr, 1, {name:"new hi"} );
  8. // [{name:"hello"},{name:"new hi"},{name:"easybui"}]
  9. // 1.5.4 版本以后可以这样
  10. bs.arr.$set(1, {name:"new hi"});
  11. // arr 结果: [{name:"hello"},{name:"new hi"},{name:"easybui"}]
  1. // 例子4: 修改对象某个字段值, 需要传多一个唯一值的字段名
  2. var bs = bui.store({
  3. data: {
  4. arr:[{name:"hello"},{name:"hi"},{name:"easybui"}]
  5. }
  6. })
  7. // 1.5.4 版本以后可以这样
  8. // 单独修改某个值
  9. bs.arr.$set("hello", "hi bui", "name");
  10. // 修改整个对象,不同的key值则会一起合并过去
  11. bs.arr.$set("hi", {name:"new hi"}, "name");
  12. // arr 结果: [{name:"hello"},{name:"new hi"},{name:"easybui"}]
  1. 删除数据并触发视图更新

    1. //例子1: 删除值或索引:
    2. var bs = bui.store({
    3. data: {
    4. arr:["hello","bui","hi","bui"]
    5. }
    6. })
    7. bui.array.delete(bs.arr , "bui" );

// 1.5.4 版本以后可以这样 bs.arr.$delete(“hi”); // arr 结果: [“hello”,”hi”]

  1. ```js
  2. // 例子2: 删除值在哪个字段:
  3. var bs = bui.store({
  4. data: {
  5. arr:[{ "id":1,value:"hello"},{ "id":2,value:"bui"}]
  6. }
  7. })
  8. bui.array.delete( bs.arr, "bui", "value" );
  9. // 1.5.3 版本以后可以这样
  10. bs.arr.$delete("bui", "value");
  11. // arr 结果: [{ "id":1,value:"hello"}]

3. 模板的交互

再来一个交互类的模板, 为了代码更加清晰易懂,样式类的属性都去掉.

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. citysCheck: ["广州","深圳"],
  5. citys: ["广州","深圳","上海","北京"],
  6. },
  7. templates: {
  8. tplListCheck: function (data) {
  9. var html = "";
  10. data.forEach(function (item,i) {
  11. html += `<li class="bui-btn"><label><input type="checkbox" name="city" value="${item}" b-model="page.citysCheck">${item}</label></li>`;
  12. })
  13. return html;
  14. }
  15. }
  16. })
  1. 当前选中: <b b-text="page.citysCheck"></b>
  2. <ul id="cityList" b-template="page.tplListCheck(page.citys)"></ul>

这个模板用到 b-model 属性, 这在一开始有数据的时候, 渲染是正确的, 当数据是异步增加进来以后, 这个数据并没有选中状态. 那要如何处理呢?

模拟数据请求

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. citysCheck: [],
  5. citys: [],
  6. },
  7. templates: {
  8. tplListCheck: function (data) {
  9. let _self = this;
  10. let html = "";
  11. data.forEach(function (item,i) {
  12. // 通过比对,增加选中状态
  13. let hasCheck = bui.array.compare(item,_self.citysCheck);
  14. let checked = hasCheck ? "checked" : "";
  15. html += `<li class="bui-btn"><label><input type="checkbox" name="city" value="${item}" b-model="page.citysCheck" ${checked}>${item}</label></li>`;
  16. })
  17. return html;
  18. }
  19. },
  20. mounted: function () {
  21. // 模拟数据动态改变
  22. setTimeout(()=>{
  23. // 方法1:
  24. this.citysCheck.push("广州","深圳")
  25. this.citys.push("广州","深圳","上海","北京");
  26. // 方法2:
  27. // bui.array.merge(this.citysCheck,["广州","深圳"])
  28. // bui.array.merge(this.citys,["广州","深圳","上海","北京"])
  29. },1000)
  30. }
  31. })

这里其实还有一种办法, 通过使用 this.oneTick 方法绑定 citys 的数据更新,并且视图已经渲染完成以后, 执行多一次解析行为属性. 这个compile要慎用, 多次调用会增加多个重复的回调, 造成性能的损耗.

  1. mounted: function () {
  2. // 模拟数据动态改变
  3. setTimeout(()=>{
  4. // 通过监听 citys 的数据变更并且视图渲染完成以后, 增加数据的解析, 这样就不用在模板里面做数据比对处理了.
  5. // 必须在数据更新之前
  6. this.oneTick("citys",function () {
  7. this.compile("#cityList")
  8. })
  9. // 数据更新
  10. this.citysCheck.push("广州","深圳")
  11. this.citys.push("广州","深圳","上海","北京");
  12. },1000)
  13. }

4. 第三方模板

我们的页面只有干净的绑定, 其它都在模板的方法里面处理逻辑, 正常ES6模板其实已经能够很好的满足我们的需求了, 不过如果你习惯用第三方模板的话, 你也可以使用, 这里以 artTemplate 为例, 需要在首页引入这个模板的js文件.

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. list: ["我是列表1","我是列表2"],
  5. },
  6. templates: {
  7. artTplList: function (data,e) {
  8. var html = template("tpl-list",{ listData: data});
  9. return html;
  10. }
  11. }
  12. })
  1. <ul b-template="page.artTplList(page.list)" class="bui-list"></ul>
  2. <script id="tpl-list" type="text/html">
  3. {{each listData item index}}
  4. <li class="bui-btn" href="pages/ui_controls/bui.store.html" >{{item}}</li>
  5. {{/each}}
  6. </script>

效果预览

查看效果