Присоедините контроллеры ExtJS MVC к элементам DOM, а не к компонентам
Есть ли способ использовать метод управления Ext.app.Controller(), но передать запрос DOM? У меня есть страница, которая содержит стандартные ссылки и хотела бы добавить к ним обработчик кликов, даже если они не были созданы как кнопки расширения.
Я пробовал
Ext.define('app.controller.TabController', {
extend: 'Ext.app.Controller',
init: function() {
console.log("init");
this.control({
'a': {
click: this.changeTab
}
});
},
changeTab: function() {
alert("new tab!");
}
});
Но при нажатии на ссылки не срабатывает предупреждение.
Есть ли способ указать селектор CSS с this.control? Или он работает только с компонентами?
Ответы
Ответ 1
Я задал этот вопрос в SenchaCon в этом году, разработчики Sencha заявили, что их намерение заключается в том, что слушатели DOM должны быть прикреплены в вашем представлении, и представление должно абстрагировать их на более значимые события компонентов и отказываться от них.
Например, предположим, что вы создаете представление UserGallery, которое отображает сетку лиц. В вашем классе UserGallery вы будете прослушивать событие DOM click в теге <img>
для получения события и цели, а затем представление может инициировать событие компонента, называемое "userselected", и передать экземпляр модели для пользователя с щелчком, а не цель DOM.
Конечная цель заключается в том, что только ваши взгляды должны быть связаны с вещами, такими как события интерфейса и элементы DOM, в то время как контроллер уровня приложения имеет дело только с значимыми намерениями пользователя. Ваш код приложения и контроллера не должен быть привязан к вашей структуре разметки или реализации интерфейса вообще.
Пример просмотра
Ext.define('MyApp.view.UserGallery', {
extend: 'Ext.Component'
,xtype: 'usergallery'
,tpl: '<tpl for="users"><img src="{avatar_src}" data-ID="{id}"></tpl>'
,initComponent: function() {
this.addEvents('userselected');
this.callParent(arguments);
}
,afterRender: function() {
this.mon(this.el, 'click', this.onUserClick, this, {delegate: 'img'});
this.callParent(arguments);
}
,onUserClick: function(ev, t) {
ev.stopEvent();
var userId = Ext.fly(t).getAttribute('data-ID');
this.fireEvent('userselected', this, userId, ev);
}
});
Заметки о просмотрах
- Расширьте "Ext.Component", когда все, что вам нужно, - это управляемый
<div>
, Ext.Panel намного тяжелее, чтобы поддерживать такие вещи, как заголовки, панели инструментов, сворачивание и т.д.
- Используйте "управляемые" прослушиватели при подключении слушателей к элементам DOM из компонента (см. Component.mon). Слушатели, управляемые компонентами, будут автоматически освобождены, когда этот компонент будет уничтожен.
- При прослушивании одного и того же события из нескольких элементов DOM используйте параметр "делегировать" и присоединяйте слушателя к общему родительскому элементу, а не к отдельным элементам. Это лучше работает и позволяет произвольно создавать/уничтожать дочерние элементы, не беспокоясь о постоянном подключении/удалении прослушивателей событий для каждого дочернего элемента. Избегайте использования чего-то вроде
.select('img').on('click', handler)
- При запуске события из представления соглашение Sencha заключается в том, что первым параметром события будет
scope
- ссылка на представление, которое произвело событие. Это удобно, когда событие обрабатывается с контроллера, где вам понадобится фактическая область действия обработчика события как контроллера.
Контроллер образца
Ext.define('app.controller.myController', {
extend: 'Ext.app.Controller'
,init: function() {
this.control({
'usergallery': {
userselected: function(galleryView, userId, ev) {
this.openUserProfile(userID);
}
}
});
}
,openUserProfile: function(userId) {
alert('load another view here');
}
});
Ответ 2
Я нашел работу для этой проблемы. Это не так прямо, как можно надеяться, но он оставляет все ваши "действия" в контроллере.
requirment: оберните раздел html вашей страницы в фактическом Ext.Component
. Вероятно, это будет так или иначе. Так, например, у вас может быть простой вид, содержащий ваш HTML следующим образом:
Ext.define('app.view.myView', {
extend: 'Ext.panel.Panel',
alias: 'widget.myView',
title: 'My Cool Panel',
html: '<div><a href="#">This link will open a window</a></div><br /> <label for="myInput">Type here: </label><input name="myInput" type="text" value="" />',
initComponent: function(){
var me = this;
me.callParent(arguments);
}
});
Затем в контроллере вы используете событие afterrender
для применения слушателей к вашим элементам DOM. В приведенном ниже примере я иллюстрирую как ссылки (элемент), так и элементы ввода:
Ext.define('app.controller.myController', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'myView': {
afterrender: function(cmp){
var me = this; //the controller
var inputs = cmp.getEl().select('input'); // will grab all DOM inputs
inputs.on('keyup', function(evt, el, o){
me.testFunction(el); //you can call a function here
});
var links = cmp.getEl().select('a'); //will grab all DOM a elements (links)
links.on('click', function(evt, el, o){
//or you can write your code inline here
Ext.Msg.show({
title: 'OMG!',
msg: 'The controller handled the "a" element! OMG!'
});
});
}
}
});
},
testFunction: function(el) {
var str = 'You typed ' + el.value;
Ext.Msg.show({
title: 'WOW!',
msg: str
});
}
});
И там у вас есть, элементы DOM обрабатываются в контроллере и придерживаются архитектуры MVC!
Ответ 3
Нет, это кажется невозможным. Ext.EventBus прослушивает события, запущенные компонентами ExtJS. Ваши стандартные элементы DOM не запускают эти события. Кроме того, запрос проверяется с помощью метода компонентов ExtJS (String selector), который не может быть вызван элементами DOM. Кто-то может исправить меня, если я ошибаюсь, но я уверен, что это невозможно, к сожалению.
Ответ 4
У меня также есть решение, которое работает вокруг этой проблемы. Я использую этот метод, несмотря на то, что он имеет другие преимущества: я создал шину сообщений с широким распространением сообщений. Его объект в моем приложении расширяет Observable и определяет несколько событий. Затем я могу запустить эти события из любого приложения, включая html ссылки. Любой компонент, который хочет прослушать эти события, может передать их, и они будут стрелять, как если бы они были запущены с этого компонента.
Ext.define('Lib.MessageBus', {
extend: 'Ext.util.Observable',
constructor: function() {
this.addEvents(
"event1",
"event2"
);
this.callParent(arguments);
}
});
Затем каждый другой compnent может добавить это после инициализации:
this.relayEvents('Lib.MessageBus', ['event1','event2']);
а затем прослушайте эти события.
Вы можете инициировать события из чего угодно:
Lib.MessageBus.fireEvent('event1', 'param a', 'param b')
и вы можете сделать это из всего, включая html-ссылки.
Очень удобно запускать события из одной части приложения в другую.
Ответ 5
У меня была такая же проблема в последнее время (смешение mvc с некоторыми не компонентами).
Просто подумал, что я брошу это как ответ, поскольку он кажется довольно простым и работает для меня:)
Ext.define('app.controller.TabController', {
extend: 'Ext.app.Controller',
init: function() {
console.log("init");
this.control({
/* 'a': {
click: this.changeTab
} */
});
var link = Ext.dom.Query.selectNode('a');
Ext.get(link).on('click', this.changeTab);
},
changeTab: function() {
alert("new tab!");
}
});