这篇文章主要涉及control flow部分的binding。


foreach binding主要作用于lists或是tables内数据单元的动态绑定。下面是一个简单的例子:

js部分:

 ko.applyBindings({
  people: [
    { firstName: "Chiaki", lastName: "Izumi" },
    { firstName: "Kazusa", lastName: "Touma" },
    { firstName: "Haruki", lastName: "Murakami" }
  ]
});

html部分:

 <table>
  <thead>
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
    </tr>
  </thead>
  <tbody data-bind="foreach: people">
    <tr>
      <td data-bind="text: firstName"></td>
      <td data-bind="text: lastName"></td>
    </tr>
  </tbody>
</table>

页面显示效果如下:

这里的一个疑问在于如何利用thead显示一列标题,留作以后研究。

在上述示例中,我们简单的在ko.applybindings中添加了一个数组并将其绑定在一个tbody元素中,我们也可以自定义一个view model来实现这一效果,下面是一个更为复杂一些的例子:

js部分:

 function MyViewModel() {
  var self = this;   self.people = ko.observableArray([
    { name: "Chiaki" },
    { name: "Yuki" },
    { name: "Kazusa" }
  ]);   self.addPerson = function() {
    self.people.push({ name: "New name at " + new Date() });
  };   self.removePerson = function() {
    self.people.remove(this);
  };
} ko.applyBindings(new MyViewModel());

html部分:

 <h4>People</h4>
<ul data-bind="foreach: people">
  <li>
    Person at position <span data-bind="text: $index"></span>:
    <span data-bind="text: name"></span>
    <a href="#" data-bind="click: $parent.removePerson">Remove</a>
  </li>
</ul>
<button data-bind="click: addPerson">Add</button>

显示的效果如下:

由于这里并不支持添加script,不能够动态地显示页面。

注意一:以上的两个例子中,在每个foreach binding内部,我们都可以直接调用绑定的数组内每一项的属性,比如firstName和lastName,对于绑定的数组本身,也是可以通过使用$data来调用。这就意味着$data.firstName和firstName的效果是一样的,当然,前者势必会显得更为冗余。

注意二:在第二个例子中,我们使用了$index来表示绑定的数组中每一项的索引,$index是一个observable,每当绑定的数组中的元素索引产生变化时,UI便会对相应的$index进行更新。另外,我们也能通过$parent来获取foreach外部的数据。有关$index和$parent的更为详尽的内容,可参考binding context properties,留作以后研究。

注意三:在注意一中提到,我们能够通过$data来调用foreach绑定的数组本身,但是当我们使用嵌套的foreach,内层foreach如何能够调用外层foreach绑定的数组呢?这时我们可以借由as给外层foreach所绑定的数组定义一个另外的名称,进而在内层foreach中利用此名称来调用外层foreach所绑定的数组。接下来是一个简单的例子:

js部分:

 var viewModel = {
  person: ko.observableArray([
    { name: "Chiaki", friends: [ "Charlie", "Kazusa" ] },
    { name: "Kazusa", friends: [ "Chiaki", "Charlie" ] },
    { name: "Charlie", friends: [ "Chiaki", "Kazusa" ] }
  ])
}; ko.applyBindings(viewModel);

html部分:

 <ul data-bind="foreach: { data: person, as: 'person' }">
  <li>
3     <ul data-bind="foreach: { data: friends, as: 'friends' }">
      <li>
        <span data-bind="text: person.name"></span>:
        <span data-bind="text: friends"></span>
      </li>
    </ul>
  </li>
</ul>

页面显示效果如下:

这个例子中的外层foreach绑定的是person数组,person数组中有一个属性name和另外一个数组firends,在内层foreach中绑定的是数组firends。当我们在内层foreach要调用外层的person数组内的属性时,借由as,使用了person.name来调用。而这里也有一个有趣的情况,就是当一个数组里面只有单纯的元素,比如说friends数组,它的元素并不是object,也就不存在这identifier的问题,这时我们要调用它的元素时,只需要调用数组本身即可,这也就是为什么在之前的示例中如果我们调用绑定的数组本身会返回[object, object]。这表明,一般情况下,遍历数组中的元素只需要调用数组名(as指定)或是调用$data即可,而碰到那些内部元素是object的时候,我们要调用object内的属性则需要调用相应属性的名称。

另外需要注意的一点就是,as后所跟着的必须是一个字符串(as: "person"而不是as: person)。

注意四:有些情况下,我们使用foreach的场合会比较复杂,比如说如下的例子:

 <ul>
  <li>Header item</li>
  <!-- The following are generated dynamically from an array -->
  <li>Item A</li>
  <li>Item B</li>
  <li>Item C</li>
</ul>

这种情况下,我们并不知道改在哪个元素内添加foreach。如果是在ul添加的话,我们事先写好的header item便会被覆盖掉,而ul元素内又只能嵌套li元素,添加另一个容器来实现foreach也是不可行的。为了解决这一问题,我们需要使用无容器的控制流语法(containerless control flow syntax),与先前所提到的containerless text syntax类似。一个简单的例子如下:

js部分:

 var viewModel = {
people: ko.observableArray([
"Kazusa",
"Chiaki",
"Yuki"
])
}; ko.applyBindings(viewModel);

html部分:

 <ul>
<li>Header item</li>
<!-- ko foreach: people -->
<li>name: <span data-bind="text: $data"></span></li>
<!-- /ko -->
</ul>

页面显示效果如下:

官方文档中的注意五涉及到更改所绑定的数组时的性能问题,注意六主要涉及到destroyed的数组,这部分留作之后研究。

注意七:当我们需要在生成的DOM元素上运行一些自定义的逻辑时,我们可以用到afterRender/afterAdd/beforeRemove/beforeMove/afterMove等回调函数。

需要注意的是,这些回调函数仅仅适用于触发与列表的改变有关的动画,如果我们需要对新添加的DOM元素附加一些其他的行为,比如说事件的处理或是第三方的UI控制,将这些行为作为自定义的绑定(custom binding)会更为合适,因为这样设定的行为是与foreach互相独立的。

接下来是一个调用afterAdd的简单的例子,其中用到了jQuery Color plugin

js部分:

 var viewModel = {
people: ko.observableArray([
"Kazusa",
"Chiaki",
"Yuki"
]), yellowFadeIn: function(element, index, data) {
$(element).filter("li")
      .animate({ backgroundColor: "yellow" }, 200)
      .animate({ backgroundColor: "white" }, 800);
}, addItem: function() { this.people.push("New person"); }
}; ko.applyBindings(viewModel);

我对这里的this的使用是有疑问的,联想到computed observable内this的使用,留作之后研究。

html部分:

 <ul data-bind="foreach: { data: people, afterAdd: yellowFadeIn }">
<li data-bind="text: $data"></li>
</ul> <button data-bind="click: addItem">Add</button>

由于我还没掌握制作和插入动态图片的方式,这里不展示显示的效果,不过在点击Add button之后,New person会被添加,通知其背景颜色又黄色变为白色。

以下是对一些回调函数详尽的介绍:

afterRender是在foreach模块初始化或是添加新的元素时触发的,其接受的参数为(为了能够保持愿意,这里用英文显示,下同):

  1. An array of the inserted DOM elements
  2. The data item against which they are being bound

afterAdd与afterRender类似,不过它只会在新元素添加时触发(foreach初始化的时候并不会触发),它所接受的参数为:

  1. A DOM node being added to the document
  2. The index of the added array element
  3. The added array element

beforeRemove函数会在数组中的某一项被删除时触发。需要注意的是,beforeRemove实际上替代了UI界面对remove所做出的回应,即在beforeRemove函数中如果不对DOM相应的元素进行remove操作,则在页面上的元素是不会被删除的,但是viewModel中的数组相应的元素却已经被删除了。beforeRemove函数接受以下参数:

  1. A DOM node that you should remove
  2. The index of the removed array element
  3. The removed array element

beforeMove函数会在数组中的元素索引发生变化时触发,beforeMove会应用于所有索引产生变化的元素,即假若我们在数组开头插入元素,则其后所有元素都将受到beforeMove回调函数的影响。一个比较常用的做法是通过beforeMove来保存原有元素的坐标以便我们能够在afterMove中控制元素移动的动画。beforeMove函数接受以下参数:

  1. A DOM node that may be about to move
  2. The index of the moved array element
  3. The moved array element

afterMove函数也会在数组中的元素索引发生变化时触发,afterMove会应用于所有索引产生变化的元素,即假若我们在数组开头插入元素,则其后所有元素都将受到afterMove回调函数的影响。afterMove函数接收以下参数:

  1. A DOM node that may have moved
  2. The index of the moved array element
  3. The moved array element

对于回调函数中的before和after,我们应该有一个比较清醒的认识。before和after针对的都是UI界面中的元素变化,也就是页面产生变化之前和页面产生变化之后,与此同时,viewModel部分的数组已经发生了变化,正是viewModel部分的数组的变化才触发了before和after所对应的回调函数。

KnockoutJs学习笔记(六)的更多相关文章

  1. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  2. Learning ROS for Robotics Programming Second Edition学习笔记(六) indigo xtion pro live

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  3. Typescript 学习笔记六:接口

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  4. python3.4学习笔记(六) 常用快捷键使用技巧,持续更新

    python3.4学习笔记(六) 常用快捷键使用技巧,持续更新 安装IDLE后鼠标右键点击*.py 文件,可以看到Edit with IDLE 选择这个可以直接打开编辑器.IDLE默认不能显示行号,使 ...

  5. Go语言学习笔记六: 循环语句

    Go语言学习笔记六: 循环语句 今天学了一个格式化代码的命令:gofmt -w chapter6.go for循环 for循环有3种形式: for init; condition; increment ...

  6. 【opencv学习笔记六】图像的ROI区域选择与复制

    图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...

  7. Linux学习笔记(六) 进程管理

    1.进程基础 当输入一个命令时,shell 会同时启动一个进程,这种任务与进程分离的方式是 Linux 系统上重要的概念 每个执行的任务都称为进程,在每个进程启动时,系统都会给它指定一个唯一的 ID, ...

  8. # go微服务框架kratos学习笔记六(kratos 服务发现 discovery)

    目录 go微服务框架kratos学习笔记六(kratos 服务发现 discovery) http api register 服务注册 fetch 获取实例 fetchs 批量获取实例 polls 批 ...

  9. Spring Boot 学习笔记(六) 整合 RESTful 参数传递

    Spring Boot 学习笔记 源码地址 Spring Boot 学习笔记(一) hello world Spring Boot 学习笔记(二) 整合 log4j2 Spring Boot 学习笔记 ...

  10. Redis学习笔记六:持久化实验(AOF,RDB)

    作者:Grey 原文地址:Redis学习笔记六:持久化实验(AOF,RDB) Redis几种持久化方案介绍和对比 AOF方式:https://blog.csdn.net/ctwctw/article/ ...

随机推荐

  1. 【设计模式】——工厂方法FactoryMethod

    前言:[模式总览]——————————by xingoo 模式意图 工厂方法在MVC中应用的很广泛. 工厂方法意在分离产品与创建的两个层次,使用户在一个工厂池中可以选择自己想要使用的产品,而忽略其创建 ...

  2. MT【104】高斯函数找周期

    分析:$t(n)=n-[\frac{n}{2}]-[\frac{n}{3}]-[\frac{n}{6}]$的周期为6,故 $\sum\limits_{n=1}^{2014}(n-t(n))=\sum\ ...

  3. 【题解】 bzoj4004: [JLOI2015]装备购买 (线性基)

    bzoj4004,戳我戳我 Solution: 裸的线性基,这没啥好说的,我们说说有意思的地方(就是我老是wa的地方) Attention: 这题在\(luogu\),上貌似不卡精度,\(bzoj\) ...

  4. 【BZOJ4888】[TJOI2017]异或和(树状数组)

    [BZOJ4888][TJOI2017]异或和(树状数组) 题面 BZOJ 洛谷 题解 考虑每个位置上的答案,分类讨论这一位是否存在一,值域树状数组维护即可. #include<iostream ...

  5. 洛谷 P2542 [AHOI2005]航线规划 解题报告

    P2542 [AHOI2005]航线规划 题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系--一个巨大的由千百万星球构成的Samuel星系 ...

  6. LANMP环境编译参数查看方法

    nginx编译参数查看:/usr/local/nginx/sbin/nginx -V apache编译参数查看:cat /usr/local/apache2/build/config.nice mys ...

  7. websoclet简单示例 my 改

    首先,创建一个 maven war 项目: 首先,pom文件: <project xmlns="http://maven.apache.org/POM/4.0.0" xmln ...

  8. ReentrantLock与synchronized

    1.ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候 线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O ...

  9. Sql数据库不能频繁连接

    这个问题怎么说呢,我频繁的读一个json文件,所以就频繁的去连接了数据库.所以导致了数据库后来就不工作了(罢工?O(∩_∩)O哈哈~) 解决办法是加一个判断语句,如果是空的就连接,否则就别一直连接了. ...

  10. bzoj千题计划223:bzoj2816: [ZJOI2012]网络

    http://www.lydsy.com/JudgeOnline/problem.php?id=2816 每种颜色搞一个LCT 判断u v之间有边直接相连: 如果u和v之间有边相连,那么他们的深度相差 ...