如何向 Knockout 映射视图模型添加内容?
How to add stuff to Knockout mapping view model?
我是 Knockout 的新手,在使用 Knockout 映射插件时,我无法理解如何 "edit" 视图模型。希望有人能帮助我。我有一个列表列表。下面是一个类似的例子。基本上有多个文件的多个组。
[
{
"group": "Alice",
"files": [
{"filename": "red.mp3", "length": 5},
{"filename": "blue.mp3","length": 6},
{"filename": "yellow.mp3","length": 5}
]
},
{
"group": "Bob",
"files": [
{"filename": "green.mp3","length": 2},
{"filename": "purple.mp3","length": 10}
]
}
]
我可以从这里得到基本模型:
$.getJSON('api/get-list', function(data)
{
view = ko.mapping.fromJS(data);
ko.applyBindings(view);
});
它有效,我已经设法将它绑定在 HTML 中,所以它是可见的,并且在该区域一切正常。但是,我需要添加一些东西,但我不确定该怎么做。更重要的是,如何做到干净利落。
我正在输出带有复选框的文件,我想要一个 'select' 属性 绑定到它。我已经能够通过在后端添加该字段来做到这一点,但不希望这样做,因为它真的不应该在那里。还需要显示当前选择的数量、每组的数量和总数。
所以,基本上我想要这样的东西:
{
"formSubmit": ?,
"totalNumberOfFiles": ?,
"totalNumberOfSelectedFiles": ?,
"groups":
[
{
"group": "Alice",
"numberOfFiles": ?,
"selectedFiles": ?,
"files": [
{
"filename": "red.mp3",
"length": 5,
"selected": boolean
},
...
]
},
...
]
}
- 虽然,例如,
numberOfFiles
可能甚至不需要?可以从 files.length
或其他什么地方得到吗?
- 而且
selectedFiles
应该是一个 function/observable 来计算所选文件的数量(那看起来怎么样?)或者它应该是一个 to/removed 以某种方式添加的列表(要怎么做呢?)
- 我如何在提交功能中获取当前选定文件的列表,以便 post 将它们返回到服务器?
- 无论哪种方式,我如何 "enhance/enrich/wrap" 使用这些东西从服务器获得的基本数组,而不是太混乱?
基本上,我知道(可以弄清楚)如何在模型正常工作时进行绑定,但不了解如何在使用映射插件时以良好的方式构建它(我 真的不想手动做)。
希望有人能帮助我,因为我就是想不通
当您使用 ko.mapping.fromJS 时,每个 属性 都被转换为一个 observable,每个数组都被转换为一个 observableArray。
主视图模型 MyViewModel 有一个文件组列表,该列表使用自定义映射对象的映射进行初始化。此对象有一个 'create' 回调(如 http://knockoutjs.com/documentation/plugins-mapping.html 中所述),用于实例化一个新的文件组。
在 FileGroup 构造函数中,就在创建新的子视图模型之前,添加了 属性 'selected' false 是它的默认值。
此外,主视图模型有两个计算的可观测值:
- numberOfFiles: returns 每个文件组中的文件总数
- selectedFiles: returns 包含每个文件组中所有选定文件的数组
在 submit 方法中有一个简单的警告来演示如何访问所选文件的数组。
// data obtained from the server
var data = [
{
"group": "Alice",
"files": [
{ "filename": "red.mp3", "length": 5 },
{ "filename": "blue.mp3", "length": 6 },
{ "filename": "yellow.mp3", "length": 5 }
]
},
{
"group": "Bob",
"files": [
{ "filename": "green.mp3", "length": 2 },
{ "filename": "purple.mp3", "length": 10 }
]
}
];
// sub view model representing a single file grouping
var FileGroup = function (data) {
data.files.map(f => f.selected = false);
ko.mapping.fromJS(data, {}, this);
}
// main view model
var MyViewModel = function (data) {
this.fileGroups = ko.mapping.fromJS(data, { create: options => new FileGroup(options.data) });
this.numberOfFiles = ko.computed(() => {
return this.fileGroups().reduce((total, fg) => {
total += fg.files().length;
return total;
}, 0);
}, this);
this.selectedFiles = ko.computed(function() {
return this.fileGroups().reduce((selectedFiles, fg) => {
selectedFiles.push.apply(selectedFiles, fg.files().filter(f => f.selected()));
return selectedFiles;
}, [])
}, this);
this.submit = function() {
alert("FILES POSTED TO SERVER: " + this.selectedFiles().length);
}
}
var viewModel = new MyViewModel(data);
ko.applyBindings(viewModel);
.fileGroup {
border: 1px solid lightgray;
margin-bottom: 15px;
padding: 10px;
}
.selected {
border: 1px solid lightgreen;
margin-bottom: 15px;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js" type="text/javascript"></script>
<div data-bind="foreach: fileGroups">
<h3 data-bind="text: group"></h3>
<div data-bind="foreach: files" class="fileGroup">
<input type="checkbox" data-bind="checked: selected">
<span data-bind="text: filename" />
</div>
</div>
<h4>Number of Files: <span data-bind="text: numberOfFiles"></span></h4>
<div data-bind="foreach: selectedFiles, visible: selectedFiles().length > 0" class=selected>
<span data-bind="text: filename" />
</div>
<button data-bind="click: submit">Submit</button>
我是 Knockout 的新手,在使用 Knockout 映射插件时,我无法理解如何 "edit" 视图模型。希望有人能帮助我。我有一个列表列表。下面是一个类似的例子。基本上有多个文件的多个组。
[
{
"group": "Alice",
"files": [
{"filename": "red.mp3", "length": 5},
{"filename": "blue.mp3","length": 6},
{"filename": "yellow.mp3","length": 5}
]
},
{
"group": "Bob",
"files": [
{"filename": "green.mp3","length": 2},
{"filename": "purple.mp3","length": 10}
]
}
]
我可以从这里得到基本模型:
$.getJSON('api/get-list', function(data)
{
view = ko.mapping.fromJS(data);
ko.applyBindings(view);
});
它有效,我已经设法将它绑定在 HTML 中,所以它是可见的,并且在该区域一切正常。但是,我需要添加一些东西,但我不确定该怎么做。更重要的是,如何做到干净利落。
我正在输出带有复选框的文件,我想要一个 'select' 属性 绑定到它。我已经能够通过在后端添加该字段来做到这一点,但不希望这样做,因为它真的不应该在那里。还需要显示当前选择的数量、每组的数量和总数。
所以,基本上我想要这样的东西:
{
"formSubmit": ?,
"totalNumberOfFiles": ?,
"totalNumberOfSelectedFiles": ?,
"groups":
[
{
"group": "Alice",
"numberOfFiles": ?,
"selectedFiles": ?,
"files": [
{
"filename": "red.mp3",
"length": 5,
"selected": boolean
},
...
]
},
...
]
}
- 虽然,例如,
numberOfFiles
可能甚至不需要?可以从files.length
或其他什么地方得到吗? - 而且
selectedFiles
应该是一个 function/observable 来计算所选文件的数量(那看起来怎么样?)或者它应该是一个 to/removed 以某种方式添加的列表(要怎么做呢?) - 我如何在提交功能中获取当前选定文件的列表,以便 post 将它们返回到服务器?
- 无论哪种方式,我如何 "enhance/enrich/wrap" 使用这些东西从服务器获得的基本数组,而不是太混乱?
基本上,我知道(可以弄清楚)如何在模型正常工作时进行绑定,但不了解如何在使用映射插件时以良好的方式构建它(我 真的不想手动做)。
希望有人能帮助我,因为我就是想不通
当您使用 ko.mapping.fromJS 时,每个 属性 都被转换为一个 observable,每个数组都被转换为一个 observableArray。
主视图模型 MyViewModel 有一个文件组列表,该列表使用自定义映射对象的映射进行初始化。此对象有一个 'create' 回调(如 http://knockoutjs.com/documentation/plugins-mapping.html 中所述),用于实例化一个新的文件组。
在 FileGroup 构造函数中,就在创建新的子视图模型之前,添加了 属性 'selected' false 是它的默认值。
此外,主视图模型有两个计算的可观测值:
- numberOfFiles: returns 每个文件组中的文件总数
- selectedFiles: returns 包含每个文件组中所有选定文件的数组
在 submit 方法中有一个简单的警告来演示如何访问所选文件的数组。
// data obtained from the server
var data = [
{
"group": "Alice",
"files": [
{ "filename": "red.mp3", "length": 5 },
{ "filename": "blue.mp3", "length": 6 },
{ "filename": "yellow.mp3", "length": 5 }
]
},
{
"group": "Bob",
"files": [
{ "filename": "green.mp3", "length": 2 },
{ "filename": "purple.mp3", "length": 10 }
]
}
];
// sub view model representing a single file grouping
var FileGroup = function (data) {
data.files.map(f => f.selected = false);
ko.mapping.fromJS(data, {}, this);
}
// main view model
var MyViewModel = function (data) {
this.fileGroups = ko.mapping.fromJS(data, { create: options => new FileGroup(options.data) });
this.numberOfFiles = ko.computed(() => {
return this.fileGroups().reduce((total, fg) => {
total += fg.files().length;
return total;
}, 0);
}, this);
this.selectedFiles = ko.computed(function() {
return this.fileGroups().reduce((selectedFiles, fg) => {
selectedFiles.push.apply(selectedFiles, fg.files().filter(f => f.selected()));
return selectedFiles;
}, [])
}, this);
this.submit = function() {
alert("FILES POSTED TO SERVER: " + this.selectedFiles().length);
}
}
var viewModel = new MyViewModel(data);
ko.applyBindings(viewModel);
.fileGroup {
border: 1px solid lightgray;
margin-bottom: 15px;
padding: 10px;
}
.selected {
border: 1px solid lightgreen;
margin-bottom: 15px;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js" type="text/javascript"></script>
<div data-bind="foreach: fileGroups">
<h3 data-bind="text: group"></h3>
<div data-bind="foreach: files" class="fileGroup">
<input type="checkbox" data-bind="checked: selected">
<span data-bind="text: filename" />
</div>
</div>
<h4>Number of Files: <span data-bind="text: numberOfFiles"></span></h4>
<div data-bind="foreach: selectedFiles, visible: selectedFiles().length > 0" class=selected>
<span data-bind="text: filename" />
</div>
<button data-bind="click: submit">Submit</button>