淘汰组件更新未被父视图模型订阅的可观察对象

Knockout component updating observable not being subscribed to by parent view model?

本文关键字:对象 观察 模型 视图 更新 组件 淘汰      更新时间:2023-09-26

我编写了一个名为Upload的组件,它允许用户上传文件,然后使用这些文件的JSON对象报告。在这个例子中,Upload组件有一个来自父视图模型的参数:

<upload params="dropzoneId: 'uploadFilesDropzone', postLocation: '/create/upload', uploadedFiles: uploadedFiles"></upload>

最重要的是uploadedFiles。这里的参数绑定意味着我可以在组件上引用params.uploadedFiles,并在上传.push()新对象时引用它们。传递的数据,也称为uploadedFiles,是父视图模型上的observableArray:

var UploadViewModel = function () {
     // Files ready to be submitted to the queue.
     self.uploadedFiles = ko.observableArray([]);
};

我确实可以确认在我的组件上,params.uploadedFiles是一个observableArray,因为它有一个push方法。在组件上修改了这个值之后,我可以console.log()它看到它实际上已经改变了:

params.uploadedFiles.push(object);
console.log(params.uploadedFiles().length); // was 0, now returns 1

问题是这个变化似乎没有反映在我的父视图模型上。self.uploadedFiles()不改变,仍然报告长度为0。

无论我是否在父视图模型中添加self.uploadedFiles.subscribe(function(newValue) {});订阅

无论我是否在我的组件中添加了params.uploadedFiles.valueHasMutated()方法

我如何从我的组件数组的变化反映在我的父视图模型上的数组?

当源已经是一个时,为什么要创建一个新的可观察数组?您不能期望新对象具有与另一个对象相同的引用:只需将其传递给组件viewModel作为this.uploads = params.uploads。在下面的示例精简版本中,您将看到单击Add按钮时,两个数组(在不同上下文中引用的同一个数组)保持同步。

ko.components.register('upload', {
  viewModel: function(params) {
    this.uploads = params.uploads;
    this.addUpload = function() { this.uploads.push('item'); }.bind(this);
  },
  template: [
    '<div><button type="button" data-bind="click: addUpload">Add upload</button>',
    '<span data-bind="text: uploads().length + '' - '' + $root.uploads().length"></span></div>'].join('')
  
});
var app = {
  uploads: ko.observableArray([])
};
ko.applyBindings(app);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="component: {name: 'upload', params: {uploads: uploads}}"></div>

只有在你的源数组不可观察的情况下,事情才会变得更复杂,你需要手动订阅来更新源,例如。您将在viewModel中插入以下内容:

this.uploads.subscribe(function(newValue) { params.uploads = newValue; });

另外,text绑定中的输出不会为源更新,因为它不是可观察的。如果出于某种我无法想象的原因你想要有两个不同的可观测射线(一个源)1组件),您应该仍然能够使用上面的行,但将功能代码替换为params.uploads(newValue)

问题可能与此bug有关(有待确认):https://github.com/knockout/knockout/issues/1863

编辑1:所以这不是一个bug。你必须打开原始参数才能访问原始的可观察对象。在您的例子中,它将是:
params.$raw.uploadedFiles() //this would give you access to the original observableArray and from there, you can "push", "remove", etc.

问题是,当你传递一个参数给组件时,它被包装在一个计算的可观察对象中,当你打开它时,你没有原始的observableArray。

参考:http://knockoutjs.com/documentation/component-custom-elements.html advanced-accessing-raw-parameters

While绑定涉及父->子关系的属性以这种方式使用绑定

如果要将数据绑定到子属性data-bind='BindingName: ParentViewmodel.ChildViewModel.ObservableProperty'

这里似乎你想订阅一个函数当任何数据被推入数组时,你可以在可观察数组的长度上写订阅,这可以帮助你捕获你想要的事件。