寻找一种很好的方式来取回在ExtJS类风格组件中的事件处理程序的引用
我正在寻找一种很好的方式来获取事件处理程序方法内的ExtJS类风格组件的属性回引用。 背景:我尝试编码自己的Shopware 5.2购物世界小部件。基本上,它是一个高级滑块,每个幻灯片图像上都有单独的文本。为此,我已经定义了一个模型和商店,它包含“真实”数据,稍后将存储在数据库中。因此,这不是任何值得保存的数据,而是更多用于引用网格中正确项目的运行时数据。寻找一种很好的方式来取回在ExtJS类风格组件中的事件处理程序的引用
这里的问题与古典桌面应用程序问题相同:在事件处理程序中获取同一类中对象数据的引用, G。点击处理程序来保存/修改显示的数据。基本上,事件处理程序(例如,单击处理程序)独立于类的其余部分,并且它们通常也被称为C类编程语言中的静态方法。
所以,我正在寻找一种很好的方式(很好的方式=没有代码味道)在JavaScript中做到这一点。由于我是ExtJS的新手,因此我可能并不完全了解它。在Shopware中使用过时的4.1版本的解决方案和文档部件也不是一件容易的事情。我的意思是不在sencha,在Shopware devdocs也不在。
由于这是ExtJS问题,而不是Shopware可解决的问题,所以我在这里要求获得更广泛的开发人员。
好了,我说我已经想通了选项至今都:
- 非常糟糕:定义一个全局变量,住在窗口范围
- 也许并不坏,但也不是最佳解决方案:为其创建一个ExtJS命名空间并在其中存储所需的变量。这实际上是由我完成的,并且工作正常(请参阅下面的代码示例)。
以下是完整的代码,我至今编码:
Ext.define('Shopware.apps.Emotion.view.components.Unsplash', {
extend: 'Shopware.apps.Emotion.view.components.Base',
alias: 'widget.emotion-components-unsplash',
declareNsGlobals: function() {
Ext.ns("Unsplash.componentView");
Unsplash.componentView.imgPos = -1;
},
/**
* Initialize the component.
*
* @public
* @return void
*/
initComponent: function() {
var me = this;
me.callParent(arguments);
me.declareNsGlobals();
// me.setDefaultValues();
me.add(me.createBannerFieldset());
me.initGridData();
// me.refreshHiddenValue();
},
/**
* Creates the fieldset which holds the banner administration. The method
* also creates the banner store and registers the drag and drop plugin
* for the grid.
*
* @public
* @return [object] Ext.form.FieldSet
*/
createBannerFieldset: function() {
var me = this;
me.slideEditorItem = me.getSlideEditorItem();
me.mediaSelection = Ext.create('Shopware.form.field.MediaSelection', {
fieldLabel: me.snippets.select_banner,
labelWidth: 100,
albumId: -3,
listeners: {
scope: me,
selectMedia: me.onAddBannerToGrid
}
});
me.bannerStore = Ext.create('Ext.data.Store', {
fields: [ 'position', 'path', 'link', 'altText', 'title', 'mediaId', 'slideText' ]
});
me.ddGridPlugin = Ext.create('Ext.grid.plugin.DragDrop');
me.cellEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToEdit: 2
});
me.bannerGrid = Ext.create('Ext.grid.Panel', {
columns: me.createColumns(),
autoScroll: true,
store: me.bannerStore,
height: 200,
plugins: [ me.cellEditing ],
viewConfig: {
plugins: [ me.ddGridPlugin ],
listeners: {
scope: me,
drop: me.onRepositionBanner
}
},
listeners: {
scope: me,
edit: function() {
me.refreshHiddenValue();
}
}
});
return me.bannerFieldset = Ext.create('Ext.form.FieldSet', {
title: me.snippets.banner_administration,
layout: 'anchor',
'defaults': { anchor: '100%' },
items: [ me.slideEditorItem, me.mediaSelection, me.bannerGrid ]
});
},
/**
* Factory method for the TinyMCE form element creation.
*
* @returns {Shopware.form.field.TinyMCE}
*/
getSlideEditorItem: function() {
return Ext.create('Shopware.form.field.TinyMCE', {
name: 'slide_editor',
id: 'slide_editor',
translatable: false,
fieldLabel: 'Slide Text',
labelWidth: 100
});
},
/**
* Helper method which creates the column model
* for the banner administration grid panel.
*
* @public
* @return [array] computed columns
*/
createColumns: function() {
var me = this, snippets = me.snippets;
return [ {
header: '⚌',
width: 24,
hideable: false,
renderer: me.renderSorthandleColumn
}, {
dataIndex: 'path',
header: snippets.path,
flex: 1
}, {
dataIndex: 'link',
header: snippets.link,
flex: 1,
editor: {
xtype: 'textfield',
allowBlank: true
}
}, {
dataIndex: 'altText',
header: snippets.altText,
flex: 1,
editor: {
xtype: 'textfield',
allowBlank: true
}
}, {
dataIndex: 'title',
header: snippets.title,
flex: 1,
editor: {
xtype: 'textfield',
allowBlank: true
}
}, {
xtype: 'actioncolumn',
header: snippets.actions,
width: 60,
items: [ {
iconCls: 'sprite-minus-circle',
action: 'delete-banner',
scope: me,
handler: me.onDeleteBanner
}, {
iconCls: 'sprite-pencil',
action: 'editSlideTextWhatever',
tooltip: "load slide text in editor and update it",
scope: me,
handler: me.onEditSlideText
} ]
} ];
},
/**
* Refactor sthe mapping field in the global record
* which contains all banner in the grid.
*
* Adds all banners to the banner administration grid
* when the user opens the component.
*
* @return void
*/
initGridData: function() {
var me = this,
elementStore = me.getSettings('record').get('data'), bannerSlider;
// TODO: check if this below works?!
Ext.each(elementStore, function (element) {
if (element.key === 'banner_slider') {
bannerSlider = element;
return false;
}
});
if (bannerSlider && bannerSlider.value) {
Ext.each(bannerSlider.value, function (item) {
me.bannerStore.add(Ext.create('Shopware.apps.Emotion.model.Unsplash', item));
});
}
},
/**
* Event listener method which will be triggered when one (or more)
* banner are added to the banner slider.
*
* Creates new models based on the selected banners and
* assigns them to the banner store.
*
* @public
* @event selectMedia
* @param [object] field - Shopware.MediaManager.MediaSelection
* @param [array] records - array of the selected media
*/
onAddBannerToGrid: function (field, records) {
var me = this, store = me.bannerStore;
Ext.each(records, function (record) {
var count = store.getCount();
var model = Ext.create('Shopware.apps.Emotion.model.Unsplash', {
position: count,
path: record.get('path'),
mediaId: record.get('id'),
link: record.get('link'),
altText: record.get('altText'),
title: record.get('title'),
slideText: record.get('slideText')
});
store.add(model);
});
// We need a defer due to early firing of the event
Ext.defer(function() {
me.mediaSelection.inputEl.dom.value = '';
me.refreshHiddenValue();
}, 10);
},
/**
* Event listener method which will be triggered when the user
* deletes a banner from banner administration grid panel.
*
* Removes the banner from the banner store.
*
* @event click#actioncolumn
* @param [object] grid - Ext.grid.Panel
* @param [integer] rowIndex - Index of the clicked row
* @param [integer] colIndex - Index of the clicked column
* @param [object] item - DOM node of the clicked row
* @param [object] eOpts - additional event parameters
* @param [object] record - Associated model of the clicked row
*/
onDeleteBanner: function (grid, rowIndex, colIndex, item, eOpts, record) {
var me = this;
var store = grid.getStore();
var globImgPos = Unsplash.componentView.imgPos;
store.remove(record);
console.log("Unsplash.componentView.imgPos", Unsplash.componentView.imgPos);
console.log("record position:", record.get("position"));
// console.log("eOpts scope imgPos", eOpts.scope);
if (globImgPos > -1 && record.get("position") === globImgPos) {
Ext.getCmp("slide_editor").setValue("", false);
}
me.refreshHiddenValue();
},
/**
* Event listener method which will be triggered when the user
* whishes to edit a banner slide text from banner administration grid panel.
*
* Removes the banner from the banner store.
*
* @event click#actioncolumn
* @param [object] grid - Ext.grid.Panel
* @param [integer] rowIndex - Index of the clicked row
* @param [integer] colIndex - Index of the clicked column
* @param [object] item - DOM node of the clicked row
* @param [object] eOpts - additional event parameters
* @param [object] record - Associated model of the clicked row
*/
onEditSlideText: function (grid, rowIndex, colIndex, item, eOpts, record) {
var me = this;
// TODO: defer load and growl message on after done
var htmlEditor = Ext.getCmp('slide_editor');
Unsplash.componentView.imgPos = record.get("position");
htmlEditor.setValue(record.get("slideText") + " behind that " + record.get("position"), false);
},
/**
* Event listener method which will be fired when the user
* repositions a banner through drag and drop.
*
* Sets the new position of the banner in the banner store
* and saves the data to an hidden field.
*
* @public
* @event drop
* @return void
*/
onRepositionBanner: function() {
var me = this;
var i = 0;
globImgPos = Unsplash.componentView.imgPos;
me.bannerStore.each(function (item) {
// also update the imgPos to detect item deletion also right after repositioning, if there is one already defined
if (globImgPos > -1 && globImgPos === item.get("position")) {
Unsplash.componentView.imgPos = i;
}
item.set('position', i);
i++;
});
me.refreshHiddenValue();
},
/**
* Refreshes the mapping field in the model
* which contains all banners in the grid.
*
* @public
* @return void
*/
refreshHiddenValue: function() {
var me = this,
store = me.bannerStore,
cache = [];
store.each(function (item) {
cache.push(item.data);
});
var record = me.getSettings('record');
record.set('mapping', cache);
},
/**
* Renderer for sorthandle-column
*
* @param [string] value
*/
renderSorthandleColumn: function() {
return '<div style="cursor: move;">⚌</div>';
}
});
几个明显的价值指向它:
- 此代码最初是为在另一个部件制造Shopware编码书。我用这个作为起点,因为我只能得到这个小工具的工作。所以我删除了所有不需要的代码,并用我自己的代码替换掉了。由于它仍在开发中,因此可能会有一些反向引用或来自原始小部件的名称。一个是品牌名称“Unsplash”。正如我所说,我无法改变这一点,以导致一个工作小部件。当然,这将在开发结束之前改变。所以,没有真正的最终用户会在我的小部件中看到这些品牌名称。这只是现在(我和我本地安装的开发环境)。
- 我还从Shopware“横幅滑块”小工具中复制了很多功能逻辑,因为它几乎与我需要做的一样。因此,您可能会发现与原始小部件的一些相似之处。
- 除了我的第2点,缩短了工作代码示例。如果您对那些可能没有显示的小功能感兴趣,您可以在这里找到它:https://github.com/shopware/shopware/blob/5.2/themes/Backend/ExtJs/backend/emotion/view/components/banner_slider.js
此外,媒体小部件可以在那里找到。
- 我最初被用作我的起始基础微件可以在这里找到:https://s3-eu-west-1.amazonaws.com/gxmedia.galileo-press.de/supplements/4185/4243_Zusatzmaterialien.zip
各自的作者此微件(第7)的是丹尼尔Nögel(书作者),Shopware AG(本书的协助)等等。这些代码示例实际上没有给出明确的许可证。由于这是一本如何做本书,我假设我有权在我的小部件中使用此代码。
你所有的事件处理程序是正常的方法,不是静态的,而且仅限于此me
,这是组件本身。
这使得它很容易:您可以扩展组件对象。因此,您可以使用me.imgPos
或this.imgPos
(取决于您是否在事件侦听器的开始处定义var me = this
)而不是Unsplash.componentView.imgPos
。
自己修复了参考问题。我在这里发现了另外一个问题,从中我发现了作者确切的问题。 ExtJS中的这种行为只适用于事件处理函数,因为ExtJS模拟了一种类风格的程序设计,但这是一个例外,它是“接近”的对象方向。 JavaScript在静态和静态之间没有区别。所以,这完全解决了我的问题,太:ExtJS - How to reference "self" in a custom function in a custom class?
我刚刚宣布这样的对象引用本身:
Ext.define('Shopware.apps.Emotion.view.components.Unsplash', {
extend: 'Shopware.apps.Emotion.view.components.Base',
alias: 'widget.emotion-components-unsplash',
// [...]
objRef: null,
// [...]
然后在initComponent魔术适用:
initComponent: function() {
objRef = this;
// [...]
},
我需要也可以将每个事件处理程序从var me = this;
更改为objRef
。 E.摹:
onEditSlideText: function (grid, rowIndex, colIndex, item, eOpts, record) {
// TODO: defer load and growl message on after done
var htmlEditor = Ext.getCmp('slide_editor');
Unsplash.componentView.imgPos = record.get("position");
htmlEditor.setValue(record.get("slideText"), false);
objRef.setEditorButtonNumberVal(Unsplash.componentView.imgPos);
}
其他的一切比事件可以留在使用var me = this;
旧的风格。此问题仅适用于事件。
如果要在被调用的函数中重用上下文,可以在函数的调用上绑定上下文。
例如:
me.add(me.createBannerFieldset.bind(me).call());
它将使用的上下文,其中所述执行是。
所以,如果你fctA提醒您“
编辑:例
this.fctA = function() {
var me = this;
this.valA = true;
this.valB = false;
fctInsideA() {
//here *this* is relatate to fctInsideA not fctA
me.fctC.bind(me).call();
}
}
this.fctB = function() {
var me = this;
this.valA = false;
this.valB = true;
fctInsideB() {
//here *this* is relatate to fctInsideB not fctB
me.fctC.bind(me).call();
}
}
this.fctC = function() {
if(this.valA)
alert("A context");
if(this.valB)
alert("B context);
}
如果使用bind
,在当前的函数中定义的范围内将通过在被调用函数的上下文
所以,如果你fctA,提醒您“上下文”。如果你打电话fctB,提醒您“B语境”。
是不是更清楚了吗?
请问你能解释一下吗?我现在不确定,你的意思。因此,如果我打开两个对话框实例会发生什么?然后我是否将这些实例绑定在一起? – alpham8
我做了一个编辑解释。如果不清楚,请告诉我 –
我试过之前问。它不起作用。我的班级属性总是失去了参考。在事件处理程序中,每次都是初始值,而不是用户从之前选择的值。这对删除很不利,因为它会删除错误的网格条目。另外,如果我没有在我的列中传递'scope'键,那么这个属性起作用,但他说,'me.refreshHiddenValue()'是未定义的?!所以,使用命名空间是我发现的第一个选择,这两个事情都起作用。 – alpham8
请将'console.log(me)'放入所有侦听器,并检查它们是否指向组件。我想你有一个范围问题需要解决。 – Alexander
是的。就是这样,但不像你在你的回答中描述的那样。像JavaScript一样,事件函数中的'this'引用事件本身,而不是经常被排除在类或对象之外。我不敢相信我在这个问题上跑了几百次,而且我在这里也没有认出它...... – alpham8