I am developing a simple JavaScript-based plugin architecture which allows for any JavaScript control from any framework (jQueryUI, ExtJS, etc). to be plugged into and reused on any web page. My plugins below happen to use ExtJS 4.
The first plugin renders fine in the first tab. However, since the second tab hasn't yet rendered when the page loads, the second plugin (also a grid) is first rendering to the document body, and then it renders properly (the HTMLElement/div is moved) inside the tab when the tab is selected. I'd like the plugin content to be hidden prior to rendering inside the tab. Also, when it it does render [when the tab is selected], horizontal scrollbars don't show unless I resize a column.
Any ideas how to fix this?
Ideas: use something other than contentEl; leverage various ExtJS config options; change my architecture.
Here is the plugin code:
(function(MyNamespace) {
var gridDataStore = ...
MyNamespace.Plugin.Chart = MyNamespace.Plugin.extend({
return {
initialize: function() {
// ...
},
render: function() {
var stockGrid = Ext.create('Ext.grid.Panel', {
autoRender: true,
autoShow: true,
store: gridDataStore,
header: false,
stateId: 'stateGrid',
columns: [
{text: 'Symbol', width: 75, sortable: true, dataIndex: 'symbol'},
{text: 'Description', width: 200, sortable: true, dataIndex: 'description'},
{text: 'Quantity', width: 75, sortable: true, dataIndex: 'quantity'},
{text: 'Last Price', width: 85, sortable: true, dataIndex: 'last_price'}
],
viewConfig: {
stripeRows: true,
enableTextSelection: true
}
});
return stockGrid.getEl().dom;
}
};
}
})(MyNamespace);
And here's code using the plugin:
var chart = new MyNamespace.Plugin.Chart();
var anotherPlugin = new MyNamespace.Plugin.Another();
var stocksWindow = Ext.create('Ext.Window', {
title: 'Stocks',
width: 600,
height: 450,
layout: 'fit',
items: [
Ext.create('Ext.tab.Panel', {
activeTab: 0,
items: [{
title: 'Chart',
autoScroll: true,
contentEl: chartPlugin.render() // RENDER FIRST PLUGIN IN FIRST TAB
},{
title: 'Something Else',
autoScroll: true,
contentEl: anotherPlugin.render() // RENDER SECOND PLUGIN IN SECOND TAB
}]
})
]
});
I can add it to an invisible container, but it feels dirty doing so:
var container = document.createElement('div');
document.getElementsByTagName('body')[0].appendChild(container);
container.style.visibility = 'hidden';
var stockGrid = Ext.create('Ext.grid.Panel', {
...
renderTo: container
...
});
Here's an example
Here are a few problems with your code.
What you are calling plugins are not plugins, they are just subclasses of Ext.grid.Panel. A plugin is something that adds functionality to an Ext.Component, but that is not what Stocks is doing. It's just what we call a preconfigured class.
Here's what I would do, just make MyNamespace.Plugin.Stocks be a subclass of Ext.grid.Panel, you can now easily pass those as the items of a Ext.tab.Panel. Make sure you rename it, it's not a plugin.
By giving your widget subclasses the alias: 'widget.stock-grid', you can create them using just an object definition, without having to instantiate them, the framework will take care of rendering it only when needed (the tab is activated)
Ext.create('Ext.tab.Panel', {
activeTab: 0,
items: [{
title: 'Stocks',
autoScroll: true,
xtype: 'stock-grid'
},{
title: 'Orders',
autoScroll: true,
xtype: 'stock-grid'
}]
})
When using managed layouts, you cannot just simply copy a node into another container, because the node you copied in there won't have a managed layout.
Here's a modified version of your code that is written more like Ext-JS intended it. http://jsfiddle.net/NVfRH/10/
You can look into the generated HTML and you'll notice that the grid under orders only renders when you activate that tab.
You should also notice that your grids weren't properly sizing themselves to fill the window, my code fixes that since they are correctly placed in the layout pipeline and obey the fit layout.
Remaining Problems
Your grids are sharing a store, notice that when you sort one, the other gets sorted, so you can't have them with different sorting.
You were using a stateId, but then you were creating two instances with the same stateId, all stateIds must be unique
And lastly, I must ask, were you really relieved when Chad Johnson changed his name to Chad Ochocinco? :)
Related
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
I am trying to create a list of links from a data store, and then use an XTemplate to loop through and display all the links in the data store. I am trying to display the links in a modal window. This is my code:
var win;
var tpl = new Ext.XTemplate("<p>Field1: {f1}, Field2: {f2} </p>").compile();
var data = {
f1: 'a',
f2: null
};
if(!win){
win = new Ext.Window({
el:'hello-win',
layout:'fit',
width:500,
height:300,
closeAction:'hide',
plain: true,
items: [
{
xtype: 'component',
autoEl: {
html: '<input type="text">'
},
listeners: {
render: function(_component) {
_component.getEl().on('keyup', function(e) {
//console.log(this);
console.log(this.dom.childNodes[0].value);
});
}
}
},
{
xtype: 'panel',
title: 'test',
height: 100,
bodyPadding: 10,
tpl: tpl,
data: data,
renderTo: Ext.get('hello-win')
}
]
});
}
win.show(this);
There are no errors in console, but the links never load. It just shows my modal window and my textbox, but no links. I would put in in a jsfiddle, but I don't know how (I'm new to ExtJs).
How can I display a list of links in this modal window?
You should not define renderTo in you panel config. Component defined in items array will be rendered to parent component automatically.
Your window have two items so you can not use fit layout. This layout can be used only if component have only one child component. You can remove layout definition and Ext JS framework will use auto layout or you can use some layout which support more child items, like hbox, vbox, etc.
Your code should look like this:
var win = new Ext.Window({
width:500,
height:300,
closeAction:'hide',
plain: true,
items: [
{
xtype: 'textfield',
enableKeyEvents: true,
listeners: {
keyup: function(c) {
console.log(c.getValue());
}
}
},
{
xtype: 'panel',
title: 'test',
height: 100,
bodyPadding: 10,
tpl: tpl,
data: data
}
]
});
Also you should consider using textfield component instead of creating textfield by using component with autoEl config.
I have some trouble using an Ext.Panel in Sencha Touch 2.2. It works at first, but after building it can't be closed though hideOnMaskTap is true.
Here's my code:
MinPanel.js
Ext.define('MinimalPanelExample.view.MinPanel', {
extend: 'Ext.Panel',
requires: [
'Ext.dataview.List'
],
config: {
id: 'minPanel',
width: '320px',
height: '480px',
modal: true,
hideOnMaskTap: true,
html: 'minimal panel',
items: [
{
xtype: 'list',
width: '100%',
height: '100%'
}
]
}
});
Adding it in Main.js:
var minPanel = Ext.create('MinimalPanelExample.view.MinPanel');
[...]
items: {
docked: 'top',
xtype: 'titlebar',
title: 'Welcome to Sencha Touch 2',
items: [
{
xtype: 'button',
text: 'Button',
listeners: {
tap: function(button) {
minPanel.showBy(button);
}
}
}
],
},
What's curious is that the list inside the panel isn't even shown if I run the built package, it just shows the html: 'minimal panel'. There's no warnings in the Javascript console, though.
Any ideas about why that happens?
Also, feel free to tell me if my way of creating / accessing the panel in the first place is wrong.
Figured it out. You can't just Ext.create on top of the Main view file, it has to be somewhere in the Ext.define block. It works if I create the MinPanel in an initialize listener, and then get it via Ext.getCmp('minPanel') when the user presses the button.
i'm a newbie with ExtJS, i'm using ExtJS 4.0.7 and what they call MVC, just getting use to the code, so, i was taking a look to the examples, and there is one that works perfect for what i'm trying to do, the "MultiSelect and ItemSelector" example, more specific, the ItemSelector, that is the one i'm using, it use some kind of library named "ux", that is show for example i think, but the ItemSelector is ok form me, i don't want to change or add nothing to the component, so, what i do is, create a Window with a Form in it, and within the form an ItemSelector, the ItemSelector use a remote store cuz i load the data from the database, everything works perfect, i can see the data in the ItemSelector and use the component, the problem is when i close the window and open it again, the ItemSelector is just empty, no error, no warning, nothing that point to a problem, just an empty ItemSelector, this is my code:
//My window definition
Ext.define('Sigep.view.ProjectForm', {
extend: 'Ext.Window',
uses: ['Ext.ux.form.ItemSelector'],
id:"projectFormWindow",
title: 'New Project',
autoHeight:true,
width: 700,
modal:true,
layout: 'fit',
initComponent: function() {
var win = this;
var studentStore = Ext.create('Sigep.store.StudentsStore');
this.items = [
{
xtype:"form",
id: 'projectForm',
bodyPadding: 10,
border:false,
autoHeight:true,
postUrl:baseURL+'projects/save',
defaults: {
border:false
},
fieldDefaults: {
labelAlign: 'left',
labelWidth: 110,
msgTarget: 'side'
},
items:[
{
xtype:'tabpanel',
plain:true,
activeTab: 0,
height:260,
margin: '10 0 0 0',
defaults:{
bodyStyle:'padding:10px'
},
items:[{
title:'Autor(es)',
defaults: {
allowBlank:false
},
defaultType: 'textfield',
items:[
{
xtype: 'itemselector',
name: 'project[authors][]',
id: 'itemselector-field',
anchor: '100%',
labelWidth: 90,
width:600,
height:210,
fieldLabel: 'ItemSelector',
imagePath: baseURL+'extjs/ux/images/',
store: studentStore,
displayField: 'fullName',
valueField: 'userId',
maxSelections:2,
buttons: ['add', 'remove'],
delimiter:null
}
]
}]
}
]
}
];
this.buttons= [{
text: 'Save',
handler:function(){
win.close();
}
},{
text: 'Cancel',
handler:function(){
win.close();
}
}];
studentStore.load();
this.callParent();
}
});
///////////////////////////////////////////////
//In a click event of a button
var win = Ext.create('Sigep.view.ProjectForm');
win.show();
//////////////////////////////////////////////
//The Store definition
Ext.define('Sigep.store.StudentsStore', {
extend: 'Ext.data.Store',
fields: ['userId', 'fullName'],
proxy: {
type: 'ajax',
url:baseURL+'accounts/students',
reader: {
type: 'json',
root: 'results'
}
}
});
I also tried setting autoLoad:true to the store, but it doesn't work neither, so, As you can see, all is very simple, the first time i open the window, everything works ok, but when i close and open again, the ItemSelector is empty, i tried with every event i can try already, and nothing work, with the FireBug, after the window is show, i execute something like this
//if i execute this code after the window is show, the item selector is filled with the data
var item = Ext.getCmp("projectFormWindow").down('#itemselector-field');
var store = item.store;
item.bindStore(store);
this code reload the itemselector after the window is show, so i tried in the show event, but it doesn't work, if i put a button in the window and in the handler this 3 lines, the ItemSelector is loaded ok. the funny is that ItemSelector extend MultiSelect, and if i change the xtype to multiselect, it works great, just perfect, but what about the ItemSelector, i was looking inside the definition code and i think is something like in the second load the toField is not getting created, but i don't know how to deal with this,
so, exist a workarround for this? a way to make it work? hope you can help me guys
thanks
I was looking into the Ext js forum, someone gave a solution, i don't know if it is the best but it works for me,
studentStore.load(function(){
win.down('#itemselector-field').bindStore(studentStore);
});
now, it is working as i wanted
hope it can help others
In my Forms initComponent method I added a listener for beforerender
initComponent: function () {
var me = this;
Ext.applyIf(me, {
listeners: {
beforerender: {
fn: me.onFormBeforeRender,
scope: me
}
}
});
me.callParent(arguments);
},
onFormBeforeRender: function(abstractcomponent, options) {
// where store is created globally like this var store = Ext.create('TIP.store.CommodityStore');
store.load(function(){
Ext.getCmp("commoselector").bindStore(store);
});
}
I want to be able to reorder the rows within a single grid. Im relatively new to extjs and have tried searching for this but all the resources i find are for older ext js versions and some of properties defined in those aren't valid anymore. eg- http://www.vinylfox.com/getting-started-with-grid-drag-drop/
xtype: 'grid',
id: 'row-grid',
hideHeaders: true,
store: 'SelectedRowStore',
//enableDragDrop: true,
//ddGroup: 'rowgrid-dd',
columns: [
{
header: 'Rows',
flex: 1,
sortable: false,
dataIndex: 'DisplayName'
},
{
id: 'button-column',
dataIndex: 'ID',
sortable: true,
hideable: false,
width: 35,
renderer: PA.common.RendererHelper.renderButtonForAddRowMainGrid
}
]
I'd really appreciate any help/advice on this issue.
Take a look at the grid to grid drag-n-drop example. It works with two grids, but I'm sure it can be easily modified to allow rearranging rows within one grid.
You can follow the sencha sample as Rene said or follow this:
Add this to your grid:
viewConfig: {
plugins: {
ptype: 'gridviewdragdrop',
containerScroll: true,
dragGroup: 'someuniquenameforyourgrid',
dropGroup: 'someuniquenameforyourgrid'
},
},
And, only if you need some listener to do some anction, add (inside viewConfig):
listeners: {
drop: 'onDropGrid'
}
and event handler code in your controller:
onDropGrid: function (node, data, dropRec, dropPosition) {
...
}