I use jQuery datatable and Export buttons.
I change the filename and the title.
My problem is that I want the title to be dynamic, based on the filters and custom filters.
My problem is that The file name configured on the first load only
.withButtons([
{
extend: "excelHtml5",
filename :$scope.getFileName(false),
title:$scope.getFileName(true),
exportOptions: {
columns: ':visible'
},
//CharSet: "utf8",
exportData: { decodeEntities: true}
},
Of course $scope.getFileName() is only called once, or the number of times you intentionally call it. Go the other way around : Use the init() function along with a $watch :
.withButtons([{
extend: "excelHtml5",
//filename: $scope.getFileName(false),
//title: $scope.getFileName(true),
exportOptions: {
columns: ':visible'
},
//CharSet: "utf8",
exportData: {
decodeEntities: true
},
init: function(dt, node, config) {
$scope.watch('fileName', function()
//config.filename is in fact config.title + config.extension
config.title = $scope.fileName.title;
config.extension = $scope.fileName.extension;
})
}
}])
You are not explaining why you need to use a $scope.getFileName() despite you are using angular where such things is easily automated. With the above you can now update a fileName struct on $scope whenever it is needed, and the export filename will change accordingly
$scope.fileName = {
extension: '.xls',
title: 'my-table-export'
}
Related
In SAPUI5, you can often bind resource bundle properties to items in several ways. I'm attempting to do it in JavaScript, using data provided by an Odata service, but the methods I've found here so far haven't worked. I've tried two different ways:
How to Use Internalization i18n in a Controller in SAPUI5?
binding issue with sap.m.List and model configured in manifest.json
And neither of these have worked. I feel this is because I'm inside a items list, inside of a dialog that's causing my issue:
My code is:
var resourceBundle = this.getView().getModel("i18n");
if (!this.resizableDialog) {
this.resizableDialog = new Dialog({
title: 'Title',
contentWidth: "550px",
contentHeight: "300px",
resizable: true,
content: new List({
items: {
path: path,
template: new StandardListItem({
title: resourceBundle.getProperty("{Label}"),//"{ path : 'Label', fomatter : '.getI18nValue' }",
description: "{Value}"
})
}
}),
beginButton: new Button({
text: 'Close',
press: function () {
this.resizableDialog.close();
}.bind(this)
})
});
getI18nValue : function(sKey) {
return this.getView().getModel("i18n").getProperty(sKey);
},
Using the 2nd method above, never calls the JavaScript method. I didn't think it would work, as it's more JSON based. The first method, loads the data fine, but instead of showing the resource bundle text, it shows just the text found inside of the {Label} value.
The value found inside of {Label} matches the key found inside of the resouce bundle i.e. without the i18n> in front, like you would have in a view.
Anyone have any suggestions?
Use of formatter will solve your problem, but the way you're doing it is wrong. Try this, it will solve your problem.
var resourceBundle = this.getView().getModel("i18n");
if (!this.resizableDialog) {
this.resizableDialog = new Dialog({
title: 'Title',
contentWidth: "550px",
contentHeight: "300px",
resizable: true,
content: new List({
items: {
path: path,
template: new StandardListItem({
title: {
parts: [{ path: "Label" }],
formatter: this.getI18nValue.bind(this)
},
description: "{Value}"
})
}
}),
beginButton: new Button({
text: 'Close',
press: function () {
this.resizableDialog.close();
}.bind(this)
})
});
}
And the formatter function getI18nValue will be like this,
getI18nValue: function(sKey) {
return this.getView().getModel("i18n").getResourceBundle().getText(sKey);
},
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'
}
});
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');
}
});
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'.
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'],
...
}