I have a simple app presentation with a set of windows with sencha components. When a event of move previous window or move next window the main controller take the view, clean it and add a new component to view...
Ext.define('jsclient.view.main.MainVC', {
extend: 'Ext.app.ViewController',
alias: 'controller.main-view',
config: {
listen: {
global: {
eventNewWindow: 'eventNewWindow'
}
}
},
eventNewWindow: function (callback) {
this.view.removeAll();
this.view.add(callback());
}
});
The problem occurs when i put a panel component that contains a tabpanel, the components paint all window, paints the tabs, but no paint the components that contain each tab. If i exam the components that window create, its was created but not render or showing.
Ext.define('jsclient.view.articleCheck.ArticleCheckV', {
extend: 'Fwk.Panel',
alias: 'widget.article-check',
controller: 'article-check',
viewModel: 'article-check',
requires: [
'Fwk.Panel',
'Ext.TabPanel',
'jsclient.view.articleCheck.ArticleCheckVM',
'jsclient.view.articleCheck.ArticleCheckVC',
'jsclient.view.articleCheck.articlePending.ArticlePendingV',
'jsclient.view.articleCheck.articleChecked.ArticleCheckedV'
],
cls: 'article-check',
title: 'Mi ventana**',
titleAlign: 'center',
layout: {
type: 'vbox',
align: 'fit'
},
items: [{
xtype: 'tabpanel',
flex: 1,
deferredRender: true,
items: [{
title: 'Pendientes',
reference: 'pending',
xtype: 'article-pending',
width: "50%"
}, {
title: 'Chequeados',
reference: 'checked',
xtype: 'article-checked',
width: "50%",
}]
}],
tools: [{
type: 'left',
docked: 'left',
listeners: {
click: {
element: 'element',
fn: 'goPrevious'
}
}
}, {
type: 'plus',
docked: 'right',
margin: '0 20 0 0',
listeners: {
click: {
element: 'element',
fn: 'goNewArticle'
}
}
}],
listeners: {
click: {
element: 'element',
fn: 'prueba'
}
},
constructor: function (config) {
this.callParent([config]);
}
});
Any code have two file more that helps viewmodel component, i will saw you a example above.
The View:
Ext.define('jsclient.view.main.MainV', {
extend: 'Fwk.Panel',
alias: 'widget.main-view',
id: 'main-view',
requires: [
'Fwk.Panel',
'jsclient.view.main.MainVM',
'jsclient.view.main.MainVC',
'jsclient.view.blockCheck.BlockCheckV',
'jsclient.view.centerList.CenterListV',
'jsclient.view.articleCheck.ArticleCheckV',
'jsclient.view.articleCheck.articlePending.ArticlePendingV',
'jsclient.view.articleCheck.articleChecked.ArticleCheckedV'
],
viewModel: 'main-view',
controller: 'main-view',
layout: {
type: 'vbox',
align: 'fit'
},
margin: 'auto',
items: [{
xtype: 'center-list',
flex: 1
}],
constructor: function (config) {
this.callParent([config]);
//Read url params and restore context if needed
var urlParams = location.search;
if (urlParams !== '') {
Fwk.Context.restore(urlParams);
}
}
});
The Controller:
Ext.define('jsclient.view.main.MainVM', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.main-view',
data: {},
stores: {}
});
Well, to my problem i doesn't find a solution in the way i'm trying solved... But using a different point of view i can solved.
My oldest way, create items on a component:
This solution might be correct but... for tabpanel doesn't work. I create a component in viewport (main component) and in this component when a event happend i create a new component.
-> Viewport
|
-> MainComponent
|
-> AnyComponent (event)
Using this piece of code i try modified the view main with a callback that return the
constructor of new component in main view:
/*
* Callback in other view
*/
callback: function(options){
return new jsclient.view.mycomponent.MyComponentV(options);
}
/*-----------------------------*/
eventNewWindow: function (callback) {
this.view.removeAll();
this.view.add(callback());
}
My new way create items on viewport:
The new solution is similar but use Viewport instead main component, We need have some
things clear. Viewport is a set (Array) of views that compete for main view, if not are
on main view, the components is on second plane and they not are visible but are init.
So solution left like that, instead use main component we use viewport:
We create N controller for N view, and when a other view send a global event, the controller catch the event and itself create here on viewport.
Ext.define('jsclient.controller.main.MainController', {
extend: 'Ext.app.Controller',
requires: [
'jsclient.view.main.i18n.Main_en_EN',
'jsclient.view.main.i18n.Main_es_ES',
'jsclient.view.main.MainV',
],
config: {
listen: {
global: {
eventNewMain: 'eventNewMain'
}
}
},
eventNewMain : function(options) {
Ext.Viewport.setActiveItem('main-view');
}
});
In the case we need pass argument to component we can do this piece of code:
Ext.define('jsclient.controller.articleCheck.ArticleCheckController', {
extend: 'Ext.app.Controller',
requires: [
'jsclient.view.main.i18n.Main_en_EN',
'jsclient.view.main.i18n.Main_es_ES',
'jsclient.view.articleCheck.ArticleCheckV',
],
config: {
listen: {
global: {
eventNewArticleCheck: 'eventNewArticleCheck'
}
}
},
eventNewArticleCheck: function (options) {
if(options != undefined){
Ext.Viewport.removeAll();
}
Ext.Viewport.add({
xtype: 'article-check',
options: { block: options }
});
Ext.Viewport.setActiveItem('article-check');
}
});
Related
I'm using, or abusing, Sencha Touch for the first time and I just want to push a list view, when i click a button. Here is my view:
Ext.define('TouchNuts.view.Decision', {
extend: 'Ext.Panel',
xtype: 'decision',
config: {
title: 'Decision',
scrollable: true,
styleHtmlContent: true,
styleHtmlCls: 'Decision',
tpl: '<h2>{name}</h2>, <h3>{description}<h3>, <h4>{price:ellipsis(15)}</h4> <h1>you can do this </h1>',
items: [
{
xtype: 'button',
text: 'SEND',
ui: 'confirm',
docked: 'bottom',
action: 'doSomething'
}
]
}
});
Here is the view I'd like to push:
Ext.define('TouchNuts.view.File', {
extend: 'Ext.Panel',
xtype: 'file',
config: {
title: 'File',
iconCls: 'star',
layout: 'fit',
items: [
{
xtype: 'list',
id: 'file',
store: 'TransactionStore',
itemTpl: '<h2>{name:ellipsis(15)}</h2>, <h3>{description:ellipsis(8)}<h3>, <h4>{price:ellipsis(15)}</h4>',
itemCls: 'SummaryItems'
}
]
}
});
And here is my controller:
Ext.define('TouchNuts.controller.doSomething', {
extend: 'Ext.app.Controller',
config: {
refs: {
},
control: {
'button[action=doSomething]' : {
tap: function() {
getMainView('TouchNuts.view.Decision').push('TouchNuts.view.File');
}
}
}
}
});
I'm pretty good with HTML, CSS, and jQuery, but new to JS and totally clueless when it comes to Sencha so any advice is appreciated.
It is good to give your views an itemId inorder to reference them in your controller. So for instance:
TouchNuts.view.Decision can have an itemId:decisionPanel
and
TouchNuts.view.File can have an itemId:filePanel
Now in your Controller you would do this:
...
config: {
refs: {
decisionPanel: {
autocreate: true,
selector: '#decisionPanel',
xtype: 'decision'
},
filePanel: {
autocreate: true,
selector: '#filePanel',
xtype: 'file'
}
},
control: {
'button[action=doSomething]' : {
tap: 'onButtonTap'
}
}
onButtonTap : function(button, e, options) {
var me = this;
Ext.Viewport.setActiveItem(me.getDecisionPanel());
}
...
You will notice that I used getDecisionPanel() to get the decisionPanel view. This is because a getter function is automatically generated for each ref you specify and in order to access it, you new to use get+ the Capitalized ref name.
More info here: http://docs.sencha.com/touch/2-1/#!/api/Ext.app.Controller
Instead of
getMainView('TouchNuts.view.Decision').push('TouchNuts.view.File');
You have to create the view first and then push it to view
getMainView('TouchNuts.view.Decision').push(Ext.create('TouchNuts.view.File'));
I am trying to create a component and access it inside controller. But while accessing the component by id, it is not returning the component. It always returns undefined. Please find below the code.
enter code here
//Component creation in view layer as below
Ext.define('MyApp.view.performance.grid.IFrameGridCmp', {
extend: 'Ext.panel.Panel',
alias: 'widget.crMainPanel',
id:'mainPanelId',
layout: {
align: 'stretch',
type: 'vbox'
},
border:0,
resizable: false,
forceFit: true,
autoWidth: true,
initComponent: function() {
Ext.apply(this, {
items: [
{
xtype:'panel',
flex:.02,
border:0
},
{
xtype:'crHeaderPanel',
flex:.05
},
{
xtype: 'crUpperPanel',
flex: 0.93
},
Ext.create('Ext.Component', {
autoEl: {
tag: 'iframe',
cls: 'x-hidden',
src: Ext.SSL_SECURE_URL
},
id:'FileDownloader',
height:0,
listeners: {
afterrender: function () {
this.getEl().on('load', function () {
console.log('loaded download frame');
});
}
},
load: function(config){
var e = this.getEl();
e.dom.src = config.url + (config.params ? '?' + Ext.urlEncode(config.params) : '');
e.dom.onload = function() {
Ext.getBody().unmask();
if(e.dom.contentDocument.body.childNodes[0].wholeText == '404') {
Ext.Msg.show({
title: 'Attachment missing',
msg: 'The document you are after can not be found on the server.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
});
}
};
}
})
]
});
this.callParent(arguments);
}
});
========================================
enter code here
//Accessing the controller as below in controller
views: ['performance.grid.IFrameGridCmp'],
//The below line gives error
var downloader = Ext.getCmp('FileDownloader');
downloader.load({
url: '/APXUI/data/json/xls?exportData='+Ext.JSON.encode(records)
});
Well, the view is not created at the time you are calling Ext.getCmp()
Note that views: ['performance.grid.IFrameGridCmp'], is only a sort of binding that view to the controller, which means the controller will create a getter for you, nothing more. You still need to instantiate the view by calling .create(); or Ext.widget('crMainPanel')
In you controller use control for example to handle it:
me.control({
'crMainPanel #FileDownloader': {
afterrender: me.addDownloader
}
});
Don't use Ext.getCmp() it is really slow and you will have many issues with that.
Don't use id - better use itemId.
Why you need to call this from controller?
I have got the tabpanel - it's the main form (view).
In this tabpanel I define the different tabs - xtype:'panel'.
So, I have one main(controller) , main view and some tabs views.
The tab's views are referenced in main view.
I want to define listener of activate event of some child's panel in main controller.
How can I do that?
the main controller :
Ext.define('KP.controller.account.apartment.Main', {
extend: 'Ext.app.Controller',
views: ['account.apartment.Main',
'account.apartment.Requisites'
],
models: ['account.apartment.Requisites'
],
stores: ['account.apartment.Requisites'
],
init: function () {
}
});
The main view:
Ext.define('KP.view.account.apartment.Main', {
extend: 'Ext.window.Window',
alias: 'widget.ApartmentData',
height: 566,
width: 950,
activeItem: 0,
layout: {
type: 'fit'
},
autoShow: false,
initComponent: function() {
var me = this;
Ext.applyIf(me, {
items: [
{
xtype: 'tabpanel',
activeTab: 0,
deferredRender: true,
items: [
{
xtype: 'RequisitesApartment'
}
]
}
]
});
me.callParent(arguments);
}
});
The child panel RequisitesApartment (view):
Ext.define('KP.view.account.apartment.Requisites', {
extend: 'Ext.panel.Panel',
alias: 'widget.RequisitesApartment',
id: 'panel_accountrequisites',
height: 350,
width: 1124,
autoScroll: true,
layout: {
type: 'fit'
},
listeners: {
activate: function () {
....load data....
...this listeners I want to push in 'main' controller...
}
},
initComponent: function () {
var me = this;
var grid_store = Ext.create('KP.store.account.apartment.Requisites');
Ext.applyIf(me, {
dockedItems: [
{
xtype: 'gridpanel',
height: 260,
autoScroll: true,
dock: 'bottom',
store: grid_store,
id: 'r_gridFlatParams',
forceFit: true,
columns: [
...some columns....
],
viewConfig: {
}
}
]
});
me.callParent(arguments);
}
});
Register it directly as control within the responsible controller
Here is a example with a working query. For sure you just will need the query, but I think it's a good example. The custom cfg property ident make it easy find each tab. As in the example below you will have specify a tabConfig within each panel and define the ident there.
Ext.create('Ext.tab.Panel', {
width: 400,
height: 400,
renderTo: document.body,
items: [{
title: 'Foo',
tabConfig: {
ident: 'foo'
},
}, {
title: 'Bar',
tabConfig: {
ident: 'bar',
title: 'Custom Title',
tooltip: 'A button tooltip'
}
}]
});
console.log(Ext.ComponentQuery.query('tabpanel tabbar tab[ident=foo]')[0]);
console.log(Ext.ComponentQuery.query('tabpanel tabbar tab[ident=bar]')[0]);
Another way is to use css id's which can be queried like '#my-name' but I recommend to use a custom one as in the example above
Well, I should put this code in 'main'(controller):
this.control({
'ApartmentData tabpanel RequisitesApartment': {
activate: function () {
console.log('hello!');
}
}
});
The problem was in wrong selector , that I used.
The correct selector is :
'ApartmentData tabpanel RequisitesApartment'
There 'ApartmentData'(define like a alias: 'widget.ApartmentData') - is the 'window' xtype -the main form.
tabpanel - panel with tabs in 'window', and 'apartServList'(define like alias: 'widget.RequisitesApartment') - the some panel.
Thanks for sra!
the correct thing to do is to pass a config object to the member function control into controller init function. From Sencha documentation : The control function makes it easy to listen to events on your view classes and take some action with a handler function.
A quick example straight from extjs docs:
Ext.define('MyApp.controller.Users', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'viewport > panel': {
render: this.onPanelRendered
}
});
},
onPanelRendered: function() {
console.log('The panel was rendered');
}
});
Hope this helps.
Cheers
I am writing a Sencha Touch 2 app. I have recently got stuck with the following problem.
I have a Container view which has data bound to it with setRecord(myRecord). So my question is what's the right way to populate my subcomponents with this data?
Here is my code(simplified for brevity):
Ext.define('MyApp.model.Item', {
extend: 'Ext.data.Model',
config: {
fields: ['name', 'description', 'image'],
proxy: { /* proxy config */ }
}
});
Ext.define('MyApp.view.DetailsView', {
extend: 'Ext.Container',
xtype: 'itemdetails',
config: {
layout: 'hbox',
items: [
{
xtype: 'container',
flex: 1,
layout: 'vbox',
items: [
{
xtype: 'img',
src: '' // SHOULD BE POPULATED FROM DATA.image
},
{
xtype: 'button',
text: 'Buy',
action: 'buyItem'
}
]
},
{
flex: 3,
tpl: '<h1>{name}</h1><p>{description}</p>' // SHOULD BE POPULATED FROM DATA
}
]
}
});
And here is the code that populates and shows my view from a controller:
Ext.define('Myapp.controller.Main', {
...
refs: {
itemDetails: {
selector: '',
xtype: 'itemdetails',
autoCreate: true
}
}
routes: {
'item/details/:id': 'showItemDetails'
},
...
showItemDetails: function(id) {
MyApp.model.Item.load(id, {
callback: function(item){
var card = this.getItemDetails();
card.setRecord(item);
this._showCard(card);
},
scope: this
});
}
});
I first implemented it with a simple Container containing a 'tpl', but in this case I was not able to have a button inside it, which would be queryable from the controller. Any ideas?
Thx, in advance.
Taken from a comment in the Sencha Touch docs for Ext.Component, add a setRecords function that recursively sets the record on each item in the items component hierarchy:
setRecords: function(component, record) {
me = this;
component.getItems().each(function(item) {
if (typeof item.setRecord == 'function') {
item.setRecord(record);
}
//set record on all subitems
if (typeof item.getItems == 'function') {
var subItems = item.getItems();
if (subItems.getCount() > 0) {
me.setRecords(item, record);
}
}
});
}
and then override setRecord to call the new function:
setRecord: function(record) {
result = this.callParent([record]);
this.setRecords(this, record);
return result;
}
So now no more need to explicitly set the data on specific components
in a Extjs application I have a Grid and a Tabs line over it. Content of the Grid depends on the selected Tab.
Say tabs has Jan-Feb-Mar-... values. Clicking of the Tab I would reload grid's store
Question: is it possible to avoid duplicating of the 12 grid components in favor to have one shared instance?
Thanks
Disclaimer: searching at the sencha's forum, google, stackoverflow was not successful :(
It is, but it would require more effort than it is worth. Just create a prototype for your component, so that you can create new instances really quickly.
I haven't tried this myself, but I imagine that you could create a TabPanel with empty tabs and size the TabPanel so that only the tab strip is visible. Under that (using the appropriate layout, border, vbox, etc.) create your GridPanel and use the TabPanel's activate event to reload the grid based on the currently-active tab.
Hope the following implementation meet your needs
1. Create your custom grid and register it
2. place it tab panel
As the grid is created using xtype, it would not create 12 instances when you change tabs.
Application.PersonnelGrid = Ext.extend(Ext.grid.GridPanel, {
border:false
,initComponent:function() {
Ext.apply(this, {
store:new Ext.data.Store({...})
,columns:[{...}, {...}]
,plugins:[...]
,viewConfig:{forceFit:true}
,tbar:[...]
,bbar:[...]
});
Application.PersonnelGrid.superclass.initComponent.apply(this, arguments);
} // eo function initComponent
,onRender:function() {
this.store.load();
Application.PersonnelGrid.superclass.onRender.apply(this, arguments);
} // eo function onRender
});
Ext.reg('personnelgrid', Application.PersonnelGrid);
var panel = new Ext.TabPanel({
items:[{
title:'Jan',
items: [{xtype:'personnelgrid'}]
}, {
title: 'Feb',
items: [{xtype:'personnelgrid'}]
}
....
{
title: 'Dec',
items: [{xtype:'personnelgrid'}]
}]
})
Since this is the only place discussed about this until now, I share what I just found.
The trick is use dockedItems in ExtJs 4 (Not sure either grid can be added into tbar in ExtJs 3)
When changing the active tab, only body will be change but not the docked item. Just set the grid height equal to the body during boxready and resize so that we can't see the body anymore.
This is the code for ExtJs 4.2 MVC that also make use of refs.
Ext.define('app.controller.Notification', {
extend: 'Ext.app.Controller',
views: ['notification.List'],
stores: ['Notification'],
models: ['Notification'],
refs: [{
ref: 'pnlNotif',
selector: 'pnlNotif'
}, {
ref: 'notifList',
selector: 'notifList'
}],
init: function () {
this.control({
'dbPnlNotif': {
added: this.pnlNotifAdded,
boxready: this.calcNotifListSize,
resize: this.calcNotifListSize,
tabchange: this.pnlNotifTabChange
}
});
},
pnlNotifAdded: function (pnlNotif) {
pnlNotif.add({ title: '1', html: '1' });
pnlNotif.add({ title: '2', html: '2' });
pnlNotif.add({ title: '3', html: '3' });
},
calcNotifListSize: function (pnlNotif) {
// calc the notification list height to make sure it use the whole body
// This way we can use only one instance of list to display for each tabs
// because the list is rendered as dockedItems
var height = pnlNotif.getHeight();
var headerHeight = pnlNotif.getDockedItems()[0].getHeight();
var tabBarHeight = pnlNotif.getDockedItems()[1].getHeight();
height = height - headerHeight - tabBarHeight;
if (this.getNotifList().getHeight() !== height) {
this.getNotifList().setHeight(height - 1);// - 1 to include border bottom
}
},
pnlNotifTabChange: function (pnlNotif, newTab) {
// do something to filter the list based on selected tab.
}
});
Ext.define('ML.view.Notification', {
extend: 'Ext.tab.Panel',
alias: ['widget.pnlNotif'],
title: 'Notification',
dockedItems: [{
xtype: 'notifList'
}]
});
Ext.define('ML.view.notification.List', {
extend: 'Ext.grid.Panel',
alias: 'widget.notifList',
dock: 'top',
store: 'Notification',
initComponent: function () {
this.columns = [
...
];
this.callParent(arguments);
}
});
Try this
var gridJanName = Ext.create('Ext.grid.Panel', {
enableColumnHide: false,
autoScroll:true,
store: storeJanNameGroup,
border:true,
stripeRows: true,
columnLines:false,
loadMask: true,
tbar:tbgridTools,
margin: '1 1 1 1',
pageSize: 100,
maxWidth:700,
features: [groupFeature],
selModel: {
mode: 'MULTI'
},
columns: [
{xtype:'rownumberer',width:50},
{dataIndex:'id', hidden:true},
//etc
]
});
var gridFebName = Ext.create('Ext.grid.Panel', {
enableColumnHide: false,
autoScroll:true,
store: storeJanNameGroup,
border:true,
stripeRows: true,
columnLines:false,
loadMask: true,
tbar:tbgridTools,
margin: '1 1 1 1',
pageSize: 100,
maxWidth:700,
features: [groupFeature],
selModel: {
mode: 'MULTI'
},
columns: [
{xtype:'rownumberer',width:50},
{dataIndex:'id', hidden:true},
//etc
]
});
//
//etc grid
//
var JanPanel = Ext.create('Ext.panel.Panel', {
title:'Jan',
bodyPadding: 5,
Width:780,
layout: {
type: 'hbox',
align: 'stretch'
},
items: [gridJanName]
});
var FebPanel = Ext.create('Ext.panel.Panel', {
title:'Feb',
bodyPadding: 5,
Width:780,
layout: {
type: 'hbox',
align: 'stretch'
}
//,items: [gridFebName]
});
var MarPanel = Ext.create('Ext.panel.Panel', {
title:'Mar',
bodyPadding: 5,
Width:780,
layout: {
type: 'hbox',
align: 'stretch'
}
//,items: [gridMarName]
});
//etc
var eachMonthstabs = Ext.create('Ext.tab.Panel', {
minTabWidth: 130,
tabWidth:150,
//Width:750,
scroll:false,
autoHeight: true,
id:'timestabs',
enableTabScroll:true,
items: [
{
xtype:JanPanel
},
{
xtype:FebPanel
},
{
xtype:MarPanel
}
///etc
]
});
For me good solution was to use a left toolbar called lbar with list of buttons and a single grid instead of tabpanel