I'm trying to port existing application from ExtJs 4.2.1 to 6.0.1
The problem that in debugger I see that grid has 'ext-empty-store' store instead of 'store.accounting.Quota'
I can load the store directly in panel activation listener by doing
var store = Ext.data.StoreManager.lookup('QuotaKPI.store.accounting.Quota');
store.load();
In firebug I see request and perfect json in response but nothing appears in the grid
Here are code snippets
app/store/accounting/Quota.js
Ext.define('QuotaKPI.store.accounting.Quota', {
extend: 'Ext.data.JsonStore',
model: 'QuotaKPI.model.accounting.QuotaModel',
alias: 'store.accounting.Quota',
storeId: 'QuotaKPI.store.accounting.Quota',
autoLoad: false,
proxy: {
...
}
});
app/view/accounting/QuotaGrid.js
Ext.define('QuotaKPI.view.accounting.QuotaGrid', {
extend: 'Ext.grid.Panel'
,xtype: 'QuotaGrid'
,store: Ext.data.StoreManager.lookup('QuotaKPI.store.accounting.Quota')
,columns: [
...
]
,dockedItems : [
,{xtype: 'pagingtoolbar',
dock:'bottom',
store: Ext.data.StoreManager.lookup('QuotaKPI.store.accounting.Quota'),
displayInfo: true,
displayMsg: 'Displaying Quota Details {0} - {1} of {2}',
emptyMsg: "No Quota to display"
}
]
,initComponent: function() {
this.callParent(arguments);
}
});
Store, model and grid declared in controller
Ext.define('QuotaKPI.controller.accounting.AccountingController', {
extend: 'Ext.app.Controller',
stores: ['accounting.Quota'],
models: ['accounting.QuotaModel'],
views: ['accounting.QuotaGrid']
...
And controller itself listed in app.js
Ext.application({
name: 'QuotaKPI',
controllers: [
'accounting.AccountingController'
],
init: function(app){
},
autoCreateViewport: true
});
Any help, please?
I know storeId doesn't accept some character (for example "-"), I don't know for dot... in any case I suggest to make it simple.
Try "myStoreId"
In addition you can try:
Ext.define('QuotaKPI.view.accounting.QuotaGrid', {
extend: 'Ext.grid.Panel'
,xtype: 'QuotaGrid'
,store: "myStoreId",
,columns: [
...
]
,dockedItems : [
,{xtype: 'pagingtoolbar',
dock:'bottom',
store: "myStoreId",
displayInfo: true,
displayMsg: 'Displaying Quota Details {0} - {1} of {2}',
emptyMsg: "No Quota to display"
}
]
,initComponent: function() {
this.callParent(arguments);
}
});
In addition I suggest to ensure you have a proper schema configuration (see http://docs.sencha.com/extjs/6.0/6.0.1-classic/#!/api/Ext.data.schema.Schema)
And then, you could try also with ViewModel instead of storeId (see http://docs.sencha.com/extjs/5.0/application_architecture/view_models_data_binding.html)
Don't hesitate to do a https://fiddle.sencha.com/#home
Good Luck!
Transition is not easy...
Related
OK, I have a great confusion about data binding in SAPUI5.
After reading this and this info I still don't know why my code doesn't work.
I define the data and the model in onInit function of the controller:
var oControlsData = {
controls: [
{control: [
{title: "Input Filed"},
{visible: true},
{iconUrl: "sap-icon://edit"}
]},
{control :[
{title: "Combo Box"},
{visible: true},
{iconUrl: "sap-icon://edit"}
]},
{control :[
{title: "Radio Button"},
{visible: true},
{iconUrl: "sap-icon://bo-strategy-management"}
]}
]
};
var oControlsModel = new sap.ui.model.json.JSONModel(oControlsData);
sap.ui.getCore().setModel(oControlsModel, "controls");
Then, in onAfterRendering function I try to bind data from the model to the sap.m.List control:
var oList = sap.ui.getCore().byId("controlsList");
var oListTemplate = new sap.m.StandardListItem({
title: "{title}",
visible: "{visible}",
icon: "{iconUrl}"
});
oList.bindItems({
path: "controls>/controls/control",
template: oListTemplate
});
It seems to me the path is correct, but I still get "No Data" in the control.
What went wrong here?
If I miss something big in understanding data binding process, please give a brief explanation, because every time I have to play with data binding I wonder if it work or not.
The path in bindItems should be the path to the list of Items.
So in your case controls>/controls.
The paths of the properties of the ListItem should then be relative to each object in the list.
sap.ui.getCore().byId("controlsList").bindItems({
path: "controls>/controls",
template: new sap.m.StandardListItem({
title: "{controls>control/0/title}",
visible: "{controls>control/0/visible}",
icon: "{controls>control/0/iconUrl}"
})
});
You need the control/0/ because of your inner array. If you have control over the structure of your data, I would recommend to remove that.
var oControlsData = {
controls: [
{
title: "Input Filed",
visible: true,
iconUrl: "sap-icon://edit"
},
...
]
};
On a sidenote, why are you doing databinding in onAfterRendering?
This should be done in the View. For example in XML:
<List items="{controls>/controls}">
<StandardListItem title="{controls>control/0/title}" visible="{controls>control/0/visible}" icon="{controls>control/0/iconUrl}" />
</List>
i am develop an app with several windows. Many windows are similar, so i think write a superclass, and extend it.
I have, the superclass:
Ext.define('AM.view.ui.DecoratorAbstract',{
extend: 'Ext.window.Window',
alias: 'widget.decoratorAbstract',
initComponent: function(){
this.title = this.aTitle;
this.resizable = this.cfg[0];
this.closable = this.cfg[1];
this.minimizable = this.cfg[2];
//this.items = this.anItem;
this.callParent(arguments);
}
});
And the subclass:
Ext.define('AM.view.ui.DecoratorForm',{
extend: 'AM.view.ui.DecoratorAbstract',
alias: 'widget.decoratorForm',
initComponent: function(){
this.callParent();
this.buttons = [
{ text:'Guardar', action:'save', iconCls: 'ico-save' },
{ text:'Cancelar', action:'cancel', iconCls: 'ico-cancel' }
];
}
});
Both classes are included in the Controller:
Ext.define('AM.controller.Ui',{
extend: 'Ext.app.Controller',
views: [
'ui.Toolbar',
'ui.Statusbar',
'ui.AlertErr',
'ui.AlertOk',
'ui.AlertWar',
'ui.AlertDelete',
'ui.AlertUndelete',
'ui.DecoratorAbstract',
'ui.DecoratorForm',
'ui.DecoratorGrid'
],
model: [],
store: [],
});
From the Firebug js console i create the subclass:
Ext.create('AM.view.ui.DecoratorForm',{cfg:[true,true,true],aTitle: 'Title'}).show();
The window is showed but, not the buttons.
Any ideas ?.
There are a few things here... First, move this.callParent() to the end of your initComponent. This is because the initComponent further up the inheritance does something with this.buttons, and you are missing out on that by calling callParent before setting the buttons.
Next, you really shouldn't use this cfg thing that you are passing in. Just pass in the config parameters that you want to use, and they will be available:
Ext.define('AM.view.ui.DecoratorAbstract',{
extend: 'Ext.window.Window',
alias: 'widget.decoratorAbstract',
initComponent: function(){
this.callParent(arguments);
}
});
Ext.define('AM.view.ui.DecoratorForm',{
extend: 'AM.view.ui.DecoratorAbstract',
alias: 'widget.decoratorForm',
initComponent: function(){
this.buttons = [
{ text:'Guardar', action:'save', iconCls: 'ico-save' },
{ text:'Cancelar', action:'cancel', iconCls: 'ico-cancel' }
];
this.callParent();
}
});
//to instantiate:
Ext.create('AM.view.ui.DecoratorForm',{
resizable: true,
closable: true,
minimizable: true,
title: 'Title'
}).show();
Anytime you are trying to "trick" the component by using something like that cfg array, you should probably re-think what you're doing and see if there's a smarter way.
One other thing you should look into using is Ext.apply(). It will save a lot of bytes by changing what you had before into something like this:
Ext.apply(this, {
title: 'my title',
resizable: cfg[0],
closable: cfg[1],
///...etc...
})
What if you move
this.callParent(arguments);
To the end of initComponent in your DecoratorForm.
I'm a little bit stuck here. My model code is
Ext.define('MyFancyModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'id', type: 'string' },
{ name: 'name', type: 'string' }
],
proxy: {
type: 'rest',
url: '/fancymodel',
noCache: false
}
});
When I try to load data by id using
Ext.ModelManager.getModel('MyFancyModel').load('some-id', {});
the request url is /fancymodel/some-id?id=some-id which is obviously not correct. So how can I achieve the right request url: /fancymodel/some-id without any patches or overrides?
EDIT:
jsfiddle
In the developer console you can see failed GET request
http://fiddle.jshell.net/fancymodel/some-id?id=some-id
EDIT:
Thread on the Sencha forum
I haven't found any ExtJS solution, so I have written a small patch (not sure it works in every situations) :
Ext.override(Ext.data.proxy.Rest, {
buildUrl: function (request) {
delete request.params.id;
return this.callParent(arguments);
}
});
The standard way :
Ext.define('MyPatches.data.proxy.Rest', {
override: 'Ext.data.proxy.Rest',
buildUrl: function (request) {
delete request.params.id;
return this.callParent(arguments);
}
});
I am very confused by the Sencha documentation for ExtJS. The documentation begins with a Getting Started guide which highlights and illustrates the importance on a suitable structure for the classes and source code of your application. But the provided examples then break all the conventions laid down by the Getting Started guide. Instead of code being broken down into appropriate Model, Store, View, etc. class files the examples are provided as a single file with example source code which is not easily re-usable in separate source files.
I started by following the Portal example (http://docs.sencha.com/ext-js/4-1/#!/example/portal/portal.html) as this is the sort of application I want to create. I wanted to enhance the Portal example and add in a screen which would display a grid and use a RESTful web service as the data backend. I have created the backend I just want to create the front-end. So I looked at the RESTful example (http://docs.sencha.com/ext-js/4-1/#!/example/restful/restful.html)
I have tried to copy the RESTful example into the recommended pattern of seperate classes e.g. Model, Store, View:
Model:
Ext.define('MyLodge.model.Member', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'},
{name: 'email', type: 'string'},
{name: 'href', type: 'string'}
]
});
Store:
Ext.require('MyLodge.model.Member');
Ext.define('MyLodge.store.Members', {
autoLoad: true,
autoSync: true,
model: 'MyLodge.model.Member',
proxy: {
type: 'rest',
url: 'http://localhost:8888/rest/memberapi/members' ,
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json'
}
},
listeners: {
write: function(store, operation){
var record = operation.getRecords()[0],
name = Ext.String.capitalize(operation.action),
verb;
if (name == 'Destroy' ) {
record = operation.records[0];
verb = 'Destroyed';
} else {
verb = name + 'd';
}
Ext.example.msg(name, Ext.String.format( "{0} member: {1}", verb, record.getId()));
}
}
});
View:
Ext.define('MyLodge.view.content.MemberGrid', {
extend: 'Ext.grid.Panel',
alias: 'widget.membergrid',
initComponent: function(){
var store = Ext.create('MyLodge.store.Members' );
Ext.apply( this, {
height: this.height,
store: store,
stripeRows: true,
columnLines: true,
columns: [{
id : 'name',
text : 'Name',
flex: 1,
sortable : true,
dataIndex: 'name'
},{
text : 'E-Mail',
width : 150,
sortable : true,
dataIndex: 'email'
},{
text : 'Href',
width : 200,
sortable : true,
dataIndex: 'href'
}],
dockedItems: [{
xtype: 'toolbar',
items: [{
text: 'Add',
iconCls: 'icon-add',
handler: function(){
// empty record
store.insert(0, new MyLodge.model.Member());
rowEditing.startEdit(0, 0);
}
}, '-', {
itemId: 'delete',
text: 'Delete',
iconCls: 'icon-delete',
disabled: true,
handler: function(){
var selection = grid.getView().getSelectionModel().getSelection()[0];
if (selection) {
store.remove(selection);
}
}
}]
}]
});
this.callParent(arguments);
}
});
But I am not sure where to put the code to control the grid row selection and enable the Delete button:
grid.getSelectionModel().on('selectionchange', function(selModel, selections){
grid.down('#delete').setDisabled(selections.length === 0);
});
Also when I press the Add button I get the following error:
Uncaught TypeError: Object [object Object] has no method 'insert'.
Any help would be appreciated.
You are having scoping issues. Basically the variable store is defined only in the initComponent function and therefore of local function scope.
Your handler function has it's own scope. It is firing in the scope of the toolbar button. So if you say this in the handler it would refer to the button. Hence you can say this.up('panel').store - and that gives you the correct reference to the store backing your grid panel.
Another advice is not to implement everything at once. Write a little bit to see if it works and then add to it little by little.
RE: the docs examples, I agree that it's frustrating, but there's not many options. Having a fully-MVC-style implementation of each example would not only be onerous to produce, but would also probably make point of the example get lost in the structure.
RE: your question about the where to "put" the code to control the grid, I would recommend setting up a controller with listeners for the events on the grid in the control() section. This will let you decouple the handling of the events that are fired by your grid from the view itself.
Am using Extjs 4, and have created a custom Rest Proxy to handle communication with my Zend backend api.
(See post http://techfrere.blogspot.com/2011/08/linking-extjs4-to-zend-using-rest.html)
When using a Store to handle communication, I was using Ext.require to load the proxy, and then referenced the proxy on the type field and all was good and it loaded my data: as per:
Ext.require('App.utils.ZendRest');
...
proxy : {
type : 'zest', // My custom proxy alias
url : '/admin/user'
...
}
I then decided to try to use the proxy directly on a model... and no luck. The above logic does not work.
Problems
1. When referencing zest, it does not find the previously loaded ZendRest class (aliased to proxy.zest)
2. It tries to load the missing class from App.proxy.zest (which did not exist.)
So I tried moving my class to this location and renaming to what it seemed to want. No luck.
It loads the class, but still does not initialize the app... I get no errors anywhere so v difficult to figure out where the problem is after this...
For now it seems I will have to revert to using my Zend Rest proxy always via the Store.
Question is... has anyone else seen the behavior? Is it a bug, or am I missing something?
Thanks...
Using your proxy definition, I've managed to make it work.
I am not sure why it doesn't work for you. I have only moved ZendRest to Prj.proxy namespace and added requires: ['Prj.proxy.ZendRest'] to the model.
Code:
// controller/Primary.js
Ext.define('Prj.controller.Primary', {
extend: 'Ext.app.Controller',
stores: ['Articles'],
models: ['Article'],
views: ['article.Grid']
});
// model/Article.js
Ext.define('Prj.model.Article', {
extend: 'Ext.data.Model',
fields: [
'title', 'author', {
name: 'pubDate',
type: 'date'
}, 'link', 'description', 'content'
],
requires: ['Prj.proxy.ZendRest'],
proxy: {
type: 'zest',
url: 'feed-proxy.php'
}
});
// store/Articles.js
Ext.define('Prj.store.Articles', {
extend: 'Ext.data.Store',
autoLoad: true,
model: 'Prj.model.Article'
});
// proxy/ZendRest.js
Ext.define('Prj.proxy.ZendRest', {
extend: 'Ext.data.proxy.Ajax',
alias : 'proxy.zest',
appendId: true,
batchActions: false,
buildUrl: function(request) {
var me = this,
operation = request.operation,
records = operation.records || [],
record = records[0],
format = me.format,
reqParams = request.params,
url = me.getUrl(request),
id = record ? record.getId() : operation.id;
if (me.appendId && id) {
if (!url.match(/\/$/)) {
url += '/';
}
url += 'id/' + id;
}
if (format) {
reqParams['format'] = format;
}
/* <for example purpose> */
//request.url = url;
/* </for example purpose> */
return me.callParent(arguments);
}
}, function() {
Ext.apply(this.prototype, {
actionMethods: {
create : 'POST',
read : 'GET',
update : 'PUT',
destroy: 'DELETE'
},
/* <for example purpose> */
reader: {
type: 'xml',
record: 'item'
}
/* </for example purpose> */
});
});
Here is working sample, and here zipped code.