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!
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 defined a Ext.grid.Panel called JobList that has an Ext button with an itemId called myButton. JobList has a controller. In the controller I have the following code:
Ext.define('App.controller.JobList', {
extend: 'Ext.app.Controller',
refs: [
{ref: 'jobList', selector: '#jobList'},
{ref: 'myButton', selector: '#myButton'}
],
init: function(){
this.control({
'jobList': {
select: this.selectJob
}
});
},
selectJob: function(){
this.getMyButton().enable();
}
});
I then create two instances of jobList using Ext.create they have an id of jobList1 and jobList2. The problem is when I select a job in the list on jobList2 it will enable the myButton on jobList1 not jobList2. How do I correctly enable the myButton on each instance of jobList?
Try to avoid referencing by itemId, and use aliases instead:
// in App.view.JobList.js you should have
Ext.define('App.view.JobList', {
extend: 'Ext.grid.Panel',
alias: 'widget.job-list',
// ...
dockedItems: [{
xtype: 'button',
name: 'myButton',
text: 'My button',
}]
});
// and the in the App.controller.JobList.js:
// ...
'job-list': {
selectionchange: function(model, selected) {
var button = model.view.up('job-list').down('button[name=myButton]');
button.setDisabled(Ext.isEmpty(selected));
}
}
Check the example: https://fiddle.sencha.com/#fiddle/tq1
You're using global controller, so it catches events from all views that matching the query. Look at MVVM pattern in extjs5. Sencha did a great job, in MVVM each instance of view has their own instance of ViewController, so this situation will never happen. If you want to stick with MVC pattern, then you need to manually control this. Forget about refs, you can't use them if you have more than one instance of your view class. Get other components only by query from your current component. Something like:
Ext.define('App.controller.JobList', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'jobList': {
select: this.selectJob
}
});
},
selectJob: function(selectionModel){
//first of all you need to get a grid. We have only selectionModel in this event that linked somehow with our grid
var grid = selectionModel.view.ownerCt; //or if you find more ellegant way to get a grid from selectionModel, use it
var button = grid.down('#myButton');
button.enable();
}
});
I’m trying to add different detail view based on taped item in list.
I have home screen that is using List component and this view is displaying ['Real estate', 'Vehicles', 'Jobs']... as menu items.
Based on selected item in list, I want to display different view.
And I want to follow MVC design pattern..
Here is some code...
App.js
Ext.application({
name: 'App',
controllers: ['Main'],
views: ['Viewport', 'HomePage'],
stores: ['HomePageItems'],
models: ['HomePageItem'],
launch: function () {
Ext.Viewport.add(Ext.create('App.view.Viewport'));
}
});
Viewport.js
Ext.define("App.view.Viewport", {
extend: 'Ext.navigation.View',
requires: [ 'App.view.realestate.Realestate',
'App.view.HomePage',
'App.view.jobs.Jobs',
'App.view.other.Other',
'App.view.vehicles.Vehicles'
],
config: {
items: [
{
xtype: 'homepage'
}
]
}
});
HomePage.js ( xtype = "homepage" )
Ext.define('App.view.HomePage', {
extend: 'Ext.List',
xtype: 'homepage',
id: 'homepage',
config: {
title: 'Oglasi',
itemTpl: '<strong>{name}</strong><p style="color:gray; font-size:8pt">{description}</p>',
store: 'HomePageItems',
onItemDisclosure: true
}
});
Main.js
Ext.define('App.controller.Main', {
extend: 'Ext.app.Controller',
config: {
refs: {
main: '#homepage'
},
control: {
'homepage': {
disclose: 'HookUpDetailView'
}
}
},
HookUpDetailView: function (element, record, target, index, ev) {
// TO DO: I have 4 differente views to load programmaticaly based on selected item in List
//'App.view.realestate.Realestate'
//'App.view.jobs.Jobs'
//'App.view.other.Other'
//'App.view.vehicles.Vehicles'
}
});
I found one example, but it's not working for me (push method doesn't exist)
this.getMain().push({
xtype: 'realestatehome'
});
Thank in advance!
The method you're looking for is
http://docs.sencha.com/touch/2-0/#!/api/Ext.Container-method-add
this.getMain().add({xtype: 'realestatehome'});
But what you have doesnt make sense, realestatehome is a list, you can't add a component under it. You need to read about layoutsFrom the link above
Push should work. You could try something like this.
HookUpDetailView: function (list, record) {
if(record.data.description==='real estate'){
this.getRealEstate().push({
xtype: 'realestatehome',
title: record.data.description,
data. record.data
});
if(record.data.description==='Vehicles'){
this.getVehicles().push({
xtype: 'vehicles',
title: record.data.description,
data. record.data
});
}
}
And a particular view could look like
Ext.define('App.view.RealEstateHome', {
extend: 'Ext.Panel',
xtype: 'realestatehome',
config: {
styleHtmlContent: true,
scrollable: 'vertical',
tpl: [
'{description}, {example}, {example}, {example}'
]
}
});
And the refs to access your particular view should look something like
refs: {
realEstate: 'realestatehome',
vehicles: 'vehicleshome'
},
Hope that helps
I made a mistake in controller this.getMain()
get main is returning Ext.List and I need Ext.navigation.View that have 'push' method.
So... I added xtype and id to my viewport ("container")
and quick change in my controller solved my troubles...
refs: {
main: '#homepage',
container: '#container'
}
and instead of getting Ext.List object
this.getContainer().push({
xtype: 'realestatehome'
});
And this work like a charm :)