【问题标题】:Knockout component updating observable not being subscribed to by parent view model?父视图模型未订阅淘汰组件更新可观察对象?
【发布时间】:2015-06-08 09:46:40
【问题描述】:

我编写了一个名为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() 方法添加到我的组件中。

如何让组件上的数组中的更改反映在父视图模型上的数组中?

【问题讨论】:

  • 您能否发布一个 JSFiddle 或完整的代码示例?我做过类似的事情并且没有遇到问题,但是我无法从您迄今为止发布的代码中找出问题。

标签: javascript knockout.js publish-subscribe


【解决方案1】:

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

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 绑定中的输出不会针对源进行更新,因为它是不可观察的。如果出于某种我无法想象的原因,您想要拥有 2 个不同的 observableArrays(1 个源和 1 个组件),您应该仍然可以使用上面的行,但将函数代码替换为 params.uploads(newValue)

【讨论】:

  • 当源已经是一个时,为什么要创建一个新的可观察数组?对不起,我觉得你有点糊涂了。我不这样做; UploadViewModel 我的父视图模型,而不是组件视图模型。
  • @EchoLogic 呃,这些名字很有欺骗性。尽管如此,当您将app 视为UploadViewModel 的实例时,我的答案中的代码仍然有效。因此,如您所见,如果您遵循相同的模式,一切都应该可以正常工作。但是report back with a JSON object with these files 告诉我你已经实现了自定义逻辑来处理更改。为什么不把你所有的难题都给我们呢?
【解决方案2】:

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

编辑 1:所以这不是错误。您必须解开原始参数才能访问原始的 observable。在您的情况下,它将是:

params.$raw.uploadedFiles() //this would give you access to the original observableArray and from there, you can "push", "remove", etc.

问题在于,当您将参数传递给组件时,它会被包装在计算出的 observable 中,而当您打开它时,您没有原始的 observableArray。

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

【讨论】:

    【解决方案3】:

    While Binding 属性涉及到 Parent --> Child Relation 这样使用Binding

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

    在这里,当任何数据被推送到 Array 中时,您似乎想要订阅一个函数,您可以编写 subscribe on Length of Observable 数组,这可以帮助您捕获所需的事件。

    这应该可以解决您的问题。

    【讨论】:

      猜你喜欢
      • 2012-07-25
      • 2016-12-12
      • 2011-09-17
      • 2013-04-24
      • 2015-08-29
      • 2015-01-25
      • 2014-04-12
      • 2013-05-30
      • 1970-01-01
      相关资源
      最近更新 更多