I have two classes in ExtJS 4 (a grid and a form) with correlated properties (column and field definitions).
I want to create a common definition file for them and dynamically load it:
# app/common/Columns.js
Ext.ns('myapp.common');
myapp.common.Columns =
[
{
text: 'Name',
dataIndex: 'name',
},
{
text: 'Email',
dataIndex: 'email',
}
];
# app/view/Grid.js
Ext.define
(
'myapp.view.Grid',
{
extend: 'Ext.grid.Panel',
columns: myapp.common.Columns
}
);
# app/view/Form.js
Ext.define
(
'myapp.view.Form',
{
extend: 'Ext.form.Panel',
items: myapp.common.Columns.map
(
function(v)
{
return {
name: v.dataIndex,
fieldLabel: v.text,
};
}
);
}
);
As you can see, app.common.Columns is not a class.
I know I can convert it into a class and override the form's and the grid's constructors to use it, but I'd like to keep it simple.
Do I need to add a <script> tag for app/common/Columns.js manually or there is a way to load it dynamically?
I don't think you can load file dynamically using ExtJs loader without having a class defined there. Their loader is very picky about exactly matching class name to file name. I just tried your sample - it doesn't seems to be working.
Related
This is a follow up question that I got answered here: How can I programmatically set column filters?
I have a 188 line Ext.js view. In this view I extend Ext.grid.Panel and in this grid I have set the selModel like so ...
selModel: {
cellSelect: false, // Only support row selection.
type: 'spreadsheet' // Use the new "spreadsheet" style grid selection model.
},
On one of the columns, the Status column, I am programmatically setting the filter so that only rows that have the Status of Ready will appear when the page firsts renders. I have been doing this here in the code:
columns: [
...
{
text: 'Status',
dataIndex: 'status',
itemId: 'status',
renderer: function(value, metaData) {
var filter = this.up('panel').down('#status').filter;
if (!filter.menu) {
filter.createMenu();
filter.menu
.down('menuitem[value="Ready"]')
.setChecked(true);
}
metaData.tdStyle = (value == 'Ready') ?
'color:green;font-weight: bold' :
'color:red;font-style: italic'
return(value)
},
filter: 'list',
flex: 1,
},
... more columns ...
A helpful SO member pointed out that is not the most efficient place for the code that sets the filter as the code will be executed for each row in the grid.
I have tried adding an afterrender function like so ...
{
text: 'Status',
dataIndex: 'status',
itemId: 'status',
renderer: function(value, metaData) {
metaData.tdStyle = (value == 'Ready') ?
'color:green;font-weight: bold' :
'color:red;font-style: italic'
return(value)
},
filter: 'list',
flex: 1,
listeners: {
afterrender: function(value) {
Ext.Msg.alert('We have been rendered value is ' + value );
var filter = this.up('panel').down('#status').filter;
if (!filter.menu) {
filter.createMenu();
filter.menu
.down('menuitem[value="Ready"]')
.setChecked(true); //Uncaught TypeError: Cannot read property 'setChecked' of null
}
}},
... but that results in this error message, //Uncaught TypeError: Cannot read property 'setChecked' of null.
What am I doing wrong here? Do I need the listeners:? Am I not getting passed the data I think I am getting passed to my afterrender function? Should I defining a initComponent function?
UPDATE:
I changed my code to what DrakeES suggested, ...
{
text: 'Status',
dataIndex: 'status',
itemId: 'status',
renderer: function(value, metaData) {
metaData.tdStyle = (value == 'Ready') ?
'color:green;font-weight: bold' :
'color:red;font-style: italic'
return(value)
},
flex: 1,
filter: {
type: 'list',
value: 'Ready'
}
... but the result is this:
Where the animated loading image just sits there and spins. This prevents the user from be able to change the filter interactively. I wonder what it is I am doing wrong here?
I am programmatically setting the filter so that only rows that have
the Status of Ready will appear when the page firsts renders
What checking the filter's checkbox effectively does is setting filter on the store. Because you want the filter to be applied initially, it would be better to have it in the store config right away:
filters: [
{
id: 'x-gridfilter-status',
property: 'status',
value: 'Ready'
}
]
That way the grid view appear filtered in the first place — instead of initially showing all rows and only then filtering them out once the column menu renders and applies the filter. Note that having id: 'x-gridfilter-status' on the store's filter is required so that the column's filter picks it up instead of creating a duplicate.
Setting filter on the store, however, will not send feedback to the column filter menu, so the latter will remain unchecked unless you explicitly check it. Therefore, you still need an afterrender handler on either the grid or the column to make things look in sync.
A simple and elegant solution without listeners and stuff:
filter: {
type: 'list',
value: 'Ready'
}
Full working example: https://fiddle.sencha.com/#fiddle/prp
Our application uses the Ext.grid.Panel to display rows of data. When the user clicks a "New..." button we are adding a new record to the grid store. Everything is working fine.
However, on certain fields, of just this new (as of yet) unsynced records, we would like to display specific empty text within certain cells. For example: 'Enter title here' or 'Choose a platform".
I know there is an emptyCellText property on Column, but a) its broken, and b) I'd like to have this happen only on new, unsynced (e.g. phantom) records.
As I am new to this framework, any ideas are most welcome.
You can add this text in column renderer. You can test if record column value is not set and record is phantom. In this case you can add to column your placeholder text:
columns: [
{
header: 'Name',
dataIndex: 'name',
editor: {
xtype: 'textfield',
emptyText: 'Enter name'
},
renderer: function(value, metaData, record) {
if (!value && record.phantom) {
return 'Enter name';
} else {
return value;
}
}
}
]
Edit
For displaying empty text also in editor fields just use emptyText config property in editor configuration. In editor configuration you can define by xtype which type of field will be used (textfield, numberfield, etc.) and add any of field config propertis:
editor: {
xtype: 'textfield',
emptyText: 'Enter name',
// other filed config properties...
},
Fiddle with live example: https://fiddle.sencha.com/#fiddle/3b5
I have to make a diagram editor, so I'm using AlloYUI, I've added a custom node following the answer for this question.
I've successfully set new nodes and retreive it's values via diagramBuilder.toJSON()
What I'm trying to do is change the default editor widget for the custom attribute of my custom node, I'd like to change the textbox for date picker widget, but my goal is being able to use any other form elements, like a select, or a set of radio buttons.
Toying around with the javascript debugger included in WebStorm, I've found that the default fields have an 'editor' attribute where is defined a "textAreaCellEditor".
But my custom property doesn't have this attribute, So I'm trying to attach an editor, but I cannot get it to work, I've tried with this:
Y.DiagramNodeCustom = Y.Component.create({
NAME: 'diagram-node',
ATTRS: {
type: {
value: 'custom'
},
custom_attr: {
value: 'customAttr'
}
},
EXTENDS: Y.DiagramNodeTask,
prototype: {
getPropertyModel: function () {
var instance = this;
var model = Y.DiagramNodeTask.superclass.getPropertyModel.apply(
instance, arguments);
model.push({
attributeName: 'customAttr',
name: 'Custom Attribute',
editor: Y.Component.create({
NAME: "DateField",
EXTENDS: Y.DateCellEditor
})
});
return model;
},
SERIALIZABLE_ATTRS: ['description', 'name', 'required', 'type',
'width', 'height', 'zIndex', 'xy', 'customAttr']
}
});
Y.DiagramBuilder.types['custom'] = Y.DiagramNodeCustom;
I've also tried to change the "model.push" section to:
model.push({
attributeName: 'customAttr',
name: 'Custom Attribute',
editor: {name: "textCellEditor"}
});
and to:
model.push({
attributeName: 'customAttr',
name: 'Custom Attribute',
editor: Y.DateCellEditor({
name: 'DateCellEditor'
})
});
But nothing works. Do you have any idea how can I change the default editor?
Thanks to Robert Frampton, who anwered my question in google groups, the way to do it is:
model.push({
attributeName: 'customAttr',
name: 'Custom Attribute',
editor: new Y.DateCellEditor()
});
You have to create a new instance of the Y.DateCellEditor object with adding 'new' before the constructor.
I am trying to get Ext.define & Ext.create working in Sencha touch 2, so that I can define everything in my library and just create stuff pre-configured.
However, Ext.define is not doing what I would expect it to in anything I've tried.
Why does the following code not create a panel inside the viewport with the field label "Tame"?
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
items: [{
xtype: 'textfield',
name: 'Tame',
label: 'Tame'
}
]
});
Ext.application({
viewport: {
layout:'fit'
},
launch: function(){
var form = Ext.create('Ext.form.Panel', {
items: [{
xtype: 'textfield',
name: 'name',
label: 'Name'
}
]
});
Ext.Viewport.add(Ext.create('mobi.form.Login')); // This doesnt add anything to the viewport
Ext.Viewport.add(form); //magically this works
}
})
When using Ext.define, all configurations must go inside the config block. So your code should look like this:
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
config: {
items: [{
xtype: 'textfield',
name: 'Tame',
label: 'Tame'
}
]
}
});
In general the only exceptions to this are:
extend
requires
xtype
singleton
alternateClassName
Anything else should be inside the config object, but remember, only when using Ext.define.
It looks like you are trying to use the sencha MVC concept but this is wrong if this is your only piece of code.
First create the following folder structure:
MyAppFolder
index.html (include the sencha lib here)
app.js (main file)
app (folder)
controller
Main (main controller)
model (optional if no model is defined)
store (optional if no model is defined)
view
Viewport.js (your main viewport)
resources
css
style.css (your custom style)
images (your icons and images if you have)
Then in your app.js you would define something like this:
// enable loader for dynamic loading of .js classes
Ext.Loader.setConfig({
enabled : true,
paths : {
}
});
/**
* Better performance is achived when knowing which .js classes we need to load prior to execution of this class.
*/
Ext.require([
]);
/**
* This is the definition of our mobile application.
* The name of this app is MVCTest.
*/
Ext.application({
name : 'MVCTest',
controllers : ['Main'],
views : ['Viewport'],
launch : function() {
Ext.create('MVCTest.view.Viewport');
}
});
Then your main controller:
Ext.define('MVCTest.controller.Main', {
extend : 'Ext.app.Controller',
config : {
refs : {
viewport : 'mvctest-viewport'
}
}
});
Then your viewport would look something like this, according to your example:
Ext.define('MVCTest.view.Viewport', {
extend : 'Ext.Container',
xtype : 'mvctest-viewport',
config : {
fullscreen : true,
layout : 'card',
items:
[
{
xtype: 'textfield',
name: 'name',
label: 'Name'
},
{
xtype: 'mvctest-tame'
}
]
}
});
By specifying the xtype mvctest-tame it will search for this xtype and add this in as a new item to this card. So you need the tame view:
Ext.define('MVCTest.view.Login',{
extend:'Ext.form.Panel',
xtype: 'mvctest-tame',
items: [{
xtype: 'textfield',
name: 'Tame',
label: 'Tame'
}
]
});
And do not forget to add the Login view to the app.js..
I am trying to get working a simple (noob) examle of Combo loaded with data from Xml file.
Here is my xml:
<?xml version="1.0" encoding="UTF-8"?>
<accounts>
<account>
<name>Savings Account</name>
<id>1</id>
</account>
<account>
<name>Current Account</name>
<id>2</id>
</account>
</accounts>
When I configure and add XmlStore, it reports 2 records found.
Here is the code for the XmlStore:
cteo = Ext.extend(Ext.data.XmlStore, {
constructor: function(cfg) {
cfg = cfg || {};
cteo.superclass.constructor.call(this, Ext.apply({
storeId: 'cteo',
url: 'cteo.xml',
record: 'account',
data: '',
fields: [
{
name: 'name',
mapping: 'name'
},
{
name: 'id',
mapping: 'name'
}
]
}, cfg));
}
});
new cteo();
finally, this is the code for the combo:
MyPanelUi = Ext.extend(Ext.Panel, {
title: 'My Panel',
width: 400,
height: 250,
initComponent: function() {
this.items = [
{
xtype: 'label',
text: 'Cuenta Origen'
},
{
xtype: 'combo',
store: 'cteo',
displayField: 'name',
valueField: 'id'
}
];
MyPanelUi.superclass.initComponent.call(this);
}
});
It must be something simple, but I am stuck...
This will not do anything:
store: 'cteo',
You need to pass in the object reference that you assigned earlier, not a string:
store: cteo,
Alternately, you could call Ext.StoreMgr.lookup('cteo'), but judging by your code I assume that the variable reference was your intention.
One comment on your code. Doing this:
cteo = Ext.extend(Ext.data.XmlStore, {
...
cteo();
...is a bit strange, and is most likely creating a global variable in the window scope (assuming that cteo is not defined as a var somewhere earlier). Think of it as defining a custom class, then creating a new instance of the class you defined. Also, think about your naming -- a store subclass should be a specific type of store, which should be evident in the name. Typically, your code should look more like this:
Ext.ns('MyNamespace');
MyNamespace.CteoStore = Ext.extend(Ext.data.XmlStore, {
...
});
var cteoStore = new CteoStore();
Oh yeah, one other thing. You do not need to override the constructor for the sole purpose of providing default configs. Just do this:
MyNamespace.CteoStore = Ext.extend(Ext.data.XmlStore, {
storeId: 'cteo',
url: 'cteo.xml',
record: 'account',
data: '',
fields: [
{
name: 'name',
mapping: 'name'
},
{
name: 'id',
mapping: 'name'
}
]
});
This is also more useful since these configs are overrideable, unlike in your example. This makes it more reusable (like if you ever wanted to assign a different id to another instance).
Check out this thread at sencha site:
http://www.sencha.com/forum/showthread.php?105818-(noob)-Populating-combo-from-XmlStore-with-Ext-js-designer