i have a simple template:
tpl: '<table><tr><td>{LogicalCondition}</td></tr></table>',
Instead of {LogicalCondition} I want to place extjs component (dynamically).
(http://jsfiddle.net/4t458yxo/2/)
Ext.define('MyApp.view.ux.form.BoolDisplayField', {
extend: 'Ext.Component',
alias: 'widget.booldisplayfield',
tpl: '<table><tr><td>{LogicalCondition}</td></tr></table>',
constructor: function (config) {
this.initConfig(config);
this.callParent(arguments);
},
applyValue: function (v) {
if (v) {
this.update({
//instead of combo i am getting [object object]
LogicalCondition: Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose State',
store: ["a"],
queryMode: 'local',
displayField: 'name'
})
});
}
return v;
}
});
var c = Ext.create('MyApp.view.ux.form.BoolDisplayField', {
renderTo: Ext.getBody(),
value: false
});
c.setValue(true);
Ext (X)Template does not allow components in its data but plain objects or arrays are assumed. Ext when processing the new data for the template (update call) understands "LogicalCondition" as text - you get object (combo instance) converted to text ([object object]).
Most likely, there is another way how to achieve what you want, (X)Template/data is obviously not the best one.
You could use a controller? Create the component you need dynamically when you need it and just use add() to a Container with table layout?
http://docs.sencha.com/extjs/4.2.2/?mobile=/api/Ext.container.AbstractContainer#method-add
Related
A component definition:
Ext.define('Retroplanner.view.dimension.DimensionMapping', {
alias: 'widget.dimensionMapping',
extend: 'Ext.form.Panel',
...
items: [{
xtype: 'combo'
}, ...
]
A 'select' handler of the child item must create a widget and add this widget to the items array of its parent.
Inside of this child item, it its 'select' handler, I can find its parent by some search techniques. But I would like to avoid it if it is possible. I do not have a reference variable to the parent neither.
A better approach would be - to create function in the parent, and attach it somehow to the child item:
Ext.define('Retroplanner.view.dimension.DimensionMapping', {
...
onSiRemoteCombo: function(cmb, rec, idx) {
alert("select handler");
var newItem = Ext.widget('somexType');
this.items.add(newItem);
}
The question, how to attach onSiRemoteCombo?
I've found a similar solution here: How to create listener for child component's custom event
First, it does not work for me. I can give a full example that I tried to use.
2nd, I would like to create items via the most common way/in the common place, not via initComponent method. I would like to have something like:
Ext.define('Retroplanner.view.dimension.DimensionMapping', {
...
afterRender: function() {
var me = this;
//exception here
//Uncaught TypeError: Cannot read property 'on' of undefined
me.items[0].on('select', onSiRemoteCombo, this);
},
items: [{
xtype: 'combo'
}, ...
],
onSiRemoteCombo: function(cmb, rec, idx) {
alert("Ttt");
var dimensionMapping = Ext.widget('propGrid');
this.getParent().add(dimensionMapping);
}
But I get an exception:
//exception here
//Uncaught TypeError: Cannot read property 'on' of undefined
me.items[0].on('select', onSiRemoteCombo, this);
And also, attach a listener after each rendering, really is a bad idea.
Are there any best practices for such use cases? Ideally, if it will work in different versions of Ext JS, at least in 5.x and 6.x
Attach a handler in a child and access its parent? A child should not depend on its parent. Only parent should know, what to do.
One way to solve this is by wrapping the combo item component initialization into form's initComponent method. This way when setting a listener for the combo, you can use this.formMethod to reference a form method. Here is some code:
Ext.define('Fiddle.view.FirstForm', {
extend: 'Ext.form.Panel',
bodyPadding: 15,
initComponent: function () {
Ext.apply(this, {
items: [{
xtype: 'combo',
fieldLabel: 'First Combo',
store: ['first', 'second'],
listeners: {
'select': this.onComboSelect
}
}]
});
this.callParent();
},
onComboSelect: function () {
alert('I am a first form method');
}
});
The second approach by using a string listener on the combo, and by setting defaultListenerScope to true on the form. This way the listener function will be resolved to the form's method. Again, some code:
Ext.define('Fiddle.view.SecondForm', {
extend: 'Ext.form.Panel',
bodyPadding: 15,
defaultListenerScope: true,
items: [{
xtype: 'combo',
fieldLabel: 'Second Combo',
store: ['first', 'second'],
listeners: {
'select': 'onComboSelect'
}
}],
onComboSelect: function () {
alert('I am a second form method');
}
});
And here is a working fiddle with both approaches: https://fiddle.sencha.com/#view/editor&fiddle/27un
I am trying to reference a store in my app for the purpose of adding a paging tool bar at the bottom of my gird. In most examples I have studied the store is referenced by variable, ex: store: someStore. However, by I have build my app a little differently and did create a reference variable to the store. I have
tried assigning an id but this did not work.
Here is what I have:
In my view Grid.js:
Ext.define('myApp.view.user.Grid', {
extend: 'Ext.grid.Panel',
viewModel: {
type: 'user-grid'
},
bind: {
store: '{users}',
},
columns: {...},
//my paging tool bar
dockedItems: [{
xtype: 'pagingtoolbar',
dock: 'bottom',
store: 'girdStore'
//store: {users} -> did not work
}],
...
});
In my view model GridModel.js:
Ext.define('myApp.view.user.GridModel', {
extend: 'Ext.app.ViewModel',
requires: [
'myApp.model.User'
],
stores: {
users: {
model: 'myApp.model.User',
storeId: 'gridStore',
autoLoad: true
}
},
formulas: {...}
});
When I try to reference the {users} store by id 'gridStore' I get this error:
Uncaught TypeError: Cannot read property 'on' of undefined
What is the best way to proceed without completely refactoring my model?
When you have a reference to your grid, you could get the store by calling the getStore function. See the ExtJs 6.2.1 documentation.
var grid; // reference to your grid
var store = grid.getStore();
You can create the store in initComponent and then attach it to the dockedItems, so both will share the same store.
initComponent: function () {
var store = Ext.create('Ext.data.Store', {
model: 'myApp.model.User',
storeId: 'gridStore',
autoLoad: true
});
this.store = store;
this.dockedItems = [{
xtype: 'pagingtoolbar',
dock: 'bottom',
store:store
}];
this.callParent(arguments);
}
The initComponent is called once when a new instance of the class is created, see the description in the documentation.
...It is intended to be implemented by each subclass of
Ext.Component to provide any needed constructor logic. The
initComponent method of the class being created is called first, with
each initComponent method up the hierarchy to Ext.Component being
called thereafter. This makes it easy to implement and, if needed,
override the constructor logic of the Component at any step in the
hierarchy. The initComponent method must contain a call to callParent
in order to ensure that the parent class' initComponent method is also
called...
The view with the initComponent function.
Ext.define('myApp.view.user.Grid', {
extend: 'Ext.grid.Panel',
viewModel: {
type: 'user-grid'
},
initComponent: function () {
var store = Ext.create('Ext.data.Store', {
model: 'myApp.model.User',
storeId: 'gridStore',
autoLoad: true
});
this.store = store;
this.dockedItems = [{
xtype: 'pagingtoolbar',
dock: 'bottom',
store: store
}];
this.callParent(arguments);
},
columns: {...},
...
});
Hello i am receiving an error from the code below, and not sure why because i thought i was defining it. I want to make sure my code is working properly before i add complexity to the report.
launch: function() {
this._createGrid();
},
_createGrid: function() {
Ext.create('Rally.data.wsapi.TreeStoreBuilder').build({
models: ['PortfolioItem/Initiative'],
autoLoad: true,
enableHierarchy: true
}).then({
success: function(store) {
var myGrid = Ext.create('Ext.Container', {
items: [{
xtype: 'rallytreegrid',
columnCfgs: ['Name', 'Owner'],
store: store
}],
renderTo: Ext.getBody()
});
}
});
this.add(myGrid);
},
});
"Error: success callback for Deferred transformed result of Deferred transformed result of Deferred threw: ReferenceError: myGrid is not defined"
I am new to this so any help would be greatly appreciated!
You issue you're running into is probably due to some confusion in how components and containers behave in ExtJS combined with the this scoping issue mentioned in the answer above.
Here's how I would write it:
_createGrid: function() {
Ext.create('Rally.data.wsapi.TreeStoreBuilder').build({
models: ['PortfolioItem/Initiative'],
autoLoad: true,
enableHierarchy: true
}).then({
success: function(store) {
//The app class is already a container, so you can just
//directly add the grid to it
this.add({
xtype: 'rallytreegrid',
itemId: 'myGrid',
columnCfgs: ['Name', 'Owner'],
store: store
});
},
scope: this //make sure the success handler executes in correct scope
});
}
You also don't need to feel like you need to keep a myGrid reference around since you can always find it using the built-in component querying feature of ExtJS:
var myGrid = this.down('#myGrid');
You're defining myGrid inside of the success function scope, then trying to use it at the end of the _createGrid function, where it is undefined. I assume you're trying to do it that way because this is not bound correctly inside the success function. Try this instead:
_createGrid: function() {
var self = this;
Ext.create('Rally.data.wsapi.TreeStoreBuilder').build({
models: ['PortfolioItem/Initiative'],
autoLoad: true,
enableHierarchy: true
}).then({
success: function(store) {
var myGrid = Ext.create('Ext.Container', {
items: [{
xtype: 'rallytreegrid',
columnCfgs: ['Name', 'Owner'],
store: store
}],
renderTo: Ext.getBody()
});
self.add(myGrid);
}
});
},
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.
I am trying to create items inside a component as it gets initialized, with a function.
Consider the following:
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
config:{
items: [{
xtype: 'textfield',
name: 'Name',
label: 'Name'
}]
});
Ext.application({
viewport: {
layout:'fit'
},
launch: function(){
Ext.Viewport.add(Ext.create('mobi.form.Login'));
}
})
I am trying to get The mobi.form.login to generate its config from a function that runs on initialize ( or whatever I can use to over write the config I specify ).
I know Sencha touch 2 has the constructor, and initialize function, but both of them seem to have arguments=[] ( eg an empty array )
This is more or less how it would look if I was doing it in ExtJS 4.x:
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
initComponent:function(config){
config=Ext.apply({}.config,{});//make sure config exists
config.items= [{
xtype: 'textfield',
name: 'Name',
label: 'Name'
}]
Ext.apply(this, config);
this.callParent(arguments);
}
});
If you ever wanted to do this, you could use constructor or initialize.
Constructor you would use for synchronous logic which will be fast and you want to happen before the component is initialized. You can access the configuration through the constructors first argument:
Ext.define('MyComponent', {
extend: 'Ext.Component',
constructor: function(config) {
console.log(config);
this.callParent([config]);
}
});
Ext.application({
launch: function(){
Ext.create('MyComponent', { test: 1 })
// Will log out:
// {
// test: 1
// }
}
});
Remember you will always need to callParent with the config/arguments within constructor.
In any other situation, you should use initialize which is called after all the config's have been... initialized. :) We use this a lot internally for adding listeners.
initialize: function() {
this.on({
...
});
}
you don't need to call initialize manually it is already done by constructor and when calling this function you can access items data using this.items and create panel items there
Ext.define('mobi.form.Login',{
extend:'Ext.form.Panel',
config: {
items: []
},
initialize : function()
{
this.items = [Ext.create({
xtype: 'textfield',
name: 'Name',
label: 'Name'
})];
this.callParent();
}
});
Ext.application({
viewport: {
layout:'fit'
},
launch: function(){
Ext.Viewport.add(Ext.create('mobi.form.Login'));
}
})
Use the following:
Ext.apply(this, {
items: [
....
]
});
Have you tried something similar to this? I'm just passing a config object to Ext.create, though I can't test it right now. See http://docs.sencha.com/touch/1-1/#!/api/Ext-method-create
Ext.Viewport.add(Ext.create(
{
xtype: 'mobi.form.Login',
items: [ /*A list of items*/ ]
}
));
You could stick this snippet in its own function as well, one that takes in items as a parameter. Hope this solves your problem!