I have a Sencha class that extends Ext.draw.Component and it accepts a store of MyModel. I am trying two different methods and I am getting differing, non-satisfactory results.
The First Method
Inside the constructor of the class I read in the store and do the following:
//Inside constructor of the class
this.store = config.store; //config is passed in from the constructor
var me = this;
me.store.each(function (model) {
me.renderTo = model.get('elementToRenderTo');
me.items = [{
type: 'rect',
x: 1.6620979,
y: 52.362183,
radius: 90,
width: 448.10959,
height: 1000,
fill: model.get('color'),
stroke: 'none'
}];
if (me.items) {
Ext.apply(config, { //config is passed in from the constructor
items: me.items
});
}
me.callParent([config]);
}
When I put the last of the code where it is (inside of the store.each), I get an exception:
Uncaught TypeError: Cannot call method 'apply' of undefined
The Second Method
However, if I move the Ext.apply and callParent outside of the store.each, I don't get an expection, but only the last model gets drawn (likely because the me.items is being overwritten on each iteration of the model).
//Inside constructor of the class
this.store = config.store; //config is passed in from the constructor
var me = this;
me.store.each(function (model) {
me.renderTo = model.get('elementToRenderTo');
me.items = [{
type: 'rect',
x: 1.6620979,
y: 52.362183,
radius: 90,
width: 448.10959,
height: 1000,
fill: 'black',
stroke: 'none'
}];
}
if (me.items) {
Ext.apply(config, { //config is passed in from the constructor
items: me.items
});
}
me.callParent([config]);
Is there another way to create a custom Ext.draw.Component that uses a store? What am I missing? The second method doesn't seem right, but I can't get rid of the exception on the first.
There are a few possible issues with this code:
1
this.store = config.store;
Is this the store instance or a string config? The proper way to deal with store configs in a constructor is like so:
this.store = Ext.data.StoreManager.lookup(this.store || 'ext-empty-store');
2
Although you are using me for scope, you probably want to ensure that the scope of each is indeed that of what's outside it so:
me.store.each(function (model) { ... }, this);
3
Regardless of where you do this, you won't be pushing all of the items:
Ext.apply(config, {
items: me.items
});
Because what you do here is you keep override item with me.items.
You shouldn't apply anything to items - it's an array managed by the component itself. You should really add items to it instead:
items.push( me.items )
4
Are you assuming local store only? Because if your store is to be loaded asynchronously - you'd get nothing unless you load the items upon load.
5
What are you actually trying to do here? Have the items loaded from a store? If such is the case you shouldn't do it in the constructor.
You should really look at one of the Ext source files to see how this is to be done. Here's a simplified version of how Ext.panel.Table does it:
Ext.define('Ext.panel.Table', {
extend: 'Ext.panel.Panel',
initComponent: function() {
var me = this,
store = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');
me.mon(store, {
load: me.onStoreLoad,
scope: me
});
},
onStoreLoad: function() {
}
});
Related
Hi Im a novice in ExtJs script, im trying to develop custom multifield, i was able to understand the node creation part, but in the scripting part im unable to catch some of the things like in listener adding scope:this, fn:this.updatehidden i tried to google out the answer but i din get any satisfactory answer. so could any one please explain me the scope:this part
and why we call superclass constructor in the initcomponent,any related resources are also welcome
Thanks in advance
Love to code
Ejst.CustomWidget = CQ.Ext.extend(CQ.form.CompositeField, {
/**
* #private
* #type CQ.Ext.form.TextField
*/
hiddenField: null,
/**
* #private
* #type CQ.Ext.form.ComboBox
*/
allowField: null,
/**
* #private
* #type CQ.Ext.form.TextField
*/
otherField: null,
constructor: function(config) {
config = config || { };
var defaults = {
"border": false,
"layout": "table",
"columns":2
};
config = CQ.Util.applyDefaults(config, defaults);
Ejst.CustomWidget.superclass.constructor.call(this, config);
},
// overriding CQ.Ext.Component#initComponent
initComponent: function() {
Ejst.CustomWidget.superclass.initComponent.call(this);
this.hiddenField = new CQ.Ext.form.Hidden({
name: this.name
});
this.add(this.hiddenField);
this.allowField = new CQ.form.Selection({
type:"select",
cls:"ejst-customwidget-1",
listeners: {
selectionchanged: {
scope:this,
fn: this.updateHidden
}
},
optionsProvider: this.optionsProvider
});
this.add(this.allowField);
this.otherField = new CQ.Ext.form.TextField({
cls:"ejst-customwidget-2",
listeners: {
change: {
**scope:this,
fn:this.updateHidden**
}
}
});
this.add(this.otherField);
},
// overriding CQ.form.CompositeField#processPath
processPath: function(path) {
console.log("CustomWidget#processPath", path);
this.allowField.processPath(path);
},
// overriding CQ.form.CompositeField#processRecord
processRecord: function(record, path) {
console.log("CustomWidget#processRecord", path, record);
this.allowField.processRecord(record, path);
},
// overriding CQ.form.CompositeField#setValue
setValue: function(value) {
var parts = value.split("/");
this.allowField.setValue(parts[0]);
this.otherField.setValue(parts[1]);
this.hiddenField.setValue(value);
},
// overriding CQ.form.CompositeField#getValue
getValue: function() {
return this.getRawValue();
},
// overriding CQ.form.CompositeField#getRawValue
getRawValue: function() {
if (!this.allowField) {
return null;
}
return this.allowField.getValue() + "/" +
this.otherField.getValue();
},
// private
updateHidden: function() {
this.hiddenField.setValue(this.getValue());
}
});
// register xtype
CQ.Ext.reg('ejstcustom', Ejst.CustomWidget);
Class hierarchy, superclass constructor:
You are calling the superclass initComponent function because you want the functionality of the derived class's hierarchy to be available.
For example, if you want to construct an elephant:
First you set some properties, like "big" and "gray" and "female".
Then you construct a mammal with these properties.
The mammal class constructor will itself set some properties, like "has a head", and then call the animal constructor, so if you don't call the mammal constructor from elephant, you don't even get an animal at all!
The animal constructor will then inspect the properties and create an animal.
Then, the mammal class will add details that the animal class didn't cover, e.g. the breast.
After the mammal constructor has finished, the elephant constructor adds the details that the mammal class doesn't cover, for example the trunk.
If you would use the standard ExtJS syntax for this (not sure whether CQ has it's own "standard syntax"), the elephant definition would look like this:
Ext.define('Elephant',{
extend:'Mammal',
initComponent:function() {
var me = this;
// set config properties. Two possible calls:
// "Ext.apply" overwrites config properties already defined by the subclass before constructor has been called
// "Ext.applyIf" only sets config properties that have NOT been set by the subclass!
// Since a MiniElephant subclass may want to set size:"small", we use applyIf here.
Ext.applyIf(me,{
size:'big',
color:'gray'
});
me.callParent(arguments); // <- call constructor of superclass
me.addTrunk(); // <- postprocessing
},
addTrunk:function() {
var trunk = Ext.create('Trunk',{
...
});
me.getHead().add(trunk);
// since addTrunk is called after the mammal constructor has been executed,
// the head is already initialized and the getHead function available!
}
});
Ext.define('Mammal',{
extend:'Animal',
initComponent:function() {
var me = this;
// Every mammal has a head, so we force the property into here using "apply"!
Ext.apply({
hasHead:true,
...
});
me.callParent(arguments); // <- construct animal
me.addBreast(); // <- add breast
},
getHead:function() {
return this.headerEl;
},
...
});
Listener scope:
A listener is a function. Every function has a so-called scope, which is the object that you will get when you access this from inside the function. As long as you don't use this inside your function, the scope doesn't matter to you.
By default, in JavaScript, the scope of a function is the object that the function is attached to, so if you have an object
var listeners = {
update:function() {
console.log(this);
}
};
and if you call the function like this:
listeners.update()
it will log the listeners object to the console; but if you do it like this:
var fn = listeners.update;
fn();
it won't! The scope of a function can be set if you call the function:
listeners.update.call(myScope, firstParameter, secondParameter, ...)
or if you apply it:
listeners.update.apply(myScope, parameterArray)
(Good to remember: Apply takes the Array!)
Since, in ExtJS, the listeners configuration is processed by an Observable mixin, which puts the functions into specially crafted sub-objects, the default scope won't make sense at all to an ExtJS programmer, so they have changed it. And for convenience, ExtJS has added a config property that can be used by the programmer to define his intended scope of the function.
So if you define a panel and add a field inside:
Ext.apply(me, {
items:[{
xtype:'textfield',
listeners:{
update:function() {
console.log(this); // <- returns the panel, because...
},
scope:me // <- we are scoping to the panel!
}
}
});
I have the following class in ExtJS 5.1:
Ext.define('Web.view.guard.apps.conexao.Conexao', {
extend: 'Web.view.guard.apps.App',
width: 400,
height: 600,
statics: {
title: 'Conexão',
icon: 'https://upload.wikimedia.org/wikipedia/commons/a/ac/Approve_icon.svg'
},
});
I want to access the static attributes from the instance context, like title = this.statics.title or something like this, from the controller or the view itself, but I can't find a way in the documentation.
I found through Google a reference to a method statics(), but it doesn't work, this.statics().title returns null, although the statics() method do exists.
How is it possible?
The simple way is
Web.view.guard.apps.conexao.Conexao.title
But this.self is a reference to Web.view.guard.apps.conexao.Conexao, so you can use this.self.title. See http://docs.sencha.com/extjs/5.1/5.1.1-apidocs/#!/api/Ext.Base-property-self and https://fiddle.sencha.com/#fiddle/na0
Ext.define('Web.view.guard.apps.conexao.Conexao', {
extend: 'Web.view.guard.apps.App',
width: 400,
height: 600,
statics: {
title: 'Conexão',
icon: 'https://upload.wikimedia.org/wikipedia/commons/a/ac/Approve_icon.svg'
},
myInstanceMethod: function() {
console.log(this.self.title);
}
});
var conn = new Web.view.guard.apps.conexao.Conexao();
conn.myInstanceMethod();
// 'Conexão'
// 'https://upload.wikimedia.org/wikipedia/commons/a/ac/Approve_icon.svg'
If you want it to be accessible to subclasses, be sure to use inheritableStatics: {} instead.
I have a card layout and on card "activate" event I load the store. To keep the store from loading every time that card is activated I check to see if getCount == 0 and if true, I load the store:
handleActivateGrid: function(){
if(this.myTreeGrid().getStore().getCount() == 0){
this.myTreeGrid().getStore().load();
}
I'm using this same approach elsewhere in my code and it works perfectly fine, the only difference is in this case its a Ext.data.TreeStore.
I debugged and getCount() was undefined, even AFTER the store had loaded.
Is there another approach or method I could use to implement the above?
Thanks
EDIT: just checked the docs, getCount() is not a method that exists for Ext.data.TreeStore, so that explains that
A TreeStore consists of a single root node, with child nodes.
To see if there are nodes under the root node, you can use the following
myTreeGrid().getStore().getRootNode().childNodes.length > 0
See http://docs-origin.sencha.com/extjs/4.2.2/#!/api/Ext.data.NodeInterface-property-childNodes
A more correct approach (since a load may not load any child nodes in theory) is to hookup a load event to the store, when it's initially created. From the load handler, you can set your isLoaded flag wherever it's convenient for your code.
Something like http://www.sencha.com/forum/showthread.php?91923-How-to-check-datastore-is-loaded/page2
var storeLoaded = false;
var store = Ext.create('Ext.data.TreeStore', {
proxy: {
type: 'ajax',
url: 'get-nodes.php'
},
root: {text: 'Ext JS',id: 'src', expanded: true},
listeners: {
load: function() {
storeLoaded = true;
}
}
});
var tree = Ext.create('Ext.tree.Panel', {
store: store,
renderTo: 'tree-div',
height: 300,
width: 250,
title: 'Files'
});
Just tried this code .
myTreeGrid().getStore().getRootNode().getChildAt(0) == undefined
i have simple 'gridpanel' with 'tbar' like this
Ext.define('Ext.abc.grid', {
extend: 'Ext.grid.Panel',
type:1,
tbar:[
{
text:'title1',
class :'a1',
handler:function(type){
if (this.type == 1) { // button not 1
Ext.query(".a2").setDisabled(false);
}
},{
text:'title2',
class :'a2',
handler:function(type){
if (this.type == 1) { // button not 1
Ext.query(".a1").setDisabled(false);
}
}
]
});
i try to add class (a1) to button title1 and the some for title2, but when i get class like
Ext.query(".a1").setDisabled(false);
it's not working
and i can't get type = 1 when i click title1, i using this.type but results is 'button' not 1
How can i do that, thanks
You've got several problems here.
First, see sha's answer, you're getting an array as the result of your call to Ext.query(...).
Second, Ext.query returns Ext.dom.Element, which are Ext objects for representing actual DOM elements like div, img, etc. What you want to access, your buttons, are Ext.Component. You can query components with Ext.ComponentQuery.
Then, you're using this.type in your button handler functions, but when these method get called, this will be the button itself (this can be customized using the scope option), not the container on which you set type: 1.
Edit:
Here's how to make your example work:
Ext.define('Ext.abc.Grid', {
extend: 'Ext.grid.Panel'
,type: 1
,tbar: [{
text: 'title1'
,itemId: 'button1'
// just FYI, here the scope (this) is the window, because we are not
// in a method
,scope: this // so this doesn't work
,handler: function() {
// using ComponentQuery to get a reference to the other components
var grid = this.up('grid'), // by xtype
tbar = this.up(), // by relative position
button2 = tbar.down('#button2'); // by itemId
if (grid.type === 1) {
button2.disable();
}
}
}, {
text: 'title2'
,itemId: 'button2'
,handler: function() { ... }
}]
});
Now, reading your mind, here's what I think you actually want to do:
Ext.define('Ext.abc.Grid', {
extend: 'Ext.grid.Panel'
,type: 1
,tbar: [{
text: 'title1'
,itemId: 'button1'
}, {
text: 'title2'
,itemId: 'button2'
}]
// reading in your mind, I guess, this is what you really want to do:
,initComponent: function() {
this.callParent();
if (this.type === 1) {
this.down('#button2').disable();
} else {
this.down('#button1').disable();
}
}
});
Ext.query returns you an array http://docs.sencha.com/extjs/4.1.3/#!/api/Ext-method-query
You can't simply call setDisabled() on an array. You need to loop through all elements.
So I'm trying to put items dynamically to the panel that has slidenavigation feature:
// FlyoutNavigation.js
Ext.define("APN.view.FlyoutNavigation", {
id: "flyoutNavigationPanel",
extend: 'Ext.ux.slidenavigation.View',
Here is the initialisation of the view in another view:
// MainViewContainer.js
this.home = "Some var"
this.flyout = Ext.create('APN.view.FlyoutNavigation', {
id: 'flyoutNavigationPanel',
home: this.home
});
Than I'm trying to use this variable in the this.config.items section, however that doesn't work, it seems that Sencha compiles everything first and than initialiases the components, I might be wrong, I'm really new to Sencha Framework.
So here is the view where the home variable is used:
Ext.define("APN.view.FlyoutNavigation", {
id: "flyoutNavigationPanel",
extend: 'Ext.ux.slidenavigation.View',
xtype: 'flyoutnavigation',
requires: [
... heaps of things omitted ...
],
initialize: function () {
this.callParent();
this.setupDynamicItems();
},
config: {
items: [
{
itemId: 'nav_home',
id: 'homeView',
items: [{
xtype: 'articlelist',
id: 'latestNews',
feedUrlName: this.home, // - that's the place where UNDEFINED occurs
flex: 1
}
],
},
So this.home is undefined...
One possible solution
Comming from this question: How to dynamically create xtype templates in Sencha Touch
I decided to put all the code in this.config.items.add({ ... my items ... }) however Ext.ux.slidenavigation.View looks like gave me the BUG! :( as the initialise method occurs after the binding methods on items of FlyoutNavigation view.
Here is the message from of the bug: Uncaught TypeError: Cannot read property 'raw' of undefined View.js:310 which is basically this line: if (Ext.isFunction(item.raw.handler)) {
So my questions would be
How to get the instance variable in the config.items section? If that's possible, than all is OK
Or do you know the work around of this issue?
Thanks
I don't think you can use this.config when defining the class, instead you can use initialize function as I told you earlier. So you should be able to do this:
initialize : function() {
var me = this;
var home = me.config.home;
me.add({
itemId: 'nav_home',
id: 'homeView',
items: [{
xtype: 'articlelist',
id: 'latestNews',
feedUrlName: home,
flex: 1
}
],
});
}
OR if you have defined homeView in parent class, you can do this:
initialize : function() {
var me = this;
var home = me.config.home;
me.down('#homeView').add({
xtype: 'articlelist',
id: 'latestNews',
feedUrlName: home,
flex: 1
});
}