I´m using ExtJs4.1, here is my example. The "calendarioChange" is not called in this way:
Ext.define('App.view.atendimento.Agenda', {
extend: 'Ext.window.Window',
maximized: true,
bodyPadding: 4,
constrain: true,
layout: 'hbox',
items:[
{
xtype: 'container',
width: 300,
layout: 'vbox',
items: [
{
xtype: 'datepicker',
handler: this.calendarioChange
}
]
}
],
calendarioChange: function(picker, date){
alert('changed');
}
});
but in thsi way works:
xtype: 'datepicker',
handler: function(picker, date){
alert('changed');
}
What I´m missing in the first case?
Thanks.
the problem is you haven't taken in account the scope of your handle. Every time you nest a {} constructor you change the 'this' pointer. In your case:
this.calendarioChange
Can not work because 'this' is pointing to the datepicker not the Window. You could solve it by adding a function in the event that locate the window and then call to the appropiate method:
items: [
{
xtype: 'datepicker',
handler: function(picker, date) {
var window = this.up('window');
window.calendarioChange(picker, date);
}
}
...
Because the method you define as an event handler is not in this scope. For example you can declare the method outside Ext.define() call and then refer it just by name.
Related
In ExtJS 6.2 classic we usually implement initComponent when we extend a component.
On ExtJS modern, there's no initComponent for Ext.Container, besides constructor which function would replace initComponent in Modern?
I'm having an issue because I implemented the component initialization in the constructor and when I display the component again it runs the constructor again with the config set as the previous updated config, so the component renders things multiple times where it should only render once.
Ext.define('AppName.view.settings.CalloutBox', {
extend: 'Ext.Container',
xtype: 'calloutbox',
layout : {
type : 'hbox',
},
constructor: function(config) {
// Added this to avoid the bug I commented above.
if (config.cls) {
this.callParent(arguments);
return;
}
config.cls = `myclass`;
// Here I programmatically add elements. These cause the bug
// because on next run of this constructor these will be
// added again.
config.items = [{
xtype : 'container',
cls : `alert__icon-box`,
layout: {
type : 'vbox',
pack : 'center',
align : 'middle'
},
items: [{
xtype :'label',
cls : `alert__icon`,
html : `<i class="x-font-icon md-icon-alert"></i>`,
}]
}];
this.callParent(arguments);
}
});
Update
I found out the cause of the duplicating elements bug. It was caused by recreating the modal that the CalloutBox was in every time instead of just showing the one already open.
So we had this:
Ext.Viewport.add({ xtype: 'mymodal' }).show();
And now:
const mymodal = Ext.getCmp('mymodal');
// If component already exists in DOM, just show it.
if (mymodal) {
mymodal.show();
return;
}
// Else we create and show it.
Ext.Viewport.add({ xtype: 'mymodal' }).show();
Check out the initialize template method of Container and other classes in Modern Toolkit. Try running the following code in a fiddle:
Ext.define('MyContainer', {
extend: 'Ext.Container',
xtype: 'mycontainer',
initialize: function () {
this.add({
xtype: 'container',
layout: {
type: 'vbox',
pack: 'center',
align: 'middle'
},
items: [{
xtype: 'label',
html: 'label1',
},{
xtype: 'label',
html: 'label2',
}]});
}
});
Ext.create({
"xtype": "mycontainer",
renderTo: Ext.getBody()
});
I’m new here in at Stackoverflow and to Sencha ExtJS development. I’m a student from Germany and I’m current trying to get my degree in media computer sciences. As a part of my final assignment I’m currently developing the UI of a webapp for a local company.
While I was trying out the capabilities of the Sencha ExtJS framework I came across some problems, which is why I’m now reaching out to the community for help ;)
My first problem I had, was when I was playing around with the syntax for instancing classes using xtypes and the association of Stores inside the ViewModel:
For the purpose of easier to read and less cluttered code I wanted to give my stores their own xtype so I could instead of declaring all the stores and their configs inside the ViewModels’ stores config wanted to have every store inside their own file and then just create an instance of them later inside the ViewModel. The code I wrote for this looks like this:
ViewModel:
Ext.define('Example.viewmodel.MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myviewmodel',
requires: [
'Example.store.MyStore',
],
stores: {
StoreA: { xtype: 'store_a' },
StoreB: { xtype: 'store_b' },
StoreC: { xtype: 'store_c' }
},
data: {
/* This object holds the arbitrary data that populates the ViewModel and
is then available for binding. */
}
});
StoreA:
Ext.define('Example.store.StoreA', {
extend: 'Ext.data.Store',
xtype: 'store_a',
requires: [
'Example.model.ModelA'
],
storeId: 'StoreA',
autoLoad: false,
model: 'Example.model.ModelA',
listeners: {
load: 'onLoadofStoreA'
}
});
But apparently this isn’t working… My load listener of the store does not seem to fire at the method inside my controller and does not even seem to know about the view that is associated with the ViewModel. What am I doing wrong or is this just not meant to be done like that?
My Second Problem was when I was playing around with some of the UI components. My scenario was like this:
I wanted to have a menu that would slide in, where u could do some inputs that would then load the content for the view.
I found this example for a sliding menu (https://examples.sencha.com/extjs/6.7.0/examples/kitchensink/?modern#menus) and built this:
Inside my ViewController:
getMenuCfg: function (side) {
var cfg = {
side: side,
controller: example_controller',
id: 'topMenu',
items: [
{
xtype: 'container',
layout: 'hbox',
width: '100%',
items: [
{
xtype: 'fieldset',
reference: 'fldSet',
id: 'fldSet',
layout: 'vbox',
width: '50%',
defaults: {
labelTextAlign: 'left'
},
items: [
{
autoSelect: false,
xtype: 'selectfield',
label: 'Selectfield',
reference: 'sfExample',
id: 'sfExample',
listeners: {
change: 'onSFChange'
}
},
{
xtype: 'container',
layout: {
type: 'hbox',
align: 'end',
pack: 'center'
},
items: [{
xtype: 'textfield',
reference: 'ressource',
id: 'ressource',
flex: 1,
textAlign: 'left',
margin: '0 10 0 0',
label: 'Ressource',
labelAlign: 'top',
labelTextAlign: 'left',
editable: false,
readOnly: true
},
{
xtype: 'button',
shadow: 'true',
ui: 'action round',
height: '50%',
iconCls: 'x-fa fa-arrow-right',
handler: 'openDialog'
}
]
},
{
xtype: 'textfield',
reference: 'tfExample',
id: 'tfExample',
label: 'Textfield',
editable: false,
readOnly: true
}
]
},
}]
}];
The problem I come across now is, that I would no longer be able to easily get the references of components inside the menu (input fields) with this.lookupReference() as if they were just part of the view. In fact to find a workaround I had to trace a way back to the components using a debugger.
For example if another method inside my controller wanted to use a field inside this menu, instead of simply just doing this.lookupReference(‘sfExample’) I now had to do something like this:
var me = this,
vm = me.getViewModel(),
menuItems = me.topMenu.getActiveItem().getItems(),
fieldset = menuItems.getByKey('fldSet'),
selectfieldRessArt = fieldsetLeft.getItems().getByKey('sfExample');
I’m pretty sure that I am missing out on something important here and there has to be a way to do this easier. I’m really looking forward to your answers, thank you in advance ;)
use xtype only for components. if you need to define an type/alias for store, use alias config property instead and especify the alias category "store.".
Defining a store with an alias
Ext.define('Example.store.StoreA', {
extend: 'Ext.data.Store',
//use store. to category as a store
alias: 'store.store_a',
requires: [
'Example.model.ModelA'
],
storeId: 'StoreA',
autoLoad: false,
model: 'Example.model.ModelA',
listeners: {
load: 'onLoadofStoreA'
}
});
Instantianting your store by type
Ext.define('Example.viewmodel.MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myviewmodel',
requires: [
'Example.store.MyStore',
],
stores: {
StoreA: { type: 'store_a' },
StoreB: { type: 'store_b' },
StoreC: { type: 'store_c' }
},
data: {
/* This object holds the arbitrary data that populates the ViewModel and
is then available for binding. */
}
});
I Hope it can help you
First of all to stress it: I'm using sencha modern.
Well consider the following view:
Ext.define('myApplication.view.main.OrderView', {
extend: 'Ext.Container',
xtype: 'ordercontainer',
controller: 'order',
requires: [
'Ext.MessageBox',
'Ext.layout.Fit'
],
viewModel: 'main',
defaults: {
tab: {
iconAlign: 'top'
},
flex: 1
},
layout: 'vbox',
items: [
{
xtype: 'button',
text: 'test, to be updated',
height: 30,
flex: 0,
listeners: {
click: 'onClick'
}
},
]
});
With the corresponding controller:
Ext.define('myApplication.view.main.OrderController', {
extend: 'Ext.app.ViewController',
alias: 'controller.order',
onClick: function () {
debugger;
alert('onClick');
}
});
now I can see the button when the page is loaded. I expect the onClick function to be called (as per guide). However it is not. I don't see any alert, nor will (when development console is open in chrome) the debugger halt on the onClick function.
If I change the button to use a handle and use code-behind it works. Yet the view controller doesn't work. This shouldn't fail right? Or did I once again fall into the trap of using a guide for classic?
It's because in Modern the components doesn't have event click but tap
So change your code like this:
items: [
{
xtype: 'button',
text: 'test, to be updated',
height: 30,
flex: 0,
listeners: {
tap: 'onClick'
}
},
]
I am trying to mod the Portlet example and port it into our program. The code looks like this:
Ext.create('Ext.container.Viewport',{
id: 'app-viewport', //creates viewport
layout: {
type: 'border',
padding: '0 5 5 5' // pad the layout from the window edges
},
onPortletClose: function(portlet) {
this.showMsg('"' + portlet.title + '" was removed');
},
showMsg: function(msg) {
var el = Ext.get('app-msg'),
msgId = Ext.id();
this.msgId = msgId;
el.update(msg).show();
Ext.defer(this.clearMsg, 3000, this, [msgId]);
},
clearMsg: function(msgId) {
if (msgId === this.msgId) {
Ext.get('app-msg').hide();
}
},
items: [{
id: 'app-header',
xtype: 'box',
region: 'north',
height: 40,
html: 'Ext Welcome'
},{
xtype: 'container',
region: 'center',
layout: 'border',
items: [{
id: 'app-options', //Creates the Options panel on the left
title: 'Options',
region: 'west',
animCollapse: true,
width: 200,
minWidth: 150,
maxWidth: 400,
split: true,
collapsible: true,
layout:{
type: 'accordion',
animate: true
},
items: [{
html: content,
title:'Navigation',
autoScroll: true,
border: false,
iconCls: 'nav'
},{
title:'Settings',
html: content,
border: false,
autoScroll: true,
iconCls: 'settings'
}]
},{
id: 'app-portal', //Creates the panel where the portal drop zones are.
xtype: 'mainpanel',
region: 'center',
items: [{
id: 'col-1', //Each column represents a drop zone column. If we add more there are more created and width gets adjusted accordingly
items: [{
id: 'portlet-1',
title: 'Portlet 1',
html: content,
listeners: {
'close': Ext.bind(this.onPortletClose, this)
},{
id: 'portlet-2',
title: 'Portlet 2',
html: content,
listeners: {
'close': Ext.bind(this.onPortletClose, this)
}
}]
},{
id: 'col-2',
items: [{
id: 'portlet-3',
title: 'Portlet 3',
html: content,
listeners: {
'close': Ext.bind(this.onPortletClose, this)
}
}]
},{
id: 'col-3',
items: [{
id: 'portlet-4',
title: 'Portlet 4',
html: content,
listeners: {
'close': Ext.bind(this.onPortletClose, this)
}
}]
}]
}]
}]
});
The problem is that Ext.bind cannot read the onPortletClose function and the browser gives me a:
Uncaught TypeError: Cannot read property 'apply' of undefined error. I checked the stack and essentially in the Ext.bind(fn,scope) the fn is undefined and thus it cannot read the handler function. The difference between my application and in the example is that I add this directly into a JSP's Ext.onReady() whereas in the example all of this is added through a Ext.apply(this, {...}). I'm really confused. I tried all kinds of gimmicks to force the scope on the viewport but it seems that whatever is inside Ext.bind() loses contact with outside or something. I've used Ext.bind() before and it went fine although it was inside an initComponent configuration. Is that mandatory? If no then what is the problem?
It is important to understand the meaning of this here (or in general when working with JavaScript or ExtJS).
In a global context, this refers to the global object, also known as the global window variable. This is the case inside your Ext.onReady function:
Ext.onReady(function() {
console.log(this === window);
// -> true
// because this is the global object
});
In an object context, by default this refers to the owner object of the function (there are ways around it and actually this is exactly want you want do achieve using Ext.bind):
var obj = {
prop: 'My text property',
fn: function() {
console.log(this.prop);
// -> My text property
// because "this" refers to our object "obj"
}
};
Now, this is exactly what makes the difference between your case and the one from the examples.
When using the approach with Ext.onReady this - as pointed out above - will refer to the global object, which of course does not have a function onPortletClose.
When using the approach from the examples, this is accessed from inside of initComponent, which is a member function of your derived viewport class, and therefore this refers to the component instance (= the owning object), which allows you to access your function.
Ext.define('MyViewport', {
extend: 'Ext.container.Viewport',
onPortletClose: function(portlet) {
this.showMsg('"' + portlet.title + '" was removed');
},
initComponent: function() {
console.log(this);
// -> the instance of your viewport class
Ext.apply(this, {
items:[{
xtype: 'panel',
title: 'Portlet 1',
listeners: {
'close': Ext.bind(this.onPortletClose, this)
}
}]
});
this.callParent();
}
});
Side notes
Ext.apply
Ext.apply really just copies all properties from one object to another, so in this case it is just a convenient way of applying all attributes of that object to this with one function call. You could as well just use:
this.items = [...];
Changing the scope of your listeners
You do not need to use Ext.bind when adding listeners to a component, you can simply provide a scope configuration in your listeners object:
listeners: {
close: function() {...},
scope: this
}
External sources
If you want to dive in a little deeper (which I would recommend), you might want to read more about this at the MDN (Mozilla Developer Network).
Im working on a project using Ext JS4. In some of our classes we are declaring functions inside the initComponent function which may then be set as handlers to a control. I will include an example of this below. Ignore most of what is in the class, the key details are the fact that the Handlers are declared within initComponent and set as handlers to the buttons.
Now, this actually WORKS - the qestion here is WHY this works. I'm fairly new to JavaScript, but I thought that any vars or functions declared within a function were destroyed once that function had completed. Is this incorrect? I appreciate that there may be a better coding style to this, but I would really like to get my head around this before I consider changing loads of classes. The class is as follows... Some comments identify the key areas.
Ext.onReady(function () {
Ext.application({
name: 'Form2',
thisForm: {},
launch: function() {
thisForm = Ext.create('Form2', {});
}
});
});
Ext.define('Form2', {
extend: 'Ext.form.Panel',
layout:'border',
config: {
controlManager: {},
formVariables: {},
dataHelper: {}
},
constructor: function () {
var me = this;
...
...
// Initialize the form - I know, this might not be the be best coding style here.
me.initComponent();
},
initComponent: function() {
Ext.QuickTips.init();
var ButtonControl1 = this.controlManager.createButton('ButtonControl1');
var ButtonControl2 = this.controlManager.createButton('ButtonControl2');
...
...
// Handler for Btton1 - **I'm not using the var keyword in this declaration**
function Handler1() {
alert('This Works!');
};
// Handler for Btton2 - **I'm using the var keyword in this example**
var Handler2 = function () {
alert('This Works also!');
};
// THIS IS THE KEY PART OF THIS QUESTION - even though the handler functions are declared
// locally (above), clicking the buttons will still execute these. Do the functions
// still exist by chance, and will be garbage collected at some later time, or are they
// actually quaranteed to be there. I'm confused!
ButtonControl1.onClickEventHandler = function () {Handler1();};
ButtonControl2.onClickEventHandler = function () {Handler2();};
// Don't need to worry about this part.
Ext.create('Ext.container.Viewport', {
layout:'border',
style: { position:'relative' },
defaults: {
collapsible: true,
split: true,
bodyStyle: 'padding:0px'
},
items: [
{
collapsible: false,
split: false,
region: 'north',
height: 50,
margins: '0 2 0 2',
bbar: '',
items: [ ]
},
{
collapsible: false,
split: false,
region:'west',
margins: '0 0 0 0',
cmargins: '0 2 0 2',
width: 0,
lbar: [ ]
},
{
collapsible: false,
region:'center',
margins: '0 2 0 2',
layout: {
align: 'stretch',
type: 'hbox'
},
items: [
{
xtype: 'form',
fileUpload: true,
layout: {
align: 'stretch',
type: 'vbox'
},
flex: 1,
items: [
{
xtype: 'container',
height: 550,
layout: {
align: 'stretch',
type: 'hbox'
},
items: [
{
xtype: 'container',
width: 570,
layout: 'vbox',
padding: '5 0 0 0',
style:'background-color:rgb(255, 255, 255);',
items: [
ButtonControl1, ButtonControl2
]
}
]
}
]
}
]
}
]
});
}
});
For primitive variables (like string or int), when the function finished all its local variables are distroyed.
For non-primitive variables, the object created locally (such as array or Ext object) will not be distroyed while there is any other object references to it.
In your example ButtonControl1 and ButtonControl2 are declared outside initComponent.
Inside initComponent function, onClickEventHandler is a handler references to Handler1 and Handler2 functions.
When initComponent run finishes, because ButtonControl1 and ButtonControl2 are not in the scope of initComponent (but in the scope of onReady function), they remain to be alive and therefore also all of the objects they are reference to.
var ButtonControl1 = ....; // this global variable object
var ButtonControl2 = ....; // this global variable object
initComponent: function() {
function Handler1() {
...
};
var Handler2 = function () {
...
};
// ButtonControl1 and ButtonControl2 are declared outside of initComponent.
// Unless these variables will be distroyed, they keep onClickEventHandler references to Handler1 and Handler2 objects and therefore these objects will alive outside the scope of initComponent.
ButtonControl1.onClickEventHandler = function () {Handler1();};
ButtonControl2.onClickEventHandler = function () {Handler2();};
}
Consider the last function in the scope of onReady is initComponent (i.e. there is no any other event handler defined).
So why all these objects will remain be alive after initComponent finishes?
The answer is created 'Ext.container.Viewport' object, which is rendered to document's page and therefore all attached objects and reference objects are alive as well.