Как * не * уничтожить View при выходе из маршрута в Ember.js
Что касается новой системы маршрутизации Ember.js (описанной здесь), если я правильно понимаю, представления уничтожаются при выходе из маршрута.
Есть ли способ обойти уничтожение видов при выходе из маршрута, чтобы состояние вида сохранялось при повторном входе пользователя в маршрут?
Обновление: Похоже, представления не уничтожаются, если только представление о выходе не заменяется новым маршрутом. Например, если вы находитесь в stateA с ViewA в каком-то {{outlet master}} и переходите в stateB с ViewB в {{outlet master}}, то ViewB заменит ViewA. Обходным путем является определение нескольких выходов, когда необходимо сохранить виды, например, {{outlet master1}}, {{outlet master2}}, ...
Приятной особенностью была бы возможность передать массив представлений в розетку. А также сможете выбрать, будут ли виды уничтожены или просто станут скрытыми при выходе из маршрута.
2 ответа
С тех пор я выяснил, как изменить систему маршрутизации, чтобы представления, вставленные в розетки, не уничтожались. Сначала я переопределяю руль outlet
помощник, так что он загружает Ember.OutletView
в {{outlet}}
:
Ember.Handlebars.registerHelper('outlet', function(property, options) {
if (property && property.data && property.data.isRenderData) {
options = property;
property = 'view';
}
options.hash.currentViewBinding = "controller." + property;
return Ember.Handlebars.helpers.view.call(this, Ember.OutletView, options);
});
куда Ember.OutletView
продолжается Ember.ContainerView
следующее:
Ember.OutletView = Ember.ContainerView.extend({
childViews: [],
_currentViewWillChange: Ember.beforeObserver( function() {
var childViews = this.get('childViews');
// Instead of removing currentView, just hide all childViews
childViews.setEach('isVisible', false);
}, 'currentView'),
_currentViewDidChange: Ember.observer( function() {
var childViews = this.get('childViews'),
currentView = this.get('currentView');
if (currentView) {
// Check if currentView is already within childViews array
// TODO: test
var alreadyPresent = childViews.find( function(child) {
if (Ember.View.isEqual(currentView, child, [])) {
return true;
}
});
if (!!alreadyPresent) {
alreadyPresent.set('isVisible', true);
} else {
childViews.pushObject(currentView);
}
}
}, 'currentView')
});
В основном мы переопределяем _currentViewWillChange()
и просто спрятать все childViews
вместо удаления currentView
, Затем в _currentViewDidChange()
мы проверяем, если currentView
уже внутри childViews
и действовать соответственно. Ember.View.isEqual
модифицированная версия UnderscoreisEqual
:
Ember.View.reopenClass({
isEqual: function(a, b, stack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
if (b._chain) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = stack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (stack[length] == a) return true;
}
// Add the first object to the stack of traversed objects.
stack.push(a);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
// Ensure commutative equality for sparse arrays.
if (!(result = size in a == size in b && this.isEqual(a[size], b[size], stack))) break;
}
}
} else {
// Objects with different constructors are not equivalent.
if (a.get('constructor').toString() != b.get('constructor').toString()) {
return false;
}
// Deep compare objects.
for (var key in a) {
if (a.hasOwnProperty(key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if ( !(result = b.hasOwnProperty(key) )) break;
}
}
}
// Remove the first object from the stack of traversed objects.
stack.pop();
return result;
}
});
Таким образом, состояние представления сохраняется, когда пользователь повторно входит в маршрут.
Вместо этого я бы сохранил эту информацию в контроллере (или диспетчере состояний), чтобы при повторном вводе маршрута новое представление инициализировалось со старым состоянием. Имеет ли это смысл? Так, например, если это список постов, и один из них выбран, вы должны хранить данные о том, какой пост был выбран в контроллере (или менеджере состояний). После посещения определенного поста и возвращения в список, этот же пост будет выбран.
Я могу представить себе случай использования, когда это было бы не очень полезно (например, прокрутка до определенной позиции в длинном списке), так что, возможно, это не отвечает на ваш вопрос.