如何从主 ViewModel 更新组件内的可观察数组?
How to update an observable array inside a component from the main ViewModel?
我需要从主视图模型中设置一个 属性 在可观察数组中。
我正在使用经典的 pre
来调试和显示我的可观察数组的内容。
通过使用 types.valueHasMutated()
我可以看到应用的更改 - 仅适用于 vm.types
(否则情况不会如此)。
但是,我需要看到这些更改反映在我的组件中。
在我的示例中,当我单击 "Apples" 时,相应的输入将被禁用,如下所示。可悲的是,事实并非如此。
我做错了什么?
ko.components.register("available-items", {
viewModel: function(params) {
function AvailableItems(params) {
var self = this;
self.params = params;
self.location = "A";
self.types = ko.computed(function() {
var types = self.params.types();
return ko.utils.arrayFilter(types, function(item) {
return item.location == self.location;
});
});
self.addItem = function(data, event) {
self.params.items.addItem(self.location, data.type);
};
}
return new AvailableItems(params);
},
template: '<div>' +
'<h4>Add item</h4>' +
'<ul data-bind="foreach: types">' +
'<li>' +
'<input type="text" data-bind="value: type, enable:available, event: {click: $parent.addItem}" readonly/>' +
'</li>' +
'</ul>' +
'</div>',
synchronous: true
});
var types = [{
type: "Apples",
location: "A",
available: true
}, {
type: "Bananas",
location: "A",
available: false
}];
function Vm(data) {
var self = this;
self.items = ko.observableArray();
self.types = ko.observableArray(ko.utils.arrayMap(data, function(item) {
return item;
}));
self.items.addItem = function(location, type) {
self.items.push({
location: location,
type: type
});
if (location == "A" && type == "Apples") {
self.types()[0].available = false;
self.types.valueHasMutated();
}
};
}
ko.options.deferUpdates = true;
var vm = new Vm(types);
ko.applyBindings(vm);
pre {
position: absolute;
width: 300px;
right: 0;
top: 0;
}
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
</head>
<body>
<div data-bind="component:{name:'available-items',params:vm}"></div>
<ul data-bind="foreach: items">
<li><span data-bind="text: location"></span> - <span data-bind="text: type"></span></li>
</ul>
<pre data-bind="text: ko.toJSON(vm.types, null, 2)"></pre>
</body>
</html>
我找到问题了。它在您的 html 中:"params: vm" 应该是 "params: $data"
<div data-bind="component:{name:'available-items',params:$data}"></div>
我在 jfiddle 上有 运行 这个,即使我添加了一个新类型,我也没有得到任何更新。
似乎有问题
'<ul data-bind="foreach: types">' +
我改成了
'<ul data-bind="foreach: $root.types">' +
https://jsfiddle.net/fabwoofer/9szbqhj7/1/
现在添加了类型,但似乎未处理第一项的重新呈现。有类似问题的人建议使用此处描述的模板渲染
Knockout.js Templates Foreach - force complete re-render
希望对您有所帮助
您的 可用 属性 无法观察到。为了通知 Knockout 有关更改并让它更新 UI - 使此 属性 可观察。
ko.components.register("available-items", {
viewModel: function(params) {
function AvailableItems(params) {
var self = this;
self.params = params;
self.location = "A";
self.types = ko.computed(function() {
var types = self.params.types();
return ko.utils.arrayFilter(types, function(item) {
return item.location == self.location;
});
});
self.addItem = function(data, event) {
self.params.items.addItem(self.location, data.type);
};
}
return new AvailableItems(params);
},
template: '<div>' +
'<h4>Add item</h4>' +
'<ul data-bind="foreach: types">' +
'<li>' +
'<input type="text" data-bind="value: type, enable:available, event: {click: $parent.addItem}" readonly/>' +
'</li>' +
'</ul>' +
'</div>',
synchronous: true
});
var types = [{
type: "Apples",
location: "A",
// Make property observable
available: ko.observable(true)
}, {
type: "Bananas",
location: "A",
// Make property observable
available: ko.observable(false)
}];
function Vm(data) {
var self = this;
self.items = ko.observableArray();
self.types = ko.observableArray(ko.utils.arrayMap(data, function(item) {
return item;
}));
self.items.addItem = function(location, type) {
self.items.push({
location: location,
type: type,
available: ko.observable(false)
});
if (location == "A" && type == "Apples") {
// Update property as observable.
self.types()[0].available(false);
self.types.valueHasMutated();
}
};
}
ko.options.deferUpdates = true;
var vm = new Vm(types);
ko.applyBindings(vm);
pre {
position: absolute;
width: 300px;
right: 0;
top: 0;
}
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
</head>
<body>
<div data-bind="component:{name:'available-items',params:vm}"></div>
<ul data-bind="foreach: items">
<li><span data-bind="text: location"></span> - <span data-bind="text: type"></span></li>
</ul>
<pre data-bind="text: ko.toJSON(vm.types, null, 2)"></pre>
</body>
</html>
您可以使用用户 JotaBe 提供的解决方案:Refresh observableArray when items are not observables.
ko.observableArray.fn.refresh = function (item) {
var index = this['indexOf'](item);
if (index >= 0) {
this.splice(index, 1);
this.splice(index, 0, item);
}
}
现在,我需要更改 addItem()
并使用更新后的元素添加对 refresh
的调用:
self.items.addItem = function(location, type) {
self.items.push({
location: location,
type: type
});
if (location == "A" && type == "Apples") {
self.types()[0].available = false;
self.types.refresh(self.types()[0]); // <--- New sentence
}
};
这将刷新具有 types
列表的 <pre>
。但是不会刷新组件,它还有一个 types
.
的列表
然后我用了这个link,Forcing deferred notifications to happen early,我在refresh
里面加了ko.tasks.runEarly()
,我觉得现在可以用了
ko.observableArray.fn.refresh = function (item) {
var index = this['indexOf'](item);
if (index >= 0) {
this.splice(index, 1);
ko.tasks.runEarly(); // <--- New sentence
this.splice(index, 0, item);
}
}
这是一个Codepen.
受 pauseableComputed and observable withPausing 实施的启发,我创建了 pauseableObservable 和 pauseableObservableArray,它们能够停止向订阅者发送通知,并且比在需要时恢复。它还对所有嵌套的可暂停属性递归工作。
您可以使用它 HERE on Codepen(已根据您问题中的代码提供了示例)。
我还放置了达到目标的扩展代码:
PauseableObservable:
// PauseableObservable - it's observable that have functions to 'pause' and 'resume' notifications to subscribers (pause/resume work recursive for all pauseable child).
ko.isPauseableObservable = function(instance) {
return ko.isObservable(instance) && instance.hasOwnProperty("pause");
}
ko.pauseableObservable = function(value) {
var that = ko.observable(value);
function getPauseableChildren() {
var properties = Object.getOwnPropertyNames(that());
var currentValue = that();
var pauseables = properties.filter((property) => {
return ko.isPauseableObservable(currentValue[property]);
});
return pauseables.map((property) => {
return currentValue[property];
});
}
that.pauseNotifications = false;
that.isDirty = false;
that.notifySubscribers = function() {
if (!that.pauseNotifications) {
ko.subscribable.fn.notifySubscribers.apply(that, arguments);
that.isDirty = false;
} else {
that.isDirty = true;
}
};
that.pause = function() {
that.pauseNotifications = true;
var pauseableChildren = getPauseableChildren();
pauseableChildren.forEach((child) => { child.pause(); });
}
that.resume = function() {
that.pauseNotifications = false;
if (that.isDirty) {
that.valueHasMutated();
}
var pauseableChildren = getPauseableChildren();
pauseableChildren.forEach((child)=> { child.resume(); });
}
return that;
}
PauseableObservableArray
// PauseableObservableArray - simply use pauseable functionality of his items.
// Extension for stop notifications about added/removed items is out of scope.
ko.pauseableObservableArray = function(items) {
var that = ko.observableArray(items);
that.pause = function () {
var items = that();
items.forEach(function(item) {
if(ko.isPauseableObservable(item)) {
item.pause();
}
});
}
that.resume = function () {
var items = that();
items.forEach(function(item) {
if(ko.isPauseableObservable(item)) {
item.resume();
}
});
}
that.refresh = function () {
that.resume();
that.pause();
}
return that;
}
用法示例:
var firstItem = ko.pauseableObservable("Hello");
var secondItem = ko.pauseableObservable("World");
var items = [
firstItem,
secondItem
];
var array = ko.pauseableObservable(items);
// Stop notifications from specific observable
firstItem.pause();
// Change won't raise notification to subscribers
firstItem("Hi");
// Resume notifications
firstItem.resume();
// Stop notifications from all items of array
array.pause();
// Change won't raise notification to subscribers
array()[0]("Hi");
// Resume notifications
array.resume();
我知道实现并不完美,我也没有时间好好测试它,但我希望它能帮助你找到灵感并改进它。
我需要从主视图模型中设置一个 属性 在可观察数组中。
我正在使用经典的 pre
来调试和显示我的可观察数组的内容。
通过使用 types.valueHasMutated()
我可以看到应用的更改 - 仅适用于 vm.types
(否则情况不会如此)。
但是,我需要看到这些更改反映在我的组件中。
在我的示例中,当我单击 "Apples" 时,相应的输入将被禁用,如下所示。可悲的是,事实并非如此。
我做错了什么?
ko.components.register("available-items", {
viewModel: function(params) {
function AvailableItems(params) {
var self = this;
self.params = params;
self.location = "A";
self.types = ko.computed(function() {
var types = self.params.types();
return ko.utils.arrayFilter(types, function(item) {
return item.location == self.location;
});
});
self.addItem = function(data, event) {
self.params.items.addItem(self.location, data.type);
};
}
return new AvailableItems(params);
},
template: '<div>' +
'<h4>Add item</h4>' +
'<ul data-bind="foreach: types">' +
'<li>' +
'<input type="text" data-bind="value: type, enable:available, event: {click: $parent.addItem}" readonly/>' +
'</li>' +
'</ul>' +
'</div>',
synchronous: true
});
var types = [{
type: "Apples",
location: "A",
available: true
}, {
type: "Bananas",
location: "A",
available: false
}];
function Vm(data) {
var self = this;
self.items = ko.observableArray();
self.types = ko.observableArray(ko.utils.arrayMap(data, function(item) {
return item;
}));
self.items.addItem = function(location, type) {
self.items.push({
location: location,
type: type
});
if (location == "A" && type == "Apples") {
self.types()[0].available = false;
self.types.valueHasMutated();
}
};
}
ko.options.deferUpdates = true;
var vm = new Vm(types);
ko.applyBindings(vm);
pre {
position: absolute;
width: 300px;
right: 0;
top: 0;
}
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
</head>
<body>
<div data-bind="component:{name:'available-items',params:vm}"></div>
<ul data-bind="foreach: items">
<li><span data-bind="text: location"></span> - <span data-bind="text: type"></span></li>
</ul>
<pre data-bind="text: ko.toJSON(vm.types, null, 2)"></pre>
</body>
</html>
我找到问题了。它在您的 html 中:"params: vm" 应该是 "params: $data"
<div data-bind="component:{name:'available-items',params:$data}"></div>
我在 jfiddle 上有 运行 这个,即使我添加了一个新类型,我也没有得到任何更新。
似乎有问题'<ul data-bind="foreach: types">' +
我改成了
'<ul data-bind="foreach: $root.types">' +
https://jsfiddle.net/fabwoofer/9szbqhj7/1/
现在添加了类型,但似乎未处理第一项的重新呈现。有类似问题的人建议使用此处描述的模板渲染
Knockout.js Templates Foreach - force complete re-render
希望对您有所帮助
您的 可用 属性 无法观察到。为了通知 Knockout 有关更改并让它更新 UI - 使此 属性 可观察。
ko.components.register("available-items", {
viewModel: function(params) {
function AvailableItems(params) {
var self = this;
self.params = params;
self.location = "A";
self.types = ko.computed(function() {
var types = self.params.types();
return ko.utils.arrayFilter(types, function(item) {
return item.location == self.location;
});
});
self.addItem = function(data, event) {
self.params.items.addItem(self.location, data.type);
};
}
return new AvailableItems(params);
},
template: '<div>' +
'<h4>Add item</h4>' +
'<ul data-bind="foreach: types">' +
'<li>' +
'<input type="text" data-bind="value: type, enable:available, event: {click: $parent.addItem}" readonly/>' +
'</li>' +
'</ul>' +
'</div>',
synchronous: true
});
var types = [{
type: "Apples",
location: "A",
// Make property observable
available: ko.observable(true)
}, {
type: "Bananas",
location: "A",
// Make property observable
available: ko.observable(false)
}];
function Vm(data) {
var self = this;
self.items = ko.observableArray();
self.types = ko.observableArray(ko.utils.arrayMap(data, function(item) {
return item;
}));
self.items.addItem = function(location, type) {
self.items.push({
location: location,
type: type,
available: ko.observable(false)
});
if (location == "A" && type == "Apples") {
// Update property as observable.
self.types()[0].available(false);
self.types.valueHasMutated();
}
};
}
ko.options.deferUpdates = true;
var vm = new Vm(types);
ko.applyBindings(vm);
pre {
position: absolute;
width: 300px;
right: 0;
top: 0;
}
<!DOCTYPE html>
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
</head>
<body>
<div data-bind="component:{name:'available-items',params:vm}"></div>
<ul data-bind="foreach: items">
<li><span data-bind="text: location"></span> - <span data-bind="text: type"></span></li>
</ul>
<pre data-bind="text: ko.toJSON(vm.types, null, 2)"></pre>
</body>
</html>
您可以使用用户 JotaBe 提供的解决方案:Refresh observableArray when items are not observables.
ko.observableArray.fn.refresh = function (item) {
var index = this['indexOf'](item);
if (index >= 0) {
this.splice(index, 1);
this.splice(index, 0, item);
}
}
现在,我需要更改 addItem()
并使用更新后的元素添加对 refresh
的调用:
self.items.addItem = function(location, type) {
self.items.push({
location: location,
type: type
});
if (location == "A" && type == "Apples") {
self.types()[0].available = false;
self.types.refresh(self.types()[0]); // <--- New sentence
}
};
这将刷新具有 types
列表的 <pre>
。但是不会刷新组件,它还有一个 types
.
然后我用了这个link,Forcing deferred notifications to happen early,我在refresh
里面加了ko.tasks.runEarly()
,我觉得现在可以用了
ko.observableArray.fn.refresh = function (item) {
var index = this['indexOf'](item);
if (index >= 0) {
this.splice(index, 1);
ko.tasks.runEarly(); // <--- New sentence
this.splice(index, 0, item);
}
}
这是一个Codepen.
受 pauseableComputed and observable withPausing 实施的启发,我创建了 pauseableObservable 和 pauseableObservableArray,它们能够停止向订阅者发送通知,并且比在需要时恢复。它还对所有嵌套的可暂停属性递归工作。
您可以使用它 HERE on Codepen(已根据您问题中的代码提供了示例)。 我还放置了达到目标的扩展代码:
PauseableObservable:
// PauseableObservable - it's observable that have functions to 'pause' and 'resume' notifications to subscribers (pause/resume work recursive for all pauseable child).
ko.isPauseableObservable = function(instance) {
return ko.isObservable(instance) && instance.hasOwnProperty("pause");
}
ko.pauseableObservable = function(value) {
var that = ko.observable(value);
function getPauseableChildren() {
var properties = Object.getOwnPropertyNames(that());
var currentValue = that();
var pauseables = properties.filter((property) => {
return ko.isPauseableObservable(currentValue[property]);
});
return pauseables.map((property) => {
return currentValue[property];
});
}
that.pauseNotifications = false;
that.isDirty = false;
that.notifySubscribers = function() {
if (!that.pauseNotifications) {
ko.subscribable.fn.notifySubscribers.apply(that, arguments);
that.isDirty = false;
} else {
that.isDirty = true;
}
};
that.pause = function() {
that.pauseNotifications = true;
var pauseableChildren = getPauseableChildren();
pauseableChildren.forEach((child) => { child.pause(); });
}
that.resume = function() {
that.pauseNotifications = false;
if (that.isDirty) {
that.valueHasMutated();
}
var pauseableChildren = getPauseableChildren();
pauseableChildren.forEach((child)=> { child.resume(); });
}
return that;
}
PauseableObservableArray
// PauseableObservableArray - simply use pauseable functionality of his items.
// Extension for stop notifications about added/removed items is out of scope.
ko.pauseableObservableArray = function(items) {
var that = ko.observableArray(items);
that.pause = function () {
var items = that();
items.forEach(function(item) {
if(ko.isPauseableObservable(item)) {
item.pause();
}
});
}
that.resume = function () {
var items = that();
items.forEach(function(item) {
if(ko.isPauseableObservable(item)) {
item.resume();
}
});
}
that.refresh = function () {
that.resume();
that.pause();
}
return that;
}
用法示例:
var firstItem = ko.pauseableObservable("Hello");
var secondItem = ko.pauseableObservable("World");
var items = [
firstItem,
secondItem
];
var array = ko.pauseableObservable(items);
// Stop notifications from specific observable
firstItem.pause();
// Change won't raise notification to subscribers
firstItem("Hi");
// Resume notifications
firstItem.resume();
// Stop notifications from all items of array
array.pause();
// Change won't raise notification to subscribers
array()[0]("Hi");
// Resume notifications
array.resume();
我知道实现并不完美,我也没有时间好好测试它,但我希望它能帮助你找到灵感并改进它。