码迷,mamicode.com
首页 > 其他好文 > 详细

5个KO实例带你装B带你飞

时间:2015-01-23 19:48:53      阅读:272      评论:0      收藏:0      [点我收藏+]

标签:

Knockoutjs 以下简称 ko, 是一个 javascript 的 MVVM 框架.

在 MVC 4 的项目模板中, 默认会引用 knockoutjs, 我正式接触它也不过4个月的时间.

在没有使用它之前, 要做一个主从关系的维护, 大概要写一堆 CloneNode / Append, js 水平一般的同学, 要写这样一个业务页面, 大概要写几百几千行 js 才行.

 

本示例不是介绍 ko 的用法, 而是 以 我在项目中 处理过的 稍有些复杂的 业务 为原型, 做深入一步的理解如何用KO带你装B带你飞. 所以, 如果你不了解 ko , 可以先看看 ko 的文档:

http://knockoutjs.com/documentation/introduction.html

 

示例下载地址 (VS2013 MVC5):

http://files.cnblogs.com/xling/KOExamples.7z

 

四级联动

一分钱难倒英雄汉, 曾经,有位兄弟, 有个处理5级联动 的业务 的任务, 涉及到9张表, 在同一个页面. 总之搞的昏天暗地, 也没有最终做完.

技术分享

本示例中用到的: foreach / click / bind / attr / $parent/ $parentContext / visible / $data / $root / textInput / $index

 

bind

在 click 绑定说明文档中的第二节, Note 2: Accessing the event object, or passing more parameters.

如果事件绑定不指定参数, 默认的参数就是当前绑定到Html 对象上的 模型数据.

$parent / $parentContext

$parent 指 当前 绑定的模型数据 的 父模型数据.

$parentContext 指 当前 绑定的模型数据 的父模型数据的上下文.

 

visible

主从关系的经典场景就是: 点击主记录,显示对应的从数据列表.

在 html 中, 能达到这种效果的最简单的途径就是 显示与隐藏, 或动态生成.

如果是基于 AJAX 的数据提交, 动态生成无所谓, 最终的数据都记录在 JS对象中. 但是如果是基于 Form 表单提交, 动态生成子列表就不行了, 所以本示例用的是 显示/隐藏.

 

示例界面上的几个点

在VS 中, 按 F5 Debug , 打开 IE 后, 在解决方案中可以看到一个 Function Code 项, 可以对其进行调试:

技术分享

 

序号

序号使用的是 $index , $Index 只存在于 foreach 中, 是 $context 的子对象.

技术分享

 

 

总数

数组的长度

技术分享

因为 Companys 的定义如下:

this.Companys = ko.observableArray();

ko.observableArray() 返回的是 Function 对象, 该对象也有一个 length 属性, 但是其值一直都是 0

Companys() 返回的即上图中的 _lastValue 数组, 所以, 在绑定的时候, 是写的:

text: Companys().length , 而不是 text: Companys.length

跟据你的 Companys 定义而定. 如果 this.Companys = […] 的话, 绑定就要写成 text: Compansys.length 了.

当前活动对象

在点击某一行后, 要把子级列表显示出来, 这里是通过修改模型数据中的 IsCurrent 来实现的.

<tr data-bind="click:SetCurrent.bind(null,$data, $root),attr:{class:IsCurrent() ? ‘success‘ : ‘‘}">
 3 var BaseObj = function () {
 5 /// <summary>可设置当前活动对象的基类</summary>
 9     //是否当前对象
11     this.IsCurrent = ko.observable(false);
13     this.SetCurrent = SetCurrent;
15 };
16 
19 var SetCurrent = function (o, oc) {
21 /// <summary>设置当前活动对象</summary>
23 /// <param name="o" type="object">要设置为活动的对象</param>
25 /// <param name="oc" type="object">活动对象的记录者</param>
26 
27     if (o == null)
29        return;
30 
33     //将原来的活动对象变为非活动对象.
35     if (oc.Current != null)
37         oc.Current.IsCurrent(false);
38 
41     oc.Current = o;
43     oc.Current.IsCurrent(true);
45 }

 

子级列表的 visible 也绑定到了 IsCurrent 上:

<!-- ko foreach:Companys -->
<div data-bind="foreach:Products, visible:IsCurrent()"> Companys 的作用范围products 的作用范围

 

这样, 当主记录的 IsCurrent 变化后, 子列表显示/隐藏就会随着变化.

 

 

拖动排序

本示例中使用拖动插件是 jquery ui 提供的 sortable

技术分享

 

一开始我是取 拖动调整后 的 单元格(TD)中的港口代码 来重组模型数据.

但是该列表中, 同一港口代码允许出现两次. 这样就出现问题了.

其实跟本就不用那么复杂.

ko 有个 dataFor API , 直接拿来用就是了.

 

 1 var sortRegPortsByUISeq = function () {
 3     var ports = [];
 5     $("tbody tr").each(function (i, tr) {
 7         //获取绑定到行上的数据对象
 9         var port = ko.dataFor(tr);
11         ports.push(port);
13     });
17     vm.RegPorts(ports);
19 }

 

条件

技术分享

该业务的条件如下:

如果为一口价, 只允许有一条数据. 可以没有数据.

自动把上一条的 "小于" 带到 下一条的 "大于" 中. 因为 "大于"不能手工输入.

 

控制新增按钮显示与否用以下语法:

visible: (FAK_TEU_FORMULA_MODE() == 1 && $data.FAK().length < 1) || (FAK_TEU_FORMULA_MODE() != 1)

 

FAK_TEU_FORMULA_MODE() == 1 即 一口价

$data.FAK().length < 1 即 当前还没有数据

 

自动带入上一条的值到下一条的实现:

//RANGE_TO的更改之前的值
this._RangeTo = 0;

//当 RANGE_TO 改变的时候, 执行 vm.RangeToChanged 方法
this.RANGE_TO.subscribe(doFunction(vm.RangeToChanged, this, type));

this.RANGE_TO.subscribe(function (o) {
/// <summary>change 之前, 将 RANGE_TO 的值保存,用于后面比较</summary>
self._RangeTo = self.RANGE_TO.peek() || 0; //.peek() 返回可观察对象的最后值
}, null, "beforeChange")

 

这里定义了一个 RANGE_TO 和 一个 _RANGE_TO. _RANGE_TO 用于保存旧值.

还定义了两个针对 RANGE_TO 的 订阅, 一个无事件, 一个指定事件 beforeChange

 

当可观察对象发生了变化, 就会执行订阅(subscribe)方法.

beforeChange , 即在改变值之前.

具体的如何将值传入到下一条记录, 在 RangeToChanged 方法中定义.

 

计算结果

参于计算的各数据项, 必须是可观察的. 其它无特殊说明.

 

MVC Action 参数

大量的表单项, 展现在页面上, 不是随便输入, 就可以提交的, 这就需要有验证规则.

MVC 结合 jQuery.Validate 对客户端的验证做的很完美, 基本不用写一句JS代码就可以处理绝大部分的数据校验. 这是因为 MVC输出HTML表单项的时候, 会把模型的 DataAnnoation 转化为 data-val-xxx .

 

想使用 KO, 又不想丢掉MVC提供的这个功能, 怎么办呢?

自定义 helper

 

@helper A(string prop) {
   @Html.TextBox(prop, null, new {data_bind = string.Format("value:{0},attr:{{name:‘routes[‘ + $parentContext.$index() + ‘].Ports[‘ + $index() + ‘].{0}‘}}", prop),@class = "form-control input-sm"})
}
 
@helper B(string prop) {
@Html.Hidden(prop, null, new {data_bind = string.Format("value:$index,attr:{{name:‘routes[‘ + $parentContext.$index() + ‘].Ports[‘ + $index() + ‘].{0}‘}}", prop)})
} @helper C(string prop) {
@Html.CheckBox(prop, false, new {data_bind = string.Format("checked:{0},attr:{{name:‘routes[‘ + $parentContext.$index() + ‘].Ports[‘ + $index() + ‘].{0}‘}}", prop)})
}

 

使用

<td>@A(Helper.GetPropertyName(Html, m => m.PortCode))</td>

<td>@C(Helper.GetPropertyName(Html, m => m.IsBack))</td>

 

 

这样就使 KO 和 MVC 无缝的结合在一起了.

5个KO实例带你装B带你飞

标签:

原文地址:http://www.cnblogs.com/xling/p/4244828.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!