Define a reusable component - extjs4

1-I used following code to define a reusable grid,
but when I make instance, no config in class definition either do not have effect of break the code. What is the reason?
3- Is there any restriction in class config declaration?
2- How I can make some default columns in grid class and add some more columns to its objects?
Thanks
Ext.define("IBS.users.Grid", {
extend: "Ext.grid.Panel",
config:{
selType:'checkboxmodel', //not work
dockedItems:[/* items */], //break
multiSelect:true,
features: [
{
groupHeaderTpl: '{name}',
ftype: 'groupingsummary'
},
{
ftype:'filters',
encode: false, // json encode the filter query
local: true
}
],
viewConfig: { //not work
stripeRows: true,
filterable:true,
loadMask: false
},
listeners : {
itemdblclick: function(dv, record, item, index, e) {
console.log(arguments);
}
}
},
constructor:function(config) {
this.callParent(arguments);
this.initConfig(config);
// this.self.instanceCount++;
}
});

1-I used following code to define a reusable grid, but when I make instance, no config in class definition either do not have effect of break the code. What is the reason?
I can answer why your config doesn't have effect. Because config which is being passed into cunstructor is not your default config. You have to apply your default config in order to make default config to have effect:
constructor:function(config) {
config = Ext.applyIf(config, this.config);
this.callParent(arguments);
this.initConfig(config);
}
However, I don't know why dockedItems:[/* items */] breaks the code. Maybe you have syntax or logical errors somewhere within /* items */.
2- How I can make some default columns in grid class and add some more
columns to its objects?
That is easy. Just override your initComponent function:
Ext.define("IBS.users.Grid", {
extend: "Ext.grid.Panel",
// ...
initComponent : function(){
if (!this.columns) {
// default columns:
this.columns = [{
dataIndex: 'dkjgkjd',
// ...
}];
// if we passed extraColumns config
if (this.extraColumns)
for (var i=0; i < this.extraColumns.length; i++)
this.columns.push(this.extraColumns[i]);
}
this.callParent(arguments);
},
// ...
});
3- Is there any restriction in class config declaration?
I'm not aware of any. However, I wouldn't recommend to declare object configs in class definition. For example:
Ext.define("IBS.users.Grid", {
extend: "Ext.grid.Panel",
bbar: Ext.create('Ext.toolbar.Toolbar', // ...
// ...
});
It will be ok with first instance of the class. But when you create the second instance it's bbar refers to the same object as the first instance. And therefore bbar will disappear from the first grid.
Instead declare object configs in initComponent.

Related

rally iteration combobox returns empty

I'm new to rally app SDK and trying to do the tutorials (from Youtube and from rally site)
when I'm trying to create an iterationComboBox the object is created but with no values ("There are no Iterations defined").
i tried to run both the video tutorial code from github (session_4_interactive_grid)
// Custom Rally App that displays Defects in a grid and filter by Iteration and/or Severity.
//
// Note: various console debugging messages intentionally kept in the code for learning purposes
Ext.define('CustomApp', {
extend: 'Rally.app.App', // The parent class manages the app 'lifecycle' and calls launch() when ready
componentCls: 'app', // CSS styles found in app.css
defectStore: undefined, // app level references to the store and grid for easy access in various methods
defectGrid: undefined,
// Entry Point to App
launch: function() {
console.log('our second app'); // see console api: https://developers.google.com/chrome-developer-tools/docs/console-api
this.pulldownContainer = Ext.create('Ext.container.Container', { // this container lets us control the layout of the pulldowns; they'll be added below
id: 'pulldown-container-id',
layout: {
type: 'hbox', // 'horizontal' layout
align: 'stretch'
}
});
this.add(this.pulldownContainer); // must add the pulldown container to the app to be part of the rendering lifecycle, even though it's empty at the moment
this._loadIterations();
},
// create iteration pulldown and load iterations
_loadIterations: function() {
this.iterComboBox = Ext.create('Rally.ui.combobox.IterationComboBox', {
fieldLabel: 'Iteration',
labelAlign: 'right',
width: 300,
listeners: {
ready: function(combobox) { // on ready: during initialization of the app, once Iterations are loaded, lets go get Defect Severities
this._loadSeverities();
},
select: function(combobox, records) { // on select: after the app has fully loaded, when the user 'select's an iteration, lets just relaod the data
this._loadData();
},
scope: this
}
});
this.pulldownContainer.add(this.iterComboBox); // add the iteration list to the pulldown container so it lays out horiz, not the app!
},
// create defect severity pulldown then load data
_loadSeverities: function() {
this.severityComboBox = Ext.create('Rally.ui.combobox.FieldValueComboBox', {
model: 'Defect',
field: 'Severity',
fieldLabel: 'Severity',
labelAlign: 'right',
listeners: {
ready: function(combobox) { // this is the last 'data' pulldown we're loading so both events go to just load the actual defect data
this._loadData();
},
select: function(combobox, records) {
this._loadData();
},
scope: this // <--- don't for get to pass the 'app' level scope into the combo box so the async event functions can call app-level func's!
}
});
this.pulldownContainer.add(this.severityComboBox); // add the severity list to the pulldown container so it lays out horiz, not the app!
},
// Get data from Rally
_loadData: function() {
var selectedIterRef = this.iterComboBox.getRecord().get('_ref'); // the _ref is unique, unlike the iteration name that can change; lets query on it instead!
var selectedSeverityValue = this.severityComboBox.getRecord().get('value'); // remember to console log the record to see the raw data and relize what you can pluck out
console.log('selected iter', selectedIterRef);
console.log('selected severity', selectedSeverityValue);
var myFilters = [ // in this format, these are AND'ed together; use Rally.data.wsapi.Filter to create programatic AND/OR constructs
{
property: 'Iteration',
operation: '=',
value: selectedIterRef
},
{
property: 'Severity',
operation: '=',
value: selectedSeverityValue
}
];
// if store exists, just load new data
if (this.defectStore) {
console.log('store exists');
this.defectStore.setFilter(myFilters);
this.defectStore.load();
// create store
} else {
console.log('creating store');
this.defectStore = Ext.create('Rally.data.wsapi.Store', { // create defectStore on the App (via this) so the code above can test for it's existence!
model: 'Defect',
autoLoad: true, // <----- Don't forget to set this to true! heh
filters: myFilters,
listeners: {
load: function(myStore, myData, success) {
console.log('got data!', myStore, myData);
if (!this.defectGrid) { // only create a grid if it does NOT already exist
this._createGrid(myStore); // if we did NOT pass scope:this below, this line would be incorrectly trying to call _createGrid() on the store which does not exist.
}
},
scope: this // This tells the wsapi data store to forward pass along the app-level context into ALL listener functions
},
fetch: ['FormattedID', 'Name', 'Severity', 'Iteration'] // Look in the WSAPI docs online to see all fields available!
});
}
},
// Create and Show a Grid of given defect
_createGrid: function(myDefectStore) {
this.defectGrid = Ext.create('Rally.ui.grid.Grid', {
store: myDefectStore,
columnCfgs: [ // Columns to display; must be the same names specified in the fetch: above in the wsapi data store
'FormattedID', 'Name', 'Severity', 'Iteration'
]
});
this.add(this.defectGrid); // add the grid Component to the app-level Container (by doing this.add, it uses the app container)
}
});
and the code from Rally site (https://help.rallydev.com/apps/2.0rc2/doc/#!/guide/first_app).
// Custom Rally App that displays Defects in a grid and filter by Iteration and/or Severity.
//
// Note: various console debugging messages intentionally kept in the code for learning purposes
Ext.define('CustomApp', {
extend: 'Rally.app.App', // The parent class manages the app 'lifecycle' and calls launch() when ready
componentCls: 'app', // CSS styles found in app.css
launch: function() {
this.iterationCombobox = this.add({
xtype: 'rallyiterationcombobox',
listeners: {
change: this._onIterationComboboxChanged,
ready: this._onIterationComboboxLoad,
scope: this
}
});
},
_onIterationComboboxLoad: function() {
var addNewConfig = {
xtype: 'rallyaddnew',
recordTypes: ['User Story', 'Defect'],
ignoredRequiredFields: ['Name', 'ScheduleState', 'Project'],
showAddWithDetails: false,
listeners: {
beforecreate: this._onBeforeCreate,
scope: this
}
};
this.addNew = this.add(addNewConfig);
var cardBoardConfig = {
xtype: 'rallycardboard',
types: ['Defect', 'User Story'],
attribute: 'ScheduleState',
storeConfig: {
filters: [this.iterationCombobox.getQueryFromSelected()]
}
};
this.cardBoard = this.add(cardBoardConfig);
},
_onBeforeCreate: function(addNewComponent, record) {
record.set('Iteration', this.iterationCombobox.getValue());
},
_onIterationComboboxChanged: function() {
var config = {
storeConfig: {
filters: [this.iterationCombobox.getQueryFromSelected()]
}
};
this.cardBoard.refresh(config);
}
});
both give me an empty iteration box.
i'm getting user stories data when running code from session 3 on the video,by creating a store of user stories. I googled it and searched here for duplicates but with no successso far, so what can be the issue?
Thanks!
I copied the code you posted, both apps, without making any changes, ran the apps and the iteration box was populated in both cases. It's not the code.
Maybe if you are getting "There are no Iterations defined" there are no iterations in your project?
The second code you posted which you copied from the example in the documentation has a bug in it and even though the iteration combobox is populated, the cards do not show on a board. DevTools console has error: "Cannot read property 'refresh' of undefined".
I have a working version of this app in this github repo.

How can I get a view in Sencha Touch 2 from within a controller by calling a function automatically generated by the ref config?

I have defined a controller in Sencha, that includes a refs attribute referencing my view, yet whenever I call the automatically generated "get" function to get the view, based on the refs attribute, it returns undefined. Here is my example:
I have the following controller in app/controller/Locals.js:
Ext.define('MobileUnion.controller.Locals', {
extend: 'Ext.app.Controller',
// Base class methods.
launch: function () {
this.callParent(arguments);
},
init: function () {
this.callParent(arguments);
},
config: {
refs: {
localsEditorView: 'localseditorview',
},
control: {
localsEditorView: {}
}
},
slideUpTransition: { type: 'cover', direction: 'up' },
onEditLocalsCommand: function() {
this.activateLocalsEditor();
},
activateLocalsEditor: function() {
var localsEditorView = this.getLocalsEditorView();
console.log(localsEditorView); // Returns "undefined" to console.
Ext.Viewport.animateActiveItem(localsEditorView, this.slideUpTransition);
}
});
I have the following view in app/views/LocalsEditor.js:
Ext.define('MobileUnion.view.LocalsEditor', {
extend: 'Ext.Panel',
alias: 'widget.localseditorview',
config: {
html: 'This is the new view which should show up on top!'
},
});
So, in the above example, if I call this.getLocalsEditorView() from within my controller, I get "undefined" even though I set a refs attribute as localsEditorView: 'localseditorview' and I defined MobileUnion.view.LocalsEditor to include an alias of widget.localseditorview. I feel like I should get the view when I do this.
By the way, I did define the view in the views attribute of my app.js, so that's not it.
Further information: there's no actual error being returned in my webkit console. Just the call to console.log() noted above in my controller returns undefined, rather than the view object.
Question: What do I have to do to make this function return the view, and not undefined? Any help would be appreciated. I've looked to make sure it's not just a typo; it does not seem to be.
The "refs" is to create references to existing components. So far all you've done is declare a class, from what you've posted you've never instanced it anywhere.
You might want to read the docs about autoCreate here:
http://docs.sencha.com/touch/2-1/#!/api/Ext.app.Controller-cfg-refs

How to add a custom update on a pull refresh?

In the standard "Pull to Refresh" plugin, the list store gets updated. However, I have two lists and I need to update a different store for my detail list. How can I override the update event and reload my other store? I tried adding a simple listener but it's not firing.
[Update]
I got this snippet from the Sencha site to work:
plugins: [
{
xclass: 'Ext.plugin.PullRefresh',
pullRefreshText: 'Pull down for more new Events!',
refreshFn: function(plugin) {
console.log( "I'm pulled" );
}
}
]
Original code:
Ext.define('SenchaFiddle.view.ListView', {
extend: 'Ext.dataview.List',
xtype: 'main-list',
config: {
plugins: [
'pullrefresh',
{
pullRefreshText: 'Do it!',
type: 'listpaging',
// Don't offer "Load More" msg
autoPaging: false,
refreshFn: function() {
console.log("Boom");
},
listeners: {
'updatedata': function(plugin, list) {
console.log("Getting the data");
}
}
}
],
layout: 'fit',
width: 300,
itemTpl: '{text}'
}
});
In Sencha Touch 2.2, they have removed the refreshFn config from Ext.util.PullRefresh. I have successfully implemented a custom refreshFn with the new version of Sencha Touch by overriding the fetchLatest function inside Ext.util.PullRefresh like so...
Ext.define('MyApp.overrides.PullRefreshOverride', {
override: 'Ext.plugin.PullRefresh',
fetchLatest: function() {
var list = this.getList();
switch(list.getItemId()) {
case "list1":
this.updateStore1();
break;
case "list2":
this.updateStore2();
break;
}
this.callParent(arguments);
},
//My own custom function to add to the plugin
updateStore1: function() {
//Code to update store 1
},
//My own custom function to add to the plugin
updateStore2: function {
//Code to update store 2
}
});
Having a look at Ext.plugin.PullRefresh definition in sencha-touch-all-debug, I see this config:
/*
* #cfg {Function} refreshFn The function that will be called to refresh the list.
* If this is not defined, the store's load function will be called.
* The refresh function gets called with a reference to this plugin instance.
* #accessor
*/
refreshFn: null,
It might be a good idea that you can achieve what you need through refreshFn config.
For those who need the refreshFn back, there is a PullRefreshFn extension for PullRefresh.
I needed PullRefresh to get triggered by a Panel, rather than a List or Dataview and I also needed to manually load and set data to my Dataview upon user triggering the PullRefresh.
For this I needed the refreshFn config function that existed prior to Sencha 2.2, so here is my implementation.
PullRefreshFn (Modified)
Ext.define('Ext.plugin.PullRefreshFn', {
extend: 'Ext.plugin.PullRefresh',
alias: 'plugin.pullrefreshfn',
requires: ['Ext.DateExtras'],
config: {
/**
* #cfg {Function} refreshFn The function that will be called to refresh the list.
* If this is not defined, the store's load function will be called.
*/
refreshFn: null
},
fetchLatest: function() {
if (this.getRefreshFn()) {
this.getRefreshFn().call();
} else {
var store = this.getList().getStore(),
proxy = store.getProxy(),
operation;
operation = Ext.create('Ext.data.Operation', {
page: 1,
start: 0,
model: store.getModel(),
limit: store.getPageSize(),
action: 'read',
sorters: store.getSorters(),
filters: store.getRemoteFilter() ? store.getFilters() : []
});
proxy.read(operation, this.onLatestFetched, this);
}
}
});
My Controller
Ext.define('myApp.controller.MyController', {
extend: 'Ext.app.Controller',
requires: ['Ext.plugin.PullRefreshFn'],
...
// More code
...
// Binds the Pull Refresh to myPanel view item.
// myPanel is a panel. Not list nor dataview.
setPullRefresh: function () {
var me = this;
// We get reference to myPanel and
// we set PullRefreshFn
this.getMyPanel().setPlugins([{
xclass: 'Ext.plugin.PullRefreshFn',
docked: 'top',
// We set autoSnapBack to false,
// as we are going to trigger this manually
autoSnapBack: false,
// refreshFn will be called upon user releasing for refresh.
refreshFn: function() {
// This is a custom function that sets data to our dataview list.
// When it's done setting data, we trigger the snapBack.
me.populateMylist(function () {
me.getMyPanel().getPlugins()[0].snapBack(true);
});
}
}]);
}
});

Extending controller in ExtJS 4 MVC application

I building my ExtJS 4 application following the MVC structure. I want to make an extendable grid MyGrid with some functionality that I can reuse several times. Therefore, I guess, it should have its own controller which is also extended, so that the functionality is inherited. How is this properly done?
In the code below I illustrate how I extend the controller MyGrid with MyExtendedGrid. I realize that I'm overriding the init function in the MyGrid controller, so that it is never called. Is the problem simply solved by calling the "super" init in MyGrid from MyExtendedGrid init, or merge the control objects? Is that the proper way to do this in the MVC spirit? If so, how?
controller/MyGrid.js :
Ext.define('App.controller.MyGrid', {
extend: 'Ext.app.Controller',
refs: [
{
ref: 'myGridView',
selector: 'mygrid'
}
],
init: function() {
var me=this;
me.control({
'mygrid textfield[name=searchField]': {
change: function() {
var view = me.getMyGridView();
// Do something with view
}
}
});
}
});
controller/MyExtendedGrid.js :
Ext.define('App.controller.MyExtendedGrid', {
extend: 'App.controller.MyGrid',
views: [
'grids.MyExtendedGrid'],
refs: [
{
ref: 'myExtendedGridView',
selector: 'myextendedgrid'
}
],
init: function() {
var me=this;
me.control({
'myextendedgrid': {
// Some control code
// Using getMyExtendedGridView()
}
});
}
});
view/grids/MyGrid.js :
Ext.define('App.view.grids.MyGrid', {
extend: 'Ext.grid.Panel',
alias : 'widget.mygrid',
requires: [
],
store: '', // Not defined here
columns: [ ], // Not defined here
initComponent: function() {
var me = this;
me.tbar = [
'Search',
{
xtype: 'textfield',
name: 'searchField',
hideLabel: true,
width: 150
}
];
me.callParent(arguments);
}
});
view/grids/MyExtendedGrid.js :
Ext.define('App.view.grids.MyExtendedGrid', {
extend: 'App.view.grids.MyGrid',
alias : 'widget.myextendedgrid',
store: 'MyStore',
columns: [
// ...
],
initComponent: function() {
var me = this;
me.bbar = [
//...
];
me.callParent(arguments);
}
});
It's actually a bit trickier...
Here is what we did in our application (we have exact same situation - some kind of base controller, that is reused in many different places)
Keep init function in base controller.
Define common base method in this base controller (like gridRendered - where you need to do something for all controllers all the time).
Subscribe to the events in all child controllers but subscribe events using methods of base controller. It won't work otherwise - base controller doesn't have proper refs to properly subscribed to events.
I can post couple source snippets, but I think it's pretty straightforward.
Ok, after some thinking I decided on the following solution, which has the advantage that mygrid does not need to know about myextendedgrid.
I extend my gridview as in the question.
I gave the base grid its own controller for doing common functionality, for instance deleteButton.setDisable(false) when something is selected in the grid.
Next, I remind myself that using refs:[ (for instance with selector: 'mygrid') would ambiguously point to both instances of the base class any extended instances. When using me.control({ I instead get the relevant grid by traversing from the activated element using up:
me.control({
'mygrid textfield[name=searchField]': {
change: function(searchfield) {
var grid=searchfield.up('mygrid'); // (mygrid or myextendedgrid!)
// Do something with view
}
}
...
The extended grid I give its own controller and here I could use refs. I don't extend the controller from the MyGrid class (but rather from 'Ext.app.Controller'), unless I would like to use functions or variables from the MyGrid controller. All the controllers are started from app.js using:
Ext.application({
controllers: [
'MyGrid'
'MyExtendedGrid',
],
...
In order to get the grid when rows are selected in the grid, I stored the grid in the selection model as below:
In controller/MyGrid.js :
me.control({
'mygrid': {
afterrender: function(grid) {
var selModel=grid.getSelectionModel();
selModel.myGrid=grid;
},
selectionchange: function(selModel, selected, eOpts) {
var grid=selModel.theLookupGrid;
// Do something with view
}
...

Sencha : can't call method "update" of undefined

Here, i'm registering my app:
App = new Ext.Application({
name: "App",
launch: function() {
this.views.viewport = new this.views.Viewport();
}
});
This is the way i register new panels and components. I put each of them into seperate js files.
App.views.Viewport = Ext.extend(Ext.TabPanel, {
fullscreen: true,
tabBar: {
dock: 'bottom',
layout: {
pack: 'center'
}
},
items: [
{
xtype: 'cPanel'
},
{
xtype: 'anotherPanel'
}
]
});
// register this new extended type
Ext.reg('App.views.viewport', App.views.Viewport);
I added the other components in the same manner.
In one my components which is a list view, I want to change the container panel's activeItem with another panel when tappen on an item, like this: ( Viewport contains this container panel)
App.views.ListApp = Ext.extend(Ext.List, {
store: App.store,
itemTpl: "...",
onItemDisclosure: function(record) {
App.detailPanel.update(record.data);
App.cPanel.setActiveItem("detailPanel");
},
listeners: {
itemtap: function(view, index, item, e) {
var rec = view.getStore().getAt(index);
App.views.detailPanel.update(rec.data);
App.views.cPanel.setActiveItem("detailPanel", {type:"slide", direction: "left"});
}
}
});
App.views.detailPanel.update(rec.data);
But it says: can't call method "update" of undefined
I tried different variations on that line, like:
App.detailPanel.update(rec.data);
and i tried to give detailPanel and cPanel ids, where they were added to their container panel, and tried to reach them with Ext.get(), but none of these worked.
What is the problem here?
And any other advices would be appreciated.
Thanks.
The lazy way: give the panels ids and use:
Ext.getCmp(id)
The recommended way: Assign itemId to your panel and use:
App.views.viewport.getComponent(itemId)
This will allow you to have more than one instance of the same component at aby given time, the first example is not valid cause you can only have a singular id in the DOM tree.
Also getComponent only works for components stored in the items collection.