ExtJS 4.2.1 - issue with refs and controllers - javascript

I have my label defined as follows:
Ext.define('CapHour.view.CapHourLabel', {
extend: 'Ext.form.Label',
alias: 'widget.caphourlabel',
//itemId: 'capHourLabel',
itemId: 'label',
style: 'display:inline-block;text-align:center'
})
And my controller defined like this:
Ext.define('CapHour.controller.CapHourController', {
extend: 'Ext.app.Controller',
stores: ['CapHour'],
refs: [
{
ref: 'label',
selector: 'label'
}
],
init: function() {
var store = this.getCapHourStore();
store.on('load', function(records, operation, success) {
var label = Ext.ComponentQuery.query('#label')[0],
//var label = this.getCapHourLabel(),
hour = records.getAt(0).get('hour');
label.setText('CAP HOUR<br/>'+hour, false);
})
}
})
I can use Ext.ComponentQuery.query() just fine, but when I try to use this.getLabel(), the API call returns undefined. Is there something I'm forgetting? I tried setting the selector in the ref to 'label' and '#label' but I keep getting the same result.

You need to set the scope of the store callback to be the controller:
store.load({
scope: this,
callback: function(records) {
console.log(this.getLabel());
}
});
As a side note, you'll probably want a better selector. The current selector you have searches by xtype, globally, so that means 'grab the first thing you see that has an xtype label'.

Related

How to set custom component template and pass template arguments?

I want to create a custom component with some custom template. And I want this template to have some arguments. Now my code looks like so:
Ext.define('Ext.ux.TestComponent', {
extend:'Ext.Component',
alias: 'widget.testcomponent',
requires: [
'Ext.XTemplate'
],
constructor: function(config) {
Ext.applyIf(config, {
tpl: [
'<div class="custom_cls" custom_attr="{custom_attr_value}">Hello world</div>',
{compiled: true}
]
});
this.callParent([config]);
}
});
Ext.create("Ext.ux.TestComponent",{
renderTo: document.getElementById("component")
});
Here is a fiddle with this code. The first problem with this code is that my custom template is not rendered at all. The second problem, is that I do not know how to pass arguments to this template (and to set custom_attr in my example). When I extend Button, for example, I know I can use renderTpl and getTemplateArgs() and when I extend DataView, I can configure the template inside the constructor. But all this just does not work, when I extend Component. So, what is wrong with that and how can I fix it?
You need to define your template within the renderTpl configuration.
Additional data can be applied via renderData.
Ext.define('Ext.ux.TestComponent', {
extend: 'Ext.Component',
renderTpl: [
'<div class="custom_cls" custom_attr="{custom_attr_value}">Hello</div>'
],
renderData: {
custom_attr_value: "TestValue"
}
});
Here is the full example:
https://fiddle.sencha.com/#view/editor&fiddle/21eg
As an alternative to handing over the renderData object, you can implement an initRenderData function in your component.
This function is called by the mixin Ext.util.Renderable, which is used by Ext.Component.
Ext.define('Ext.ux.TestComponent', {
extend: 'Ext.Component',
renderTpl: [
'<div>{title}</div>'
],
config: {
title: 'default',
},
initRenderData() {
return this;
},
});
Ext.create("Ext.ux.TestComponent", {
renderTo: Ext.getBody(),
title: 'TestTitle'
});
Here is my full example for this alternative solution:
https://fiddle.sencha.com/#view/editor&fiddle/21en
tpl is used in conjunction with the data config.
Ext.define('Ext.ux.TestComponent', {
extend: 'Ext.Component',
tpl: [
'<div class="custom_cls" custom_attr="{custom_attr_value}">{content}</div>'
]
});
Ext.create("Ext.ux.TestComponent", {
renderTo: document.body,
data: {
custom_attr: 'x',
content: 'y'
}
});

ExtJS 5: Accessing bound store in component

I'm having a real hard time understanding how to access the actual bound store instance on a component, like a ComboBox or Grid. It appears that when you use their getStore method, it returns a different instance.
In my example, I have a custom grid class, and in its initComponent, I want to add a load listener. Unfortunately, the load listener never gets hit, but the bound store's load listener does. You can see the listener is getting set up before the store is loaded, so that's not the issue. It looks like the store instance in initComponent is a memory store, so the bound store's instance is not synced to the component at this point.
I know I can have a load listener on the bound store, but this is a custom grid class, and for any custom grid class, I want there to be a listener set up... so I don't want to have to do this all over the place in my code... I just want it in one class, as they're all going to have the same logic carried out.
Can someone explain to me why my custom grid class's load listener is not getting hit, and what can I do to fix this? Can I somehow access the bound store instance?
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('MyGrid', {
extend: 'Ext.grid.Panel',
xtype: 'myGrid',
initComponent: function() {
alert('initComponent')
this.callParent();
this.getStore().on('load', this.onLoad, this);
},
onLoad: function() {
alert('grid store loaded');
}
});
Ext.define('MyViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.myview',
onLoadBoundStore: function() {
alert('loaded bound');
}
})
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myview',
stores: {
myStore: {
fields: ['name', 'value'],
autoLoad: true,
proxy: {
type: 'ajax',
url: 'data1.json',
reader: {
type: 'json'
}
},
listeners: {
load: 'onLoadBoundStore'
}
}
}
});
Ext.define('MyView', {
extend: 'Ext.form.Panel',
renderTo: Ext.getBody(),
controller: 'myview',
viewModel: {
type: 'myview'
},
items: [{
title: 'blah',
xtype: 'myGrid',
reference: 'myComboBox',
bind: {
store: '{myStore}'
},
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Value',
dataIndex: 'value'
}]
}]
});
Ext.create('MyView')
}
});
Can someone explain to me why my custom grid class's load listener is
not getting hit, and what can I do to fix this?
You are not initially providing any store to your grid (the store config option is required by the way). Hence the grid is creating its own, empty store as coded here:
store = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');
That empty store is the one that gets your onLoad listener attached. It is not getting hit because the store is never loaded.
Can I somehow access
the bound store instance?
Yes, use this:
this.lookupViewModel().getStore('myStore')
You can attach your listener to the bound store instead of the empty one and see the difference. The grid's empty store eventually gets replaced by the bound one (this is why you see data in the grid, after all), but this happens after initComponent is executed. You can track the moment of setting the bound store to the grid by overriding setStore method.
I know I can have a load listener on the bound store, but this is a
custom grid class, and for any custom grid class, I want there to be a
listener set up... so I don't want to have to do this all over the
place in my code... I just want it in one class, as they're all going
to have the same logic carried out.
Since you are using MVVC it is recommended to stick to the pattern and keep views declaring view aspects only. Listeners should be declared and handled in controllers (not even in viewmodels like in your example). Otherwise, if you continue sticking to pre Ext JS 5 style and put dynamic stuff in views (i.e. the grid in your case) there is no point in using MVVC.
This is so incredibly hacky that I don't even want to post it as an answer, but I'm going to just for posterity sake. Building on Drake's answer, I figured out how to get my store using what was passed in the initial config of my grid class. The bind comes in with the braces, so that's why I'm doing a replace on it. Like I said, this probably should never be used, but it's something.
initComponent: function() {
var store = this.lookupViewModel().getStore(this.config.bind.store.replace(/[{}]/g, ''));
store.on('load', this.onLoad, this);
this.callParent();
},
Just to add one more option to the mix, you can listen to the reconfigure event on the grid, as that's what fires after binding a store is complete... so something like this would work:
Ext.application({
name: 'Fiddle',
launch: function() {
Ext.define('MyGrid', {
extend: 'Ext.grid.Panel',
xtype: 'myGrid',
initComponent: function() {
alert('initComponent')
this.on('reconfigure', this.onReconfigureGrid, this);
this.callParent();
},
onReconfigureGrid: function(grid, store, columns, oldStore, oldColumns, eOpts) {
store.on('load', this.onLoad, this);
},
onLoad: function() {
alert('grid store loaded');
}
});
Ext.define('MyViewController', {
extend: 'Ext.app.ViewController',
alias: 'controller.myview',
onLoadBoundStore: function() {
alert('loaded bound');
}
})
Ext.define('MyViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.myview',
stores: {
myStore: {
fields: ['name', 'value'],
autoLoad: true,
proxy: {
type: 'ajax',
url: 'data1.json',
reader: {
type: 'json'
}
},
listeners: {
load: 'onLoadBoundStore'
}
}
}
});
Ext.define('MyView', {
extend: 'Ext.form.Panel',
renderTo: Ext.getBody(),
controller: 'myview',
viewModel: {
type: 'myview'
},
items: [{
title: 'blah',
xtype: 'myGrid',
reference: 'myComboBox',
bind: {
store: '{myStore}'
},
columns: [{
text: 'Name',
dataIndex: 'name'
}, {
text: 'Value',
dataIndex: 'value'
}]
}]
});
Ext.create('MyView');
}
});

Get store in a ViewController

Is it possible to retrieve a store from within a ViewController?
I'm trying to dynamicaly populate a toolbar with buttons from a store, but I'm not sure how to access the store from the controller.
Any hints appreciated.
My Extjs version is 5.1.
View:
Ext.define('EurocampingsCMS.view.Foo', {
extend: 'Ext.toolbar.Toolbar',
alias: 'widget.foo',
requires: [
'EurocampingsCMS.controller.Foo'
],
controller: 'foo',
});
ViewController:
Ext.define('MyApp.controller.Foo', {
extend: 'Ext.app.ViewController',
alias: 'controller.foo',
control: {
'#': {
beforerender: 'onBeforeRender'
}
},
onBeforeRender: function (toolbar, eOpts) {
//How to get store here??
store.each(function (record) {
toolbar.add({
xtype: 'button',
itemId: record.get('localeId'),
text: record.get('label')
});
});
}
});
Best would be to fully utilize MVVM architecture and define the store in viewmodel stores object. Then it is easy:
onBeforeRender: function (toolbar, eOpts) {
var store = this.getViewModel().getStore('yourstorekey');
}
Note: This is possible only if the lifetime of the store is same as the lifetime of the view. If you need to access a global store with a longer lifetime then assign it a storeId and get it with:
var store = Ext.getStore('theStoreId');
Ext.getStore('storeIdHere') is one way to do it

Error adding MixedCollection component to extjs form

I am trying to dynamically build a extjs form and when I try to add the dynamically built MixedCollection object to the form I get a TypeError: e.mixins.elementCt is undefined error.
<div id="form-#pageSpecificVar" class="grid-container even"></div>
<script>
Ext.define('HeaderForm', {
extend: 'Ext.form.Panel',
initComponent: function () {
var me = this;
Ext.applyIf(me, {
id: Ext.id(),
defaultType: 'textfield',
items: [{
xtype: 'container',
items: [{
xtype: 'textfield',
fieldLabel: 'Test'
}]
}]
});
me.callParent(arguments);
}
});
// Define our data model
Ext.define('HeaderModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'FieldA', type: 'int' }
]
});
var store = Ext.create('Ext.data.Store', {
model: 'HeaderModel',
proxy: {
type: 'ajax',
actionMethods: { create: 'POST', read: 'GET', update: 'POST', destroy: 'POST' },
url: '#Url.Content("~/Test/Header")',
timeout: 1200000,
listeners: {
load: function () {
}
}
}
});
store.load({
scope: this,
callback: function (records, operation, success) {
var form = new HeaderForm();
var formItems = new Ext.util.MixedCollection();
Ext.each(records[0].fields.items, function (item) {
console.log(item);
formItems.add(new Ext.form.DisplayField({
fieldLabel: 'Test'
}));
}, this);
console.log(formItems);
form.add(formItems);
form.loadRecord(records[0].data);
form.render('form-#pageSpecificVar');
}
});
</script>
Another thing I don't understand is, when I put the function inside the load listener, nothing happens. So I had to resort to using the callback event.
Update:
form.add method takes a component or component array, so instead of adding MixedCollection type I refer to formItems.items to add the array of displayfields components.
But for some reason the store listeners load is not getting triggered when store.load is executed, does anyone see a problem with that?
Nevermind this... I figured out... I placed the listener instead of the proxy instead of the store.
Q2
Also something weird is that during the callback method of store.load, the records is not return with the loaded values.
Nevermind this... I figured out... It was the json object I'm passing. Forgot to take it out of the error/data structure for form.
Thanks
MixedCollection isn't an accepted parameter for add, you need to use an array. This info is in the docs.

Create an extension with an xtype in ExtJS 4

I am used to ExtJS 3.X, but am struggling with ExtJS 4.
I want to create an extension of a grid and be able to use an instance of the grid with the xtype. As far as im aware, I have to set the alias as widget.xtypename but its not working for me.
var MyGrid = Ext.define('mygrid', {
extend:'Ext.grid.Panel',
alias: 'widget.mygrid',
// rest of grid...
});
Ext.create('Ext.window.Window', {
title:'My Window',
items:[{
xtype:'mygrid'
}]
})
The Error I am getting in Chrome console is Cannot create an instance of unrecognized alias: widget.mygrid
Some help would be much appretiated
Ext.define('MyApp.Grid',{
extend: 'Ext.grid.GridPanel',
alias: 'widget.mygrid',
.......
.......
}
Now you can use as xtype:'mygrid'
The problem may be that you are attempting to instantiate an object that uses your new class, immediately following the call to Ext.define. Remember that Ext.define is an asynchronous process. Anything that needs to instantiate components should be in an onReady handler, or in Ext.application (launch), or in initComponent in a component class, or in init in a controller class, for these locations are guaranteed to be called only after all the defines have completed.
Specifying an alias beginning with "widget." will allow you to use it wherever xtype is expected. In your simple example, you might try doing the following:
var MyGrid = Ext.define('mygrid', {
extend:'Ext.grid.Panel',
alias: 'widget.mygrid',
// rest of grid...
}, function() {
Ext.create('Ext.window.Window', {
title:'My Window',
items:[{
xtype:'mygrid'
}]
});
});
This will instantiate your window within the callback after the define completes.
If you are using working on a MVC application, you can fix this by adding the view information to your controller. In your controller you need to specify the view in an array named views.. Here is an example:
Ext.define('MyApp.controller.Users', {
extend: 'Ext.app.Controller',
views: ['users.List'],
...
In your case you may need to define views:['mygrid'].
If you are not using MVC architecture, you will need to use the Ext.require and specify your grid class exists.
I believe you need to add a xtype to your config:
var MyGrid = Ext.define('mygrid', {
extend:'Ext.grid.Panel',
alias: 'widget.mygrid',
xtype: 'mygrid',
// rest of grid...
});
After researching more, I would expect the alias to be all you need. Are you defining an initComponent function? Below is an example from Sencha:
Ext.define('App.BookGrid', {
extend: 'Ext.grid.Panel',
// This will associate an string representation of a class
// (called an xtype) with the Component Manager
// It allows you to support lazy instantiation of your components
alias: 'widget.bookgrid',
// override
initComponent : function() {
// Pass in a column model definition
// Note that the DetailPageURL was defined in the record definition but is not used
// here. That is okay.
this.columns = [
{text: "Author", width: 120, dataIndex: 'Author', sortable: true},
{text: "Title", flex: 1, dataIndex: 'Title', sortable: true},
{text: "Manufacturer", width: 115, dataIndex: 'Manufacturer', sortable: true},
{text: "Product Group", width: 100, dataIndex: 'ProductGroup', sortable: true}
];
// Note the use of a storeId, this will register thisStore
// with the StoreManager and allow us to retrieve it very easily.
this.store = new App.BookStore({
storeId: 'gridBookStore',
url: 'sheldon.xml'
});
// finally call the superclasses implementation
App.BookGrid.superclass.initComponent.call(this);
}
});
This one also works:
Ext.define('Path.to.ClassUsingSubcomponent', {
...
requires: ['Path.to.YourSubcomponent'],
...
}

Categories

Resources