组件需要被复用才会有意义,所以组件HTML模板应当独立在外部文件中,以下是组件HTML模板
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="template" data-bind="attr:{'data-outer-index':outerIndex()}">
<!-- ko foreach:{data:list(),as:'o'} -->
<div>
<span data-bind="text:'内 - ' + o.name()"></span>
<span data-bind="click:$root.methods.remove.bind($parent.outerIndex(),$index())">remove</span>
<span data-bind="click:$root.methods.selected,text:o.selected() == 0?'selected':'done'">selected</span>
</div>
<!-- /ko -->
</div>
<script>
// see components.js
/**
* 使用外部模板的方法:
* 1.通过webpack打包html模板
* 2.通过require.js + text.js加载模板文件
* 3.通过以下代码将html模板转换js字符串并引用js文件
*/
/**
* 本组件说明
* 输入格式{list:[{name:'',selected:0}],outerIndex}
* 需要父组件提供$root.methods.remove/selected
*/
var template = document.getElementById('template').outerHTML.replace(/\'/ig,'\\\'').replace(/\s*\n\s*/ig,'')
</script>
</body>
</html>
组件HTML模板还需要导入到其他文件内部,此处使用的方法可能比较low,就是将组件HTML转JS字符串,其他文件导入JS文件的方式,但是是可行的
// see components.html
var COMP_MY_COMPONENT_HTML = '<div id="template" data-bind="attr:{\'data-outer-index\':outerIndex()}"><!-- ko foreach:{data:list(),as:\'o\'} --><div><span data-bind="text:\'内 - \' + o.name()"></span> <span data-bind="click:$root.methods.remove.bind($parent.outerIndex(),$index())">remove</span> <span data-bind="click:$root.methods.selected,text:o.selected() == 0?\'selected\':\'done\'">selected</span> </div><!-- /ko --></div>'
最后在使用该组件的文件中编写ViewModel以及业务逻辑
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ko组件</title>
</head>
<body>
<div>
ko组件
</div>
<br>
<div data-bind="text:outerName"></div>
<br>
<div data-bind="foreach:{data:outerList(),as:'ol'}">
<div data-bind="text:'外部迭代' + ol"></div>
<div data-bind="component:{name:'my-component',params:{outerIndex:$index(),list:ol}}"></div>
<br>
</div>
<script type="text/javascript" src="knockout-3.4.2.js"></script>
<script type="text/javascript" src="knockout.mapping.min.js"></script>
<script type="text/javascript" src="components.js"></script>
<script>
/**
* 参考:http://www.cnblogs.com/smallprogram/p/5976954.html
*
* 1.父子组件如何通信
* 子组件的数据是通过父组件输入的,因此子组件需要响应用户操作时,也应当通过更新父组件数据后,再通知子组件更新
*
* 2.子组件上下文
* $root $parent $element等
* 估计就是父组件的上下文
*/
ko.components.register('my-component', {
viewModel: {
createViewModel: function(p, c) {
function SubViewModel(p){
var self = this
var pJS = ko.mapping.toJS(p)
self.outerIndex = ko.observable(p.outerIndex)
self.list = ko.observableArray(p.list)
}
return new SubViewModel(p)
}
},
template: COMP_MY_COMPONENT_HTML
})
var outerData = {
outerName: '外部名称',
outerList: [
[{
name: 'a',
selected: 0
}, {
name: 'b',
selected: 0
}, {
name: 'c',
selected: 0
}],
[{
name: 'd',
selected: 0
}, {
name: 'e',
selected: 0
}]
]
}
var OuterVm = function () {
var self = this
self.methods = {
remove: function(innerIndex) {
console.log(this)
console.log(arguments)
return
// 是可以通信的,虽然具体功能实现还有点问题
var outerIndex = this
var outerList = ko.mapping.toJS(self.outerList)
if (innerIndex != -1){
outerList[innerIndex].splice(innerIndex,1)
ko.mapping.fromJS({outerList:outerList},{},self)
} else {
console.log('not found target')
}
},
selected: function(){
console.log(this)
console.log(arguments)
arguments[0].selected(1)
}
}
}
var outerVm = new OuterVm()
ko.mapping.fromJS(outerData, {}, outerVm)
ko.applyBindings(outerVm, document.body)
</script>
</body>
</html>