控制流
foreach
遍历
<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>
<script type="text/javascript">
ko.applyBindings({
people: [
{ firstName: 'Bert', lastName: 'Bertington' },
{ firstName: 'Charles', lastName: 'Charlesforth' },
{ firstName: 'Denise', lastName: 'Dentiste' }
]
});
</script>
带添加删除的例子
<h4>People</h4>
<ul data-bind="foreach: people">
<li>
Name 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 type="text/javascript">
function AppViewModel() {
var self = this;
self.people = ko.observableArray([
{ name: 'Bert' },
{ name: 'Charles' },
{ name: 'Denise' }
]);
self.addPerson = function() {
self.people.push({ name: "New at " + new Date() });
};
self.removePerson = function() {
self.people.remove(this);
}
}
ko.applyBindings(new AppViewModel());
</script>
$data
$date代表了遍历内容
<ul data-bind="foreach: months">
<li>
The current item is: <b data-bind="text: $data"></b>
</li>
</ul>
<script type="text/javascript">
ko.applyBindings({
months: [ 'Jan', 'Feb', 'Mar', 'etc' ]
});
</script>
需要访问遍历内容的具体属性,不需要引用$date前缀,因为默认就是当前$date
其他上下文属性
$index
$parent
使用别名
<ul data-bind="foreach: { data: people, as: 'person' }"></ul>
<ul data-bind="foreach: { data: categories, as: 'category' }">
<li>
<ul data-bind="foreach: { data: items, as: 'item' }">
<li>
<span data-bind="text: category.name"></span>:
<span data-bind="text: item"></span>
</li>
</ul>
</li>
</ul>
<script>
var viewModel = {
categories: ko.observableArray([
{ name: 'Fruit', items: [ 'Apple', 'Orange', 'Banana' ] },
{ name: 'Vegetables', items: [ 'Celery', 'Corn', 'Spinach' ] }
])
};
ko.applyBindings(viewModel);
</script>
使用container元素
下面例子中,没有地方放置foreach
<ul>
<li class="header">Header item</li>
<!-- The following are generated dynamically from an array -->
<li>Item A</li>
<li>Item B</li>
<li>Item C</li>
</ul>
使用这种方式,能够使用无容器的控制流语法
<ul>
<li class="header">Header item</li>
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<!-- /ko -->
</ul>
<script type="text/javascript">
ko.applyBindings({
myItems: [ 'A', 'B', 'C' ]
});
</script>
array变化
当修改了model array内容,比如add,mo,delete
foreach可以更新内容
销毁的entries默认隐藏
如果想展示销毁的entriies,可以设置
<div data-bind='foreach: { data: myArray, includeDestroyed: true }'>
...
</div>
预处理或者动画
如果想在DOM上定制更多的逻辑,可以使用
afterRender/afterAdd/beforeRemove/beforeMove/afterMove
<ul data-bind="foreach: { data: myItems, afterAdd: yellowFadeIn }">
<li data-bind="text: $data"></li>
</ul>
<button data-bind="click: addItem">Add</button>
<script type="text/javascript">
ko.applyBindings({
myItems: ko.observableArray([ 'A', 'B', 'C' ]),
yellowFadeIn: function(element, index, data) {
$(element).filter("li")
.animate({ backgroundColor: 'yellow' }, 200)
.animate({ backgroundColor: 'white' }, 800);
},
addItem: function() { this.myItems.push('New item'); }
});
</script>
if
如果勾选,会显示内容
<label><input type="checkbox" data-bind="checked: displayMessage" /> Display message</label>\
<div data-bind="if: displayMessage">Here is a message. Astonishing.</div>
ko.applyBindings({
displayMessage: ko.observable(false)
});
例子中Mercury的catital不显示,因为是Null
<ul data-bind="foreach: planets">
<li>
Planet: <b data-bind="text: name"> </b>
<div data-bind="if: capital">
Capital: <b data-bind="text: capital.cityName"> </b>
</div>
</li>
</ul>
<script>
ko.applyBindings({
planets: [
{ name: 'Mercury', capital: null },
{ name: 'Earth', capital: { cityName: 'Barnsley' } }
]
});
</script>
没有容器元素时使用if
<ul>
<li>This item always appears</li>
<!-- ko if: someExpressionGoesHere -->
<li>I want to make this item present/absent dynamically</li>
<!-- /ko -->
</ul>
ifnot
<div data-bind="ifnot: someProperty">...</div>
<div data-bind="if: !someProperty()">...</div>
with
例子1:子元素能获得绑定里面的内容
<h1 data-bind="text: city"> </h1>
<p data-bind="with: coords">
Latitude: <span data-bind="text: latitude"> </span>,
Longitude: <span data-bind="text: longitude"> </span>
</p>
<script type="text/javascript">
ko.applyBindings({
city: "London",
coords: {
latitude: 51.5001524,
longitude: -0.1262362
}
});
</script>
例子2:
<form data-bind="submit: getTweets">
Twitter account:
<input data-bind="value: twitterName" />
<button type="submit">Get tweets</button>
</form>
<div data-bind="with: resultData">
<h3>Recent tweets fetched at <span data-bind="text: retrievalDate"> </span></h3>
<ol data-bind="foreach: topTweets">
<li data-bind="text: text"></li>
</ol>
<button data-bind="click: $parent.clearResults">Clear tweets</button>
</div>
<script type="text/javascript">
function AppViewModel() {
var self = this;
self.twitterName = ko.observable('@example');
self.resultData = ko.observable(); // No initial value
self.getTweets = function() {
var name = self.twitterName(),
simulatedResults = [
{ text: name + ' What a nice day.' },
{ text: name + ' Building some cool apps.' },
{ text: name + ' Just saw a famous celebrity eating lard. Yum.' }
];
self.resultData({ retrievalDate: new Date(), topTweets: simulatedResults });
}
self.clearResults = function() {
self.resultData(undefined);
}
}
ko.applyBindings(new AppViewModel());
</script>
with会动态的增删元素
如果想访问父元素,推荐使用$parent/$root
不通过容器使用
<ul>
<li>Header element</li>
<!-- ko with: outboundFlight -->
...
<!-- /ko -->
<!-- ko with: inboundFlight -->
...
<!-- /ko -->
</ul>
component
参考:
http://knockoutjs.com/documentation/component-registration.html
http://knockoutjs.com/documentation/component-overview.html#example-loading-the-likedislike-widget-from-external-files-on-demand
component绑定把component注入到element,并能传入参数
<h4>First instance, without parameters</h4>
<div data-bind='component: "message-editor"'></div>
<h4>Second instance, passing parameters</h4>
<div data-bind='component: {
name: "message-editor",
params: { initialText: "Hello, world!" }
}'></div>
<script type="text/javascript">
ko.components.register('message-editor', {
viewModel: function(params) {
this.text = ko.observable(params && params.initialText || '');
},
template: 'Message: <input data-bind="value: text" /> '
+ '(length: <span data-bind="text: text().length"></span>)'
});
ko.applyBindings();
</script>
API
如果只传入一个string,就是传入了一个空参的compoment
<div data-bind='component: "my-component"'></div>
完整的语法
<div data-bind='component: {
name: "shopping-cart",
params: { mode: "detailed-list", items: productsList }
}'></div>
name 要注入的component名字,可以被observable
params 被传给component的对象
Component生命周期
1. component加载器提供viewmodel工厂和模板,默认的加载器缓存了注册的compoment
2. component模板被复制,注册到element
3. 如果component有viewmodel会被实例化,如果给构造函数会调用new YourViewModel(params)
createViewModel(params, componentInfo)
4. viewmodel被绑定到view上,如果没有viewmode,会绑定params
5. component激活
6. 元素被撕裂,viewmodel被处理
Components有viewmodel,但是也可以只有一个模板
注册
ko.components.register('special-offer', {
template: '<div class="offer-box" data-bind="text: productName"></div>'
});
注入
<div data-bind='component: {
name: "special-offer-callout",
params: { productName: someProduct.name }
}'></div>
或者更方便的
<special-offer params='productName: someProduct.name'></special-offer>
不使用容器
<!-- ko component: "message-editor" -->
<!-- /ko -->
或者
<!-- ko component: {
name: "message-editor",
params: { initialText: "Hello, world!", otherParam: 123 }
} -->
<!-- /ko -->
向components发送标签
<div data-bind="component: { name: 'my-special-list', params: { items: someArrayOfPeople } }">
<!-- Look, here's some arbitrary markup. By default it gets stripped out
and is replaced by the component output. -->
The person <em data-bind="text: name"></em>
is <em data-bind="text: age"></em> years old.
</div>
处理和内存管理
vm类有个dispose函数
比如
var someExternalObservable = ko.observable(123);
function SomeComponentViewModel() {
this.myComputed = ko.computed(function() {
return someExternalObservable() + 1;
}, this);
this.myPureComputed = ko.pureComputed(function() {
return someExternalObservable() + 2;
}, this);
this.mySubscription = someExternalObservable.subscribe(function(val) {
console.log('The external observable changed to ' + val);
}, this);
this.myIntervalHandle = window.setInterval(function() {
console.log('Another second passed, and the component is still alive.');
}, 1000);
}
SomeComponentViewModel.prototype.dispose = function() {
this.myComputed.dispose();
this.mySubscription.dispose();
window.clearInterval(this.myIntervalHandle);
// this.myPureComputed doesn't need to be manually disposed.
}
ko.components.register('your-component-name', {
viewModel: SomeComponentViewModel,
template: 'some template'
});