Автоматическое удаление почтовых индексов ko
У меня есть одностраничное приложение, которое состоит из 2 основных частей:
1.-A top bar that has dynamic content(there is a shopping cart)
2.-A dynamic component that gets loaded based on the url.
Несколько раз компоненты используют почтовый ящик для связи, проблема в том, что как только сам компонент размещен, подписки, созданные внутри , не становятся. Я знаю, что могу вручную добавить функцию dispose для каждого компонента, а затем внутри, удалить подписки, но есть ли способ сделать это автоматически для всех компонентов?
Я знаю, как зациклить все свойства и проверить, являются ли они подписками, но мне нужен способ каким-то образом прикрепить это поведение ко всем компонентам, не присоединяя вручную эту функцию dispose ко всем из них.
Я знаю, что postbox поставляется с методом сброса, который я могу вызвать внутри своей библиотеки маршрутизации, но я не хочу этого делать, потому что тогда верхняя панель также потеряет свои подписки.
Чтобы дать вам некоторую перспективу, вот как выглядит главная страница индекса:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Participant Dashboard</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- styles -->
<link href="../css/bs3/bootstrap.css" rel="stylesheet">
<link href="../css/bs3/override-bs3.css" rel="stylesheet">
<script src="../scripts/global/requireConfig.js"></script>
<script data-main="../consumer/scripts/require-config" src="../scripts/require.js"></script>
</head>
<body>
<top-bar params="routeParams:currentPageArguments"></top-bar>
<div data-bind="component: { name: currentPage, params: currentPageArguments }">
</div>
</body>
</html>
Это мой пользовательский загрузчик компонентов:
function registerConventionLoader() {
var koNamingConventionLoader = {
getConfig: function (name, callback) {
var widgetName;
var widgetConfig = common.findComponentConfig(name);
if (widgetConfig != null) {
widgetName = name.substr(widgetConfig.Prefix.length);
var widgetNamePascalCase = common.toPascalCase(widgetName);
var filePath = widgetConfig.Path;
var viewModelConfig = {require: filePath + widgetNamePascalCase};
var templateConfig = {require: "text!" + filePath + widgetNamePascalCase + '.html'};
callback({viewModel: viewModelConfig, template: templateConfig});
}
else {
callback(null);
}
}
};
ko.components.loaders.push(koNamingConventionLoader);
}
2 ответа
Нашел это! Мне пришлось получить viewmodel из require, затем прикрепить функцию dispose и, наконец, передать ее в обратный вызов: Спасибо @user3297291 за то, что направил меня в правильном направлении.
var koNamingConventionLoader = {
getConfig: function (name, callback) {
var widgetName;
var widgetConfig = common.findComponentConfig(name);
if (widgetConfig != null) {
widgetName = name.substr(widgetConfig.Prefix.length);
var widgetNamePascalCase = common.toPascalCase(widgetName);
var filePath = widgetConfig.Path;
require([filePath + widgetNamePascalCase], function (mainViewModel) {
mainViewModel.prototype.dispose = function () {
var self = this;
for (var property in self) {
if (Boolean(self[property]) && typeof self[property].dispose === "function") {
self[property].dispose();
}
}
};
var templateConfig = {require: "text!" + filePath + widgetNamePascalCase + '.html'};
callback({viewModel: mainViewModel, template: templateConfig});
});
}
else {
console.log("widget name not resolved", name);
callback(null);
}
}
};
ko.components.loaders.push(koNamingConventionLoader);
}
Уже реализованное поведение
Плагин почтового ящика добавляет dispose
Метод ваших наблюдаемых, чтобы избавиться от любых зависимостей, которые он создал.
Функция dispose удаляет все подписки, которые наблюдаемая имеет на любую тему, а также все подписки, используемые для автоматической публикации изменений в наблюдаемой.
Эта функция присоединяется к наблюдаемой при вызове publishOn, subscribeTo или syncWith.
Источник: почтовый ящик github docs
Если viewmodel вашего компонента имеет dispose
метод, нокаут вызовет его при удалении компонента.
При желании ваш класс viewmodel может иметь
dispose
функция. Если реализовано, Knockout будет вызывать это всякий раз, когда компонент разрушается и удаляется из DOMИсточник: компонент-привязка
Логика пользовательской утилизации
Зная эти два поведения библиотеки / плагина, мы можем заключить, что эта общая идея должна сработать:
MyCustomComponent.prototype.dispose = function() {
/* call `.dispose` on all properties that support it */
};
Единственный оставшийся код, который мы должны написать, это закомментированная часть:
- Цикл по свойствам модели представления
- Проверьте, поддерживают ли они
dispose
метод - Позвони, если они это сделают
Что сводится к:
MyCustomComponent.prototype.dispose = function() {
var self = this;
var propNames = Object.keys(this);
propNames.forEach(function(key) { // Loop over vm's properties
var val = self[key];
if (typeof val.dispose === "function") { // Check of dispose implementation
val.dispose(); // call dispose
}
});
};
Или в другом стиле:
MyCustomComponent.prototype.dispose = function() {
Object
.keys(this)
.filter(k => typeof this[k] === "function")
.forEach(k => this[k]());
};
Убедиться, что все компоненты реализуют логику утилизации
Я настоятельно рекомендую использовать "обычный" шаблон наследования или композиции, чтобы убедиться, что все ваши компоненты реализуют эту функцию.
Хотя это вынуждает вас редактировать все ваши компоненты, оно также явно показывает другим читателям / будущим вам реализованное поведение.
Если вы действительно хотите выйти из себя, вы можете перезаписать компонентный движок register
метод для добавления метода в виртуальную машину при создании экземпляра, но я бы не рекомендовал его:
var _register = ko.components.register;
ko.components.register = function(name, opts) {
var ogVM = opts.viewmodel;
opts.viewmodel = function(params) {
ogVM.call(this, params);
this.dispose = function() { /* ... */ }
}
return _register(name, opts);
};