Modifying items in initComponent() - extjs

I create some items in initComponent()
Problem is, this.items somehow referes to the class variable, not to the instance variable.
So when I make two instances, I end up with two buttons.
items: [],
initComponent: function() {
this.items.push( { xtype: 'button', ... }) ;
this.callParent( arguments );
}
Since I have to use push, every time new elements get pushed in.
Is there some instance equivalent to this.items where I can modify the definition before the button gets created or do I have to check for duplicates manually?

You shouldn't return this.callParent( arguments );
Just this is enough:
initComponent: function() {
var me = this;
me.items = { xtype: 'button', ... }; //Are you sure items is filled up here?
me.callParent();
}
Also if you're writing your own 'component' and you want to pass parameters in the Ext.create I always do the following:
constructor: function (config) {
var me = this;
Ext.apply(me, config);
me.callParent();
}
This will overwrite your items declaration in your class with the one you hand in the Ext.create()

You could overload the constructor and tweak the config in there:
constructor: function(config)
{
config.items.someButton = { xtype: 'button', ... };
this.callParent([config]);
}

Related

How to count a global variable?

LoginController:
var AppConstants = Ext.widget("AppConstants"); AppConstants.setGLOBAL_id_user(id_user);
App:
var AppConstants = Ext.widget("AppConstants"); console.log(AppConstants.getGLOBAL_id_user());
Console: (an empty string)
How to count a global variable?
Ext.widget() creates a new instance of a certain class every time you call it.
What you want is something that does not create new instances.
For minimal change to your code, you could do Ext.AppConstants = Ext.widget('Appconstants') in Application.init() and then access Ext.AppConstants wherever you use Ext.widget('Appconstants') right now.
The best way you could use singleton class in ExtJS. Using a singleton class you can access all the variable in throughout application whenever you required.
In this FIDDLE, I have created a demo using singleton class. Hope this will help/guide you to achieve your requirement.
CODE SNIPPET
Ext.application({
name: 'Fiddle',
launch: function () {
//{singleton: true,} When set to true, the class will be instantiated as singleton.
Ext.define('AppConstants', {
alternateClassName: 'AppConstants',
singleton: true,
config: {
GLOBAL_id_user: 'Demo_123'
},
constructor: function (config) {
this.initConfig(config);
}
});
Ext.create({
xtype: 'panel',
title: 'Demo',
bodyPadding: 15,
items: [{
xtype: 'button',
text: 'Set value',
margin: '0 15',
handler: function () {
//we set value using setter of singleton.
Ext.Msg.prompt('Set GLOBAL_id_user', 'Please enter GLOBAL_id_user value', function (btn, text) {
if (btn == 'ok') {
AppConstants.setGLOBAL_id_user(text);
}
});
}
}, {
xtype: 'button',
text: 'Get value',
handler: function () {
//we get value using getter of singleton.
Ext.Msg.alert('GLOBAL_id_user value is ', AppConstants.getGLOBAL_id_user());
}
}],
renderTo: Ext.getBody()
});
}
});
Just create a global singleton
Ext.define("MyApp.globals", {
singleton: true,
id_user: 0
});
Then access it using MyApp.globals.id_user
http://docs.sencha.com/extjs/6.5.3/classic/Ext.Class.html#cfg-singleton

ExtJS Class constructor

I have a custom class created
Ext.define('MyFormPanel, {
extend: 'Ext.form.Panel',
field1: null,
field2: null
constructor: function (config) {
this.createFields();
config.items.splice(0, 0, [
this.field1,
this.field2
]
this.callParent([config]);
}
});
However it will not add my fields to the form. However, if I swap at the config.items.splice for
config.items[0] = this.field1;
config.items[1] = this.field2;
The form panel is created correctly.
My question is am I using the splice command incorrectly? Is there an alternative?
You are not using splice correctly. It takes the elements to add as separate arguments, not an array
There are other problems:
Name of class in missing a quote to close it
Your call to splice is not closed, missing a )
You have to make sure items is an array before you can call splice on it
Config options have not been copied to this yet.
Try the following https://fiddle.sencha.com/#fiddle/8ij
Ext.define('MyFormPanel', {
extend: 'Ext.form.Panel',
field1: null,
field2: null
constructor: function (config) {
this.createFields();
config.items = config.items || [];
config.items.splice(0, 0, config.field1, config.field2);
this.callParent([config]);
}
});
You should not override constructor. You should override initComponent
Ext.define('MyFormPanel', {
extend: 'Ext.form.Panel',
field1: null,
field2: null
initComponent: function () {
this.createFields();
this.items = this.items || [];
this.items.splice(0, 0, this.field1, this.field2);
this.callParent();
}
});
From a readability perspective it makes it very hard to determine what your constructor is trying to do. Some improvements:
Move all the legwork to create fields into the initComponent as Juan recommends. This includes any dependencies on the fields in the current constructor to the parent initComponent.
You need to start invoking the super method with the special JavaScript reserved word "arguments" (as opposed to [config]) this will call another method with the argument signature which invoked the current method.
Pretty sure the only time you should need to override constructor instead of initComponent is when you are working from custom classes which don't extend off any component.
Why aren't you able to use the Panel#add() method to add your fields in initComponent? Splice feels hacky given the Ext API's allow you to add fields directly.
Ext.define('MyFormPanel, {extend: 'Ext.form.Panel',
initComponent: function () {
this.callParent(arguments);
this.createFields();
},
createFields : function() {
this.field1 = Ext.create('YourField1', {});
this.add(this.field1);
this.field2 = Ext.create('YourField2', {});
this.add(this.field2);
}});

In ExtJS components how to forward config: {} items to sub components

I am trying to write a reusable item selection panel where the user has a grid with items he can choose from and a small text field that he can use to filter the content of the grid. Right now the (simplified) view code looks like this and works.
Ext.define('MyApp.view.items.ItemSelectorPanel', {
extend: 'Ext.panel.Panel',
require: 'MyApp.view.items.SimpleItemGrid',
alias: 'widget.ItemSelectorPanel',
layout: 'form',
config: {
itemStore: false
},
constructor: function(config) {
this.initConfig(config);
this.superclass.constructor.call(this, config);
this.add([
{
fieldLabel: 'Filter',
name: 'filter'
},
{
xtype: 'SimpleItemGrid',
collapsible: true,
store: this.getItemStore()
}
]);
return this;
}
});
As you can see the ItemSelectorPanel uses the config property to expose an interface where the calling site can specify which item store to use.
Calling site (in this case the panel is added to a TabPanel):
var panelToAdd = {
xtype: 'panel',
title: 'New Selection',
closable: true,
padding: 10,
items: [{
title: 'Select node',
xtype: 'ItemSelectorPanel',
itemStore: itemStore
}]
};
Now, I love the declarative style of ExtJS 4 and how it helps to follow the MVC pattern. I would prefer to have the least amount of code possible in the views. Unfortunately this does not work:
Ext.define('MyApp.view.items.ItemSelectorPanel', {
/* ... same as above ... */
constructor: function(config) {
this.initConfig(config);
this.superclass.constructor.call(this, config);
return this;
},
items: [
{
fieldLabel: 'Filter',
name: 'filter'
},
{
xtype: 'SimpleItemGrid',
collapsible: true,
store: this.getItemStore // <-- interesting part
}
]
});
Is there a way to expose the config of a nested/sub component via the config property of the parent property in a declarative manner?
First something in general
Never add an object outside a function within a class definition unless you exactly know what you are going to do. Cause if you do so all instances will share the same instance of that object. I think I do not need to mention where this leads to...
If you have a need to place a object there you should clone it within the constructor.
To your code
I dunno what this.initConfig(config); does but the config variable is not the one from your class, it is the one from the constructor argument. I recommend you also to use initComponent() for initialization instead of the constructor() unless you have a defined need for using the constructor, which in your case you don't seem to have.
Also a 'config' is not forwarded cause it don't get executed up->bottom but bottom->up where a config get's hand up and all other properties are (already) inherited.
I still do not exactly know what your goal is, therefore I cannot give you any advice how you should do this but I can say for sure that the way you do it will lead to problems.
Edit
I still not sure that I have fully understand your needs but the following should work (if you need the listeners too you might take a look at the Ext.util.Bindable mixin)
Ext.define('MyApp.view.items.ItemSelectorPanel', {
extend: 'Ext.panel.Panel',
require: 'MyApp.view.items.SimpleItemGrid',
alias: 'widget.ItemSelectorPanel',
layout: 'form',
initComponent: function() {
// Initialize the store (might be a instance or a storeId)
var store;
if (this.itemStore) {
store = Ext.data.StoreManager.lookup(store);
}
this.itemStore = store || null;
// Add is not valid (at least not before the parent inits are executed)
this.items = [{
fieldLabel: 'Filter',
name: 'filter'
}, {
xtype: 'SimpleItemGrid',
collapsible: true,
store: this.getItemStore()
}];
this.callParent(arguments);
},
getItemStore: function() {
return this.itemStore;
}
});
No, you can't do it in the way you've described. The reason is pretty simple, let's take this as an example:
Ext.define('MyClass', {
someProp: this.foo(),
foo: function(){
return bar;
}
});
Here, we call the define() method and we pass it an object as the configuration. As such, the whole object (including the call to foo()) is evaluated before it's even passed to define, so the class/method doesn't even exist at that point.
Even if you could do that, here's also the complication that foo is an instance method on the class, but the way you're attempting to call it is as though it's a static method.
So, the answer is, you'll need to use some kind of method to do so, initComponent is typically preferred over the constructor.
You can define items in declaration of your class but you cannot call any method from your class at time of declaration. To solve it, define only items without store and than use initComponent method to set store for your view.
I didn't see an answer that addressed the original question. Here is what I've found to work ...
Creating an instance of myClass, passing in a config 'foo' with value 'bar'
var myClassInstance = Ext.create('MyApp.view.myClass', {
foo: 'bar'
});
myClass is defined as follows :
Ext.define('MyApp.view.myClass', {
extend: 'Ext.container.Container',
alias: 'widget.myclass',
config: {
foo: 'defaultVal'
},
constructor: function(configs) {
this.callParent(arguments); //create class, calls initComponent
var try1 = getFoo(); //try1 is 'defaultVal'
this.initConfig(configs); //initializes configs passed in constructor
var try2 = getFoo(); //try2 is 'bar'
},
initComponent: function() {
//myClass declaration here ...
this.callParent();
}

Reusable Action in Ext JS MVC

I have a Grid Panel with a toolbar and an context menu.
The toolbar has a edit button and the context menu has a edit menu item.
Both shares the same properties (text, icon and handler)
Ext has something called Action which makes it possible to share functionality etc. between components, but til now I have had no success getting it to work in the MVC architecture
(I am using the new MVC architecture in 4.0)
My Action class looks like this:
Ext.define( 'App.action.EditAction', {
extend: 'Ext.Action',
text: 'Edit',
handler: function()
{
Ext.Msg.alert('Click', 'You did something.');
},
iconCls: 'icon-edit-user' ,
});
And in my context menu
requires: ['App.action.EditAction'],
initComponent: function()
{
var editUser = new App.action.EditAction();
this.items = [
editUser,
{
// More menuitems
}
...
];
this.callParent(arguments);
When running the code I get "config is undefined" in the console.
Can anyone point out what I am doing wrong?
Thanks in advance,
t
Passing an empty config to your constructor will avoid the error, but have unwanted consequences later because, unfortunately, the base class (Ext.Action) relies on this.initialConfig later on. For example, if you called editUser.getText() it would return undefined instead of the expected 'Edit'.
Another approach is to override your constructor to allow a no-arg invocation and apply your overridden configuration:
Ext.define( 'App.action.EditAction', {
extend: 'Ext.Action',
text: 'Edit',
constructor: function(config)
{
config = Ext.applyIf(config || {}, this);
this.callParent([config]);
},
handler: function()
{
Ext.Msg.alert('Click', 'You did something.');
},
iconCls: 'icon-edit-user' ,
});
As per Ext.Action constructor
constructor : function(config){
this.initialConfig = config;
this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
this.items = [];
}
You must supply config not to get config is undefined exception in the second line (precisely in config.itemId part).
Updating your code as var editUser = new App.action.EditAction({}); should help(passing new empty object as config).
Surely, you could add some properties to the config object too.

Extjs How to initialize new elements when extending - without losing scope

I am trying to get better at extending the classes of Extjs, and my evolvement have lead me to this problem:
I have extended an Ext.Panel and I want my extension to have a bottom toolbar with one button as default.
myPanel = Ext.extend(Ext.Panel, {
method: function () {
return 'response!';
},
bbar: new Ext.Toolbar({
items:
[
{
xtype: 'button',
text: 'Hit me!',
handler: function (button, event) {
alert(this.method());
},
scope: this
}
]
})
});
What I haven't learnt yet is why this is not allowed. this is pointing at the global scope and not my extended panel - thus .method() is undefined inside the handler function.
You're defining the bbar on the prototype rather than on a specific object.
Override initComponent and move the bbar definition inside it.
myPanel = Ext.extend(Ext.Panel, {
method: function () {
return 'response!';
},
initComponent: function() {
var bbar = new Ext.Toolbar({
items:
[
{
xtype: 'button',
text: 'Hit me!',
handler: function (button, event) {
alert(this.method());
},
scope: this
}
]
});
// Config object has already been applied to 'this' so properties can
// be overriden here or new properties (e.g. items, tools, buttons)
// can be added, eg:
Ext.apply(this, {
bbar: bbar
});
// Call parent (required)
myPanel.superclass.initComponent.apply(this, arguments);
// After parent code
// e.g. install event handlers on rendered component
}
});
See http://www.sencha.com/learn/Manual:Component:Extending_Ext_Components for a template you can use when extending components
You have to keep in mind that the anonymous object that is the first element of the items array is created in the same scope as the one in which Ext.extend(... is executed.
If you had this:
var o = { 'a': a, 'b': b, scope: this };
you would expect that o.a, o.b, and o.scope would have the same values as a, b, and this in the current scope. Here, it's a little more complex because you are creating an object while creating an array while creating an object, etc., but the reasoning is the same.
What you should do instead is define this.bbar inside the constructor:
myPanel = Ext.extend(Ext.Panel, {
method: function () {
return 'response!';
},
constructor: function(config) {
this.bbar = new Ext.Toolbar({
items:
[
{
xtype: 'button',
text: 'Hit me!',
handler: function (button, event) {
alert(this.method());
},
scope: this
}
]
});
myPanel.superclass.constructor.apply(this, arguments);
}
});

Resources