Sencha ExtJS MVC - data source specified at run time - extjs

I want to write a proof-of-concept app along these lines:
View
- a URL text input field at top with Go button
- a big DIV underneath consisting of the rest of the view
Controller
- upon Go pressed, validate the URL text
- set up the URL to the data source
- read data from the data source
- create a nested DIV element for each data, apply CSS rules
- add the element to the big DIV
Model
- define the fields
- define the default ordering
CSS
- define the styles
First, does what I have written above work within ExtJS or will I be fighting the framework? In particular, inserting plain HTML as element nodes.
Second, does anyone know of an existing project under GPL which could act as starting point? So far what I've seen are flashy examples with URLs hard-coded and set to auto-load.

There's nothing scary or otherwise disturbing in what you've written.
Although not much advertised, ExtJS handles custom HTML & CSS pretty well. You can set some using the html or tpl config options. The latter is powered by XTemplates, so you can do loops, etc. When using these options and/or custom CSS, Ext will calculate its layouts around the rendered result, accounting for your custom style automatically. Now, in practice, that's a whole lot of work for the framework, and it doesn't always work as expected, and it won't work at all on some browsers (like not so old IE, of course). One big pitfall you should be aware of is that you should always use integer value in px for your CSS, since if a dimension resolve to a decimal value in px, Ext will choke on that.
That said, since you're apparently going to back your data with a model, you should probably use a dataview. That's a component that let you use custom HTML over a regular Ext store. It then provides goodies for item selection, paging, etc. It's the base class of other advanced data views, like Ext grids.
Regarding URLs, you don't necessarily have to hardcode them in the model's proxy (or elsewhere). You can pass an URL to an existing store's load method (as documented here).
Finally, I don't know of existing projects, but your POC is really straightforward, so here's a fiddle to get you started. The code is not 100% clean, in particular defining everything in a single file, and thus missing all the requires... But it illustrates most of the points you've asked about. Read the docs about the components / methods that are used to learn how to go beyond this.
Here's the fiddle's code:
Ext.define('Foo.model.Item', {
extend: 'Ext.data.Model',
fields: ['name']
});
Ext.define('Foo.view.MainController', {
extend: 'Ext.app.ViewController',
alias: 'controller.main',
onGo: function() {
var view = this.getView(),
url = view.down('textfield').getValue(),
dataview = view.down('dataview'),
store = dataview.getStore();
if (this.isValidUrl(url)) {
store.load({url: url});
} else {
Ext.Msg.alert(
"Invalid URL",
"This URL cannot be loaded here: " + url
);
}
},
// private
isValidUrl: function(url) {
return ['data1.json', 'data2.json'].indexOf(url) !== -1;
}
});
Ext.define('Foo.view.Main', {
extend: 'Ext.Panel',
xtype: 'main',
controller: 'main',
layout: {
type: 'vbox',
align: 'stretch'
},
items: [{
xtype: 'container',
layout: 'hbox',
margin: 3,
defaults: {
margin: 3
},
items: [{
flex: 1,
xtype: 'textfield',
emptyText: "Valid inputs are 'data1.json' or 'data2.json'",
listeners: {
specialkey: function(field, e) {
if (e.getKey() === e.ENTER) {
// custom event, for the fun of it
field.fireEvent('enterkey', field, e);
}
},
// the custom can be bound to controller in "modern ext" way
enterkey: 'onGo'
}
},{
xtype: 'button',
text: "Go",
handler: 'onGo'
}]
},{
flex: 1,
xtype: 'dataview',
margin: '0 6 6 6',
cls: 'my-dataview', // for CSS styling
store: {
model: 'Foo.model.Item',
autoLoad: false
// default proxy is ajax and default reader is json,
// which is cool for us
},
tpl: '<tpl for=".">' + '<div class="item">{name}</div>' + '</tpl>',
itemSelector: '.item'
}]
});
Ext.application({
name : 'Foo',
mainView: 'Foo.view.Main'
});
Some CSS for the data view:
.my-dataview .item {
padding: 1em;
border: 1px solid cyan;
color: magenta;
float: left;
margin: 6px;
width: 100px;
}
And an example JSON response (this is the bare minimum to be functional... read about proxies & reader to go further):
[{
name: 'Foo'
},{
name: 'Bar'
},{
name: 'Baz'
}]

Related

Sencha opposite of Ext.getCmp('mainpage').add({items:stuff});

I have used the following to add content to my view:
Ext.getCmp('mainpage').add({items:thecarousel});
thecarousel is an array representing my carousel and its content. This all works as I require it to. Here's the code for it:
var thecarousel = {
xtype: 'carousel',
width: '100%',
height: '100%',
itemId: 'thecarousel',
id: 'carousel',
defaults: {
styleHtmlContent:true,
},
items: allcharts,
}
Ext.getCmp('mainpage').add({items:thecarousel});
Ext.Viewport.setMasked(false);// remove loading message`
What I am looking for is an method to do the opposite of this and remove the carousel from the view.`
I have unsuccessfully tried the following:
Ext.getCmp('mainpage').remove('carousel',false)
Ext.getCmp('mainpage').remove({items:'carousel'})
Ext.getCmp('mainpage').remove('carousel',true)
If you are using id: 'carousel', you can do that like this:
Ext.getCmp('mainpage').remove(Ext.getCmp('carousel'))
You could also do it using a component query:
var main = Ext.getCmp('mainpanel');
main.remove(main.down('#carousel'));//added missing closing brackets
//OR, if there is only one component of xtype 'carousel' on your mainpanel:
main.remove(main.down('carousel'));
I personally would avoid using IDs and go with the second method (you could give the carousel an itemId: 'carousel' and still use main.remove(main.down('#carousel')) if you want).

How to check whether xtype object exists or not in extjs4.1

I am getting an object using Ext.component.Query. I need to check whether the object exists or not. If object exists, I need to remove the object. Can anybody tell me how to do this?
Thanks
As other posters have mentioned, the method you're looking for is Ext.ComponentQuery, which returns an array which you can then check the length of via length, which will in turn tell you if the object exists or not. If the object exists, it can be destroyed via the destroy() method of the Ext.AbstractComponent
I have made a jsFiddle example demonstrating what you're trying to do here: http://jsfiddle.net/mPYPw/
Code from the fiddle:
Ext.create('Ext.panel.Panel', {
name : 'myPanel',
title: 'Panel 1',
width: 200,
html: '<b>Its a panel!</b>',
renderTo: Ext.getBody()
});
Ext.create('Ext.panel.Panel', {
name : 'myPanel',
title: 'Panel 2',
width: 200,
html: 'Look, another panel!',
renderTo: Ext.getBody(),
dockedItems: [{
xtype: 'toolbar',
dock : 'bottom',
items: [{
text: 'Destroy all panels!',
handler: function(){
// Here we can query for the panels
var panels = Ext.ComponentQuery.query('panel[name=myPanel]'),
trees = Ext.ComponentQuery.query('treepanel');
// #param {Ext.panel.Panel[]} panels Array of panel components
if(panels.length > 0){
alert("About to destroy " + panels.length + " Panels!");
Ext.each(panels, function(panel){
panel.destroy();
});
}
// There are no tree panels
if(!trees.length){
alert("There are no tree panels to destroy!");
}
}
}]
}]
});
Simple check with Ext.ComponentQuery
var check = Ext.ComponentQuery.query('yourXtype');
if (check.length > 0)
//do something
else
//do other something
I know the documentation was messed up on one of the versions... I don't know if it is still the same in Ext JS 4.2, but in 4.1.1 you can query for Ext JS objects by xtype using something similar to this:
Ext.ComponentQuery.query('xtype');
i.e.
Ext.ComponentQuery.query('gridpanel');
I think Ext.ComponentQuery-method-query explains it.
FIRST NOTE THAT Ext.getCmp("id") is suitable for small Apps..
If u tend to hav a big app u could go for a Component Query .
This can be done in two ways Either u could use a "Xtype" or "component Id"(note component id must be prefixed with a #).

Legend Template - Chart

I got this template (default)
<span class="x-legend-item-marker {[values.disabled?'x-legend-inactive':'']}" style="background:{mark};"></span>{name}
that produce this :
I want to have the same template with every of it's functionnality. But, I need one more if-clause to it. I don't want an item to be 'legendarize' if it's value is 0.
Here is the complete code
{
xtype: 'container',
title: 'Chart',
iconCls: 'chart',
itemId: 'chart_Tab',
layout: {
type: 'fit'
},
items: [
{
xtype: 'polar',
itemId: 'pie',
colors: [
'#115fa6',
'#94ae0a',
'#a61120',
'#ff8809',
'#ffd13e',
'#a61187',
'#24ad9a',
'#7c7474',
'#a66111',
'#222222',
'#115ea6',
'#94cc0a',
'#b61120',
'#dd8809',
'#11d13e',
'#a68887',
'#94df9d',
'#7f74f4',
'#112341',
'#abcdef1'
],
store: 'relativedata',
series: [
{
type: 'pie',
label: {
textBaseline: 'middle',
textAlign: 'center',
font: '9px Helvetica'
},
labelField: 'strName',
labelOverflowPadding: 0,
xField: 'numValue'
}
],
interactions: [
{
type: 'rotate'
}
],
listeners: [
{
fn: function(element, eOpts) {
var relStore = Ext.getStore('relativedata');
var eleStore = Ext.getStore('element');
var relModel;
var eleModel;
relStore.removeAll();
//Convert to CO2 qty
for(var i = 0; i< eleStore.getCount();i++)
{
eleModel = eleStore.getAt(i);
relModel = Ext.create(APPNAME + '.model.RelativeElement');
relModel.set('strName',eleModel.get('strName'));
relModel.set('numValue', eleModel.get('numValue')*eleModel.getFactor());
relStore.add(relModel);
}
relStore.sync();
//Hide arrows-legend
this._series[0]._label.attr.hidden=true;
},
event: 'painted'
}
],
legend: {
xtype: 'legend',
docked: 'bottom',
itemId: 'pie_legend',
itemTpl: [
'<span class="x-legend-item-marker {[values.disabled?\'x-legend-inactive\':\'\']}" style="background:{mark};"></span>{name}'
],
maxItemCache: 100,
store: 'element'
}
}
]
}
I ask for help because i'm not that good with templates. I would not dare say I understand everything of the default one actually.
I'm back! Yet, nobody's calling me slim shaddy for that... Unluckily!
So, to answer your initial question, the template you need would be something like the following:
// Configuration of the chart legend
legend: {
// Finally, we can use the value field to customize our templates.
itemTpl: [
'<tpl if="value != 0">', // <= template condition
'<span class="x-legend-item-marker {[values.disabled?\'x-legend-inactive\':\'\']}" style="background:{mark};"></span>{name}',
'</tpl>'
]
// ...
}
Unfortunately, as I've said in my previous comment, quick debugger inspection shows that this value variable, or any equivalence, is not available at the time this template is applied.
Now I'm going to give you a detailed explanation about how I was able to overcome this vexation. In part because this is such an involved hack that you'd better know what you're doing if you decide to apply it, and in part because you'll learn a lot more by witnessing the fishing techniques than by being given the fish right away -- in this case, the fish is not available for retail anyway. And also in a large part, I must confess, because I like to be lyrical about things I've put some energy in, and it's late, and my defenses against self congratulation have gotten a bit weak...
So, looking at Ext.chart.Legend's code shows that there's nothing to be done there, it's just a somewhat lightweight extension of Ext.dataview.Dataview. As such it must have a store bounded to it, which, obviously (and unfortunately), is not the one bound to the chart to provide its data.
Another judicious breakpoint (in the Legend's setStore method) shows that this store comes from Ext.chart.AbstractChart, and in the code of this class we can see two things: a dedicated legend store is created in the constructor, and chart series implement a method to feed this store, namely provideLegendInfo.
We're getting closer to our goal. What we need to do is add a value field to the legend store, and have our serie provide the data for this field. Great!
The wise approach now would be to implement these modifications with the minimal amount of replication of Ext's code... But after having spent an inconsiderate amount of time trying to do that with no luck, I'll just settle for wildly overriding these two methods, and giving the advice to put a big bold warning to check that the code of these methods doesn't change with the next versions of Touch:
if (Ext.getVersion().isGreaterThan('2.2.1')) {
// Give yourself a big warning to check that the overridden methods' code
// bellow has not changed (see further comments).
}
With that out of the way, let's go to the point without any further consideration for future generations.
That is, first we add a value field to the legend store:
/**
* Adds a value field to legend store.
*/
Ext.define(null, {
override: 'Ext.chart.AbstractChart'
// Berk, what a lot of code replication :( Let's just hope that this method's code
// won't change in the future...
,constructor: function() {
var me = this;
me.itemListeners = {};
me.surfaceMap = {};
me.legendStore = new Ext.data.Store({
storeId: this.getId() + '-legendStore',
autoDestroy: true,
fields: [
'id', 'name', 'mark', 'disabled', 'series', 'index'
// Adding my value field
,'value'
]
});
me.suspendLayout();
// For whatever reason, AbstractChart doesn't want to call its superclass
// (Ext.draw.Component) constructor and, by using callSuper, skips directly to
// Ext.Container's one. So well... I respect, but I must do it old school since
// callSuper would go to Ext.draw.Component from here.
Ext.Container.prototype.constructor.apply(this, arguments);
// me.callSuper(arguments);
me.refreshLegendStore();
me.getLegendStore().on('updaterecord', 'onUpdateLegendStore', me);
me.resumeLayout();
}
}, function() {
// Post-create functions are not called for overrides in touch as they are
// in ExtJS? Hmm... That would have been the perfect place to issue a big
// warning in case the version has changed, but we'll live with it :(
});
And, second, we make our chart serie feed that value. From your code, I can deduce that you're working with a pie chart, so I'm only giving the code for that, as a matter of illustration... But, if you've followed until here, it should be trivial to implement it for other kind of series. Anyway, here's the code:
/**
* Overrides `provideLegendInfo` to add the value to the legend records.
*
* Here again, let us all cross our fingers very hard, hoping for Sencha's team to not decide
* to add their own extra fields too soon...
*/
Ext.define(null, {
override: 'Ext.chart.series.Pie'
,provideLegendInfo: function(target) {
var store = this.getStore();
if (store) {
var items = store.getData().items,
labelField = this.getLabelField(),
field = this.getField(),
hidden = this.getHidden();
for (var i = 0; i < items.length; i++) {
target.push({
name: labelField ? String(items[i].get(labelField)) : field + " " + i,
mark: this.getStyleByIndex(i).fillStyle || this.getStyleByIndex(i).strokeStyle || 'black',
disabled: hidden[i],
series: this.getId(),
index: i
// Providing actual data value to the legend record
,value: items[i].get(field)
});
}
}
}
});
Let's sum it up. We've got two overrides and a custom template. We could hope that we'd be done by now. But here's what we get:
So, the DataView is adding some markup of its own around the itemTpl's markup. Well, well, well... At this point, I'm tired of tracking Ext's internals and, fortunately (for once!), I envision a quick patch for this. So that is without an hesitation that I'm throwing this CSS rule in:
.x-legend-item:empty {
display: none;
}
And finally we're done. I guess my line of thought and code might be a little tricky to replicate, so let me provide you with a definitive proof that this all works.
In this demo, there is a "metric four" that has a value of 0.
{
'name': 'metric four',
'data': 0
}
But you won't see it. Because that was the point of all this, wasn't it?

how to wrap text of selectfield option in sencha touch 2

I'am trying to display Sencha Touch 2 selectfield with very long option text but text gets truncated with Ellipsis, like Very long option t....
How to display couple lines in one option?
Code:
{
xtype: 'selectfield',
options:[
{
text: 'Very long option I wish to splint into two separate lines',
value: 0
},
{
text: 'Another very long option I wish to splint into two separate lines',
value: 1
}
]
}
I've tried using \n and <br/> but is not working.
There are 3 two ways to do this.
use labelWrap config option set to true.
This will avoid truncating text that appears on selectfield initially. Later when you tap on selectfield; you've two choices. Using picker or list. picker will be used only if you set it to true in usePicker config. If you are on tablet, desktop or mobile default list will be shown containing options. Using labelWrap config will not be usefull if options are displayed in list after tap on selectfield.
Use following CSS override to avoid truncating.
.x-list-item-label{
height: 100% !important;
}
.x-list-label{
white-space: pre-wrap !important;
}
This override along with above mentioned labelWrap config set to true will make both list and selectfield display whole text neatly. But this will override styles that may affect appearance of other lists in your app.
Third approach that can be is to override Ext.field.Select and create custom select field. In this style, you need to just override following method - getTabletPicker that generated the list displayed on tap of selectfield. Code from ST source is as fallows -
getTabletPicker: function() {
var config = this.getDefaultTabletPickerConfig();
if (!this.listPanel) {
this.listPanel = Ext.create('Ext.Panel', Ext.apply({
centered: true,
modal: true,
cls: Ext.baseCSSPrefix + 'select-overlay',
layout: 'fit',
hideOnMaskTap: true,
items: {
xtype: 'list',
store: this.getStore(),
itemTpl: '<span class="x-list-label">{' + this.getDisplayField() + ':htmlEncode}</span>',
listeners: {
select : this.onListSelect,
itemtap: this.onListTap,
scope : this
}
}
}, config));
}
return this.listPanel;
}
Check out the line itemTpl and cls config. Here both options set styles that are defined for list. These will decide the appearance of list displayed on tap of selectfield. This approach might sound dirty. But it's useful, if you want to make some drastic changes in appearance and behaviour.

ExtJs - How to remove all floating components

Using ExtJs 4.1
Is there a way to query all floating components (windows, message boxes, etc.)?
My aim is to remove (destroy) all floating objects. It would be sufficient to "get" them on the first hand.
Well simply do it by using the Ext.WindowManager which is responsible for all floating components by default.
Following should work:
Ext.WindowManager.each(function(cmp) { cmp.destroy(); });
Here's a example JSFiddle:
Ext.create('Ext.window.Window', {
title: 'Hello',
height: 200,
width: 400,
layout: 'fit',
items: { // Let's put an empty grid in just to illustrate fit layout
xtype: 'grid',
border: false,
columns: [{header: 'World'}], // One header just for show. There's no data,
store: Ext.create('Ext.data.ArrayStore', {}) // A dummy empty data store
}
}).show();
Ext.Function.defer(function(){Ext.WindowManager.each(function(cmp) { cmp.destroy(); })}, 5000);
For further reading on DOM-Query
Edit destroy only defined types
For that case go with the xtype of the component and check it.
Ext.WindowManager.each(function(cmp) { if (cmp.xtype === 'window') cmp.destroy(); });

Resources