knockout.js实例一~联系人管理器
本次实例是基于knockout.js来完成对联系人的管理(增,删),不多说直接先上代码:
ViewModel
//[1]
var initialData = [
{ firstName: "Danny", lastName: "LaRusso", phones: [
{ type: "Mobile", number: "(555) 121-2121" },
{ type: "Home", number: "(555) 123-4567"}]
},
{ firstName: "Sensei", lastName: "Miyagi", phones: [
{ type: "Mobile", number: "(555) 444-2222" },
{ type: "Home", number: "(555) 999-1212"}]
}
];
function contactsModel(){
var self = this;
//[2]
self.contacts = ko.observableArray(ko.utils.arrayMap(initialData,
function (contact) {
return {
firstName: ko.observable(contact.firstName),
lastName: ko.observable(contact.lastName),
phones: ko.observableArray(ko.utils.arrayMap(contact.phones,
function (phone) {
return {
type: ko.observable(phone.type),
number:ko.observable(phone.number)
};
})
)
};
}
));
//[3]
self.addContact = function () {
self.contacts.push({
firstName: ko.observable(),
lastName: ko.observable(),
phones: ko.observableArray()
});
};
//[4]
self.removeContact = function (contact) {
self.contacts.remove(contact);
};
//[5]
self.addPhone = function (contact) {
contact.phones.push({
type:ko.observable(),
number:ko.observable()
});
};
//[6]
self.removePhone = function (phone) {
$.each(self.contacts() , function () {
this.phones.remove(phone);
});
};
//[7]
self.selfJsonData = ko.computed(
function () {
return JSON.stringify(ko.toJS(self.contacts()), null, 2);
},
this
);
}
ko.applyBindings(new contactsModel());
[1] initialData :
此为我们初始化数据,使用的是静态数据,如果后面作深入的话,此处可以使用ajax访问api获取json数据
[2] contacts :
contacts会作为一个可监控数组,保存我们的联系人数据,在这里我们将初始化数据进行绑定,由于我们需要监控数据的变化,所以我们使用了ko.observable()来对firstName以及lastName进行监控,
对于联系人电话phones,由于这是一个数组,所以,我们需要使用ko.observableArray()来进行监控。另外,我们注意到ko.utils.arrayMap()这个函数,这个函数能起到什么作用呢,让我们来看看这个函数的签名:
ko.utils.arrayMap(array, mapping)
它的作用就是将数组的每一项进行遍历,根据自己的mapping函数进行处理来返回新的一项,从而返回成新的数组,在上面的代码中,我们对初始化数据的每一项进行遍历执行以下函数:
function (contact) {
return {
firstName: ko.observable(contact.firstName),
lastName: ko.observable(contact.lastName),
phones: ko.observableArray(ko.utils.arrayMap(contact.phones,
function (phone) {
return {
type: ko.observable(phone.type),
number:ko.observable(phone.number)
};
})
)
};
}
我们可以看出,这里每一项都在mapping中被我们进行了监控,对每一项的每一个字段都做了控制,简而言之,arryMap这个函数我们可以看作为一个格式化处理器。在这里,我们并没有定义基本模型,下面我们将上面的代码进行稍作改变:
我们新增两个模型:phone与contact
function phone(type, number){
this.type = ko.observable(type);
this.number = ko.observable(number);
}
function contact(firstName, lastName, phones){
this.firstName = ko.observable(firstName);
this.lastName = ko.observable(lastName);
this.phones = ko.observableArray(phones);
}
然后将上面对于contacts的监控控制代码改为:
self.contacts = ko.observableArray(ko.utils.arrayMap(initialData,
function (c) {
return new contact(c.firstName, c.lastName, ko.utils.arrayMap(c.phones,
function (p) {
return new phone(p.type, p.number);
}
));
}
));
现在看来,对于后面即使初始化数据是从服务器上传递的,我们也可以轻易的进行格式化控制并且对其进行监控了。
[3] addContact:
添加联系人函数,由于我们队self.contacts进行了监控,那么我们在添加新联系人的时候,就只需要对self.contacts进行操作即可,这里我们就直接向其压入一个空的联系人数据即可
[4] removeContact:
删除联系人函数,理由同[3], 我们只需要remove当前的contact即可,注意一点就是我们的移除按钮中绑定了click:removeContact之后,在时间触发之后,函数会接收两个参数,一个为args,一个为sender,
args便是我们的当前轮询的contact对象
[5] addPhone:
添加联系人电话,与addContact近似,这里得到当前操作的contact对象,然后通过contact.phones获取电话号码数组,往其中压入一个新的控制项就可以了
[6] removePhone:
此处函数接收的是一个phone对象,如何移除这里的phone对象呢,这里我们需要用到jquery的$.each()函数, 来对self.contacts()进行遍历, 如果某contact的phones中存在phone,便会移除此项
[7] selfJsonData:
这是一个受依赖的监控属性,它的值取决于self.contacts, 所以当self.contacts变化的时候, selfJsonData也需要随之而改变,所以这里我们用到了 ko.computed(func, target)函数, 通过回调函数,
我们返回 self.contacts的json数据, 而target对象则为 this(self)
下面贴出我们的html代码
HTML Code
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Contacts Editor</title>
<link rel="stylesheet" href="../css/bootstrap.min.css"/>
</head>
<body>
<script type="text/html" id="contactListTemplate">
<tr>
<td>
<input type="text" data-bind="value: firstName"/>
</td>
<td>
<input type="text" data-bind="value: lastName"/>
</td>
<td colspan="3">
<table class="table table-responsive table-bordered">
<tbody data-bind="foreach: phones">
<tr>
<td class="col-md-4">
<input type="text" data-bind="value: type"/>
</td>
<td class="col-md-4">
<input type="text" data-bind="value: number"/>
</td>
<td class="col-md-4">
<a href="#" class="btn btn-danger btn-sm" data-bind="click: $root.removePhone">Remove Phone</a>
</td>
</tr>
</tbody>
<tr>
<td colspan="3">
<a href="#" class="btn btn-primary btn-sm" data-bind="click: $root.addPhone">Add Phone</a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td colspan="5">
<a href="#" class="btn btn-danger btn-sm" data-bind="click: $root.removeContact">Remove Contact</a>
</td>
</tr>
</script>
<div class="container">
<div class="row">
<hr/>
<a href="#" data-bind="click: addContact" class="btn btn-primary btn-sm">Add Contact</a>
<textarea name="contactsJsonData" id="contactsJsonData" class="col-md-12" rows="10" data-bind="text: selfJsonData"></textarea>
<table class="table table-responsive table-bordered">
<thead>
<tr>
<th class="col-md-3">FirstName</th>
<th class="col-md-3">LastName</th>
<th class="col-md-2">PhoneType</th>
<th class="col-md-2">Number</th>
<th class="col-md-2"></th>
</tr>
</thead>
<tbody data-bind="template: {name:'contactListTemplate', foreach: contacts}">
</tbody>
</table>
</div>
</div>
<script type="text/javascript" src="../js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="../js/knockout-3.3.0.js"></script>
<script type="text/javascript" src="../viewModels/contactsEditor.js"></script>
</body>
</html>
总结
虽然本例较为简单,但是却是用到了knockout.js的很多知识,如:模板绑定,模板嵌套,监控属性,依赖监控属性…也是一次较为全面的知识运用,当然,此例子还可以继续深入,比如通过api来获取服务器数据,以及将数据进入入库保存。