ExtJS displaying field name inside chart - extjs

Consider the following example of a stacked bar chart. I can display the count of each of the fields (comedy, action, drama, thriller) in the bars by adding the label config in the series, but how would I also display the field name?. The renderer config property for label doesn't seem to be of much use as the function only receives the count as argument.
label:{
display:'insideStart' ,
field:[null, null, 'drama', 'thriller'],
renderer:function(item){
//For this case, item will be an int value. Thus, not sure how useful it is
//as we cannot get the field name from it.
}
}

Actually, the renderer function is passed a lot more arguments than just the value. These arguments are the same as the onPlaceLabel method, with the value added to the beginning, and they are better documented there.
We've got the index of the field in the series and, as a matter of fact, we have the series also available in the item argument. With that, we can achieve what you want:
label: {
display:'insideStart'
,field:[null, null, 'drama', 'thriller']
,renderer: function(value, label, storeItem, item, i, display, animate, index) {
var series = item.series,
titles = series.title;
return titles && titles[index] || series.yField[index];
}
}
I'm trying to get the title first because, in real life, I wouldn't display a raw field name to the user. For the record, here's how the whole serie would be configured in order to do that. It doesn't appear in the doc, except for a user comment...
series: [{
type: 'bar',
axis: 'bottom',
gutter: 80,
xField: 'year',
yField: ['comedy', 'action', 'drama', 'thriller'],
title: ['Comédie', 'Action', 'Drame', 'Thriller'],
stacked: true,
label: {
display:'insideStart'
,field:[null, null, 'drama', 'thriller']
,renderer: function(value, label, storeItem, item, i, display, animate, index) {
var series = item.series,
titles = series.title;
return titles && titles[index] || item.yField;
}
}
}]

Related

How do you setup Material UI DataGrid Initial State Filter Model for Date?

I want to render a Matial UI datagrid with an initial filter model so only upcoming dates are shown. (until the user clears the default set filter)
I have no problems creating a filter for the date after the datagrid is loaded without any initialState set.
manually setting filters after rendered datagrid
However! When I attempted the initial filter model involving the date column
initialState = {{
filter: {
filterModel:{
items: [{
columnField: 'class_end_date',
operatorValue: 'onOrAfter',
value: new Date()
}],
}
}
}}
I continually get Uncaught TypeError: filterItem.value.match is not a function
top of the stacktrace
buildApplyFilterFn gridDateOperators.js:10
getApplyFilterFn gridDateOperators.js:62
Errors resulting from default filter model
I am assuming there's some kind of type conversion I need for the value property in the items filter object. Still researching, but I can't find anything helpful in MUI docs hence far.
If anyone has tackled this one I'd be grateful for a step in the right direction! Thank you.
I also ensured that my type and valueGetter were defined appropriately in my columns array. Remember I want an initial state filter model for class_end_date
{
field: 'class_start_date',
headerName: 'Start Date',
width: 140,
type: 'date',
valueGetter: (params) => {
return new Date(params.value)
}
},
{
field: 'class_end_date',
headerName: 'End Date',
width: 140,
type: 'date',
valueGetter: (params) => {
return new Date(params.value)
}
},
I've also tried using dateTime for the column type, formatting the dates from the valueGetters and the filtermodel value to mm/dd/yyyy format,
and I tried ensuring the value property was a string
filterModel:{
items: [{
columnField: 'class_end_date',
operatorValue: 'onOrAfter',
value: `${new Date()}`
}],
}

How do you create dynamic local store based on record property

I have an integer field on backend. This field is an offset (CPI Index delay) that could takes values from 0 -> N. On frontend I want to display a combo box with this value / label:
{key: 0, label: "0 - 21/06/2020"}
{key: 1, label: "1 - 21/06/2021"}
{key: 2, label: "2 - 21/06/2022"}
{key: 3, label: "3 - ... "}
The number of option is calculated this way: rent_end_date.year - first_due_date.year. Each time the first_due_date change, the options of the combo box must be calculated again.
To achieve this I've initialed a variabile indexOffsetChoices with relative formula to calculate choices each time a first_due_date changes:
viewModel: {
type: 'rentsdetailformmodel',
data: {
hideRentsDetailGrids: false,
indexOffsetChoices: []
},
formulas: {
calculateIndexOffsetChoices: {
bind: '{detailRecord.first_due_date}',
get: function (firstDueDate) {
var detailRecord = this.get('detailRecord'),
indexOffset = detailRecord.get('index_offset'),
rentEndDate = detailRecord.get('end_date');
if (indexOffset !== undefined && rentEndDate !== undefined) {
var choices = [];
for (i = 0; i < (rentEndDate.getFullYear() - firstDueDate.getFullYear()); i++) {
var RawCPIDate = new Date(firstDueDate.setFullYear(firstDueDate.getFullYear() + i)),
CPIDateFmt = Ext.Date.format(new Date(RawCPIDate), 'd/m/Y'),
label = i + ' - ' + CPIDateFmt;
choices.push({"key": i, "label": label});
}
this.indexOffsetChoices = choices;
}
}
}
}
},
Then I added my combo box with an inline local store that point to the viewModel variable indexOffsetChoices:
items: [{
xtype: 'combobox',
name: 'index_offset',
reference: 'index_offset',
fieldLabel: 'CPI Application Delay',
valueField: 'key',
displayField: 'label',
queryMode: 'local',
bind: '{detailRecord.index_offset}',
store: {
type: 'store',
storeId: 'rent__index_offset',
idProperty: 'key',
fields: ['key', 'label'],
data: this.indexOffsetChoices
}
}]
But the data are not loaded. This is the right approach? How do you create local store based on records / dynamic data? What I'm missing?
You did the hardest way sir! The 'data' property in viewModel is actually not used to store large of array data except you will work hard to modify them.
Setting dynamic options in combobox can be achieved by updating (or re-setting) it's options (using yourcombofield.setOptions([/*new array data*/]) like here ) or it's data store (using yourcombofield.getStore().setData([/* new array data*/]) like here) or if you declare your store in viewModel 'stores' property like here and have linked to your combobox's store property, you don't need to access combobox again, just modify the array data of the store and your combobox's options which has been linked to the store will be updated automatically. The important thing, try to give the easiest way to modify you array data inside the store.
Now, let's talk about when and where will you place your code to update this combobox's data? This is about the event's owner which you want to listen (and also affects your case). You said "Each time the first_due_date change". If first_due_date is affected by another field, just put your code inside it's change/keyup/keydown listeners and then re-treat you data by filtering,removing,adding, etc and the last, update the old data with the new data where ever the data has been stored before.

How to disable the widget in a widgetcolumn of a grid based on the attribute of a record in Sencha ExtJS v6?

More or less the configuration looks like this:
{
xtype: 'widgetcolumn',
//text: "", //localize
dataIndex: "carStatusButton",
//disable action for the column
sortable: false,
filter: false,
menuDisabled: true,
widget: {
xtype: 'changeavailbutton',
//reference: "gridCarStatusButton", we cannot use reference because we get a duplicate reference warning
//text is being automatically generated from dataIndex of widgetcolumn
/*
//didn't work!
defaultBindProperty: "curCarStatus", //default is text
curCarStatus: "aaaaaaaa",
setCurCarStatus: function (value) {
this.curCarStatus = value;
},*/
/*
getCurCarStatus: function () {
return "aaaaaa"
},
setCurCarStatus: function (value) {
},*/
/*text: (function() {
return this.enableToggle;
})(),
bind: {
},*/
},
}
We have considered using the updater(cell, value, record, view) but it does not get called initially
We have considered using the renderer(value, metadata, record) but we can only affect the value, it does not give us any help with the widget
We considered to use a custom defaultBindProperty in the widget like this:
defaultBindProperty: "curCarStatus", //default is text
curCarStatus: "",
setCurCarStatus: function (value) {
this.curCarStatus = value;
}
The above helped to avoid creating an extra field in the model that would be necessary. In other words initially we had a field in the model, as a transient field to get the calculated value inside the dataIndex of the widgetcolumn but didn't bring any help on what we were trying to achieve.
The fact is that (from documentation) widgetcolumn binds the dataIndex to the defaultBindProperty of the widget. One problem is that there is a bind that happens in the background that we are not aware of its key value. It would look like that if it was a configuration:
bind: {
text: "{unknownProperty}"
}
If we knew how the property was called it could be helpful to use it in various properties because in our situation we need to bind more than one properties to the widget.
We are actually looking similar functionality provided by isDisabled provides to an ActionColumn to have it in a WidgetColumn.
In the widgetcolumn itself:
onWidgetAttach: function (column, widget) {
if (widget.getWidgetRecord().get('property')) {
widget.setDisabled(false)
} else {
widget.setDisabled(true)
}
}
And the grid can be updated with grid.getView().refresh() to reflect any changes

ui-grid dropdown editor with complex json object

I am using the angular-ui-grid to make an editable grid and have looked over the tutorial here:
http://ui-grid.info/docs/#/tutorial/201_editable
Unlike the example where the options are simple structures like gender male/female, I am trying to bind a complex JSON object in my model. All of the examples I have found are binding to a simple String or Integer object.
Here is an example on Plunker (this code was first taken from the tutorial and then slightly modified to show the problem I am facing):
The core of it is in the columnDef config for the data grid.
{
name: 'product',
field: 'product.name',
enableCellEdit: true,
editType: 'dropdown',
editDropdownOptionsArray: $scope.products,
editableCellTemplate: 'ui-grid/dropdownEditor',
editDropdownIdLabel: 'name',
editDropdownValueLabel: 'name'
}
I have tried changing the values for field and editDropdownIdLabel to a variety of options to no avail. If you look at the 3 console.log lines 51-53 in the sample code you will see that as you change the product selected the name changes but the ID stays the same. I want to be able to pass the whole product object to the backend when ultimately saving changes made in this grid.
I had a quite similar issue and I found a solution which worked for me:
https://github.com/angular-ui/ng-grid/issues/2808
mltroutt's comment on Feb 20 - a generic filter.
based on that, in your pluncker I tried that in the following way:
your product column should be look like this:
name: 'product',
field: 'product.id',
cellFilter: "griddropdown:editDropdownOptionsArray:editDropdownIdLabel:editDropdownValueLabel:row.entity.product.name",
editableCellTemplate: 'ui-grid/dropdownEditor',
editDropdownIdLabel: 'id',
editDropdownValueLabel: 'name',
editDropdownOptionsArray: $scope.products
then after your controller, you have to insert a filter like this:
.filter('griddropdown', function () {
return function (input, map, idField, valueField, initial) {
if (typeof map !== "undefined") {
for (var i = 0; i < map.length; i++) {
if (map[i][idField] == input) {
return map[i][valueField];
}
}
} else if (initial) {
return initial;
}
return input;
};
})
This can be solved by using the same product objects in your data array and the editDropdownOptionsArray array and getting the whole product structure placed into your data structure when a new one is selected. First, in the columnDef change the field to just product instead of product.name, change the editDropdownIdLabel to ref (explained in the next step), and add a cellTemplate as shown.
field: 'product',
editDropdownIdLabel: 'ref',
cellTemplate: '<div>{{row.entity.product.name}}</div>'
Second, create a self-referencing field ref inside each product so that you can return that instead of just id when a selection is made. Also, replace the product inside your data with a reference to the actual product object.
let prods = {};
angular.forEach($scope.products, (product) => {
prods[product.id] = product; // hash for products by id
product.ref = product; // self reference
});
angular.forEach(data, (person) => {
person.product = prods[person.product.id];
});
Now, when an item is selected everything is kept in sync. Also, you are (arguably) actually using the grid and select tools as intended and not creating filters, watchers, etc., etc. to try to fix things up.
Now, if ui-grid had an option (like maybe the default) where the object in the options array would be returned instead of requiring a field of the object to be returned this would be a lot easier and would not create circular data structures. Maybe editDropdownIdLabel: '' or 'self' or null? Or, maybe somebody knows of a better way to get the reference.
Plunker here: http://plnkr.co/edit/wjtuwgvZYIxWpkenJS7a?p=preview and a simpler version based on the gender field example from #bagavathi - http://plnkr.co/edit/JJduek?p=preview
This solution does assume that your product data really is the same and can be shared but is probably a typical use case.
Try this
$scope.gridOptions = {
enableSorting: false,
enableCellEditOnFocus: false,
data: data,
columnDefs: [
{
name: 'product',
field: 'productName',
enableCellEdit: true,
editType: 'dropdown',
editDropdownOptionsArray: $scope.products,
editableCellTemplate: 'ui-grid/dropdownEditor',
editDropdownIdLabel: 'name',
editDropdownValueLabel: 'name'
}
],
onRegisterApi: function (gridApi) {
//set gridApi on scope
$scope.gridApi = gridApi;
gridApi.edit.on.afterCellEdit($scope, function (rowEntity, colDef, newValue, oldValue) {
if (colDef.name === 'product') {
for (var i = 0; i < $scope.products.length; i++) {
var prod = $scope.products[i];
if (prod.name === newValue) {
rowEntity.product = prod;
break;
}
}
calculateBOption(rowEntity);
}
});
}
}

EXTJS4: Compare two fields in GridPanel with each other

I have a Grid-Panel with a few columns.
Now I want to set a new Class in a Column of a Row, when the values don't match with each other.
But how can i get success to a column in a different row?
Her is my code which I tried, but it says, the ID is undefined:
...{
header: 'CRS',
dataIndex: 'crs',
id:'crs',
flex:1,
renderer: function(value, meta) {
console.log("csv-->"+Ext.getCmp('csv').value);
if(value==Ext.getCmp('csv').value)
{
//change class
}
}
},
{
header: 'CSV',
dataIndex: 'csv',
id:'csv',
flex:1
},...
The code you've posted does not seem to match what you're asking for. According to your code, it appears that you're trying to compare values across columns in the same row, not a different row. So which is it?
Anyway, assuming that your code is indicative of what you want, be sure to look at the docs for the the renderer: http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.grid.column.Column-cfg-renderer
One of the arguments passed to the renderer method is the "record", which will contain all the values for the record which is filling the values for the entire row. If you wanted to compare values across columns, you could do something like:
if( value==record.get( 'csv ') ) {
...do something here...
}
If you really need to compare values across rows, then the "store" also gets passed as one of the arguments to renderer, so you could compare values against specific row values that way.
Alternatively, you could do most of this in Model itself. If you are just comparing columns in the same row, you could create an additional field in your Model that stores the result of the comparison. If you did that, all that your renderer would need to do is switch on the value of that new field, rather than doing the entire comparison AND rendering.
I have it! This works for me:
{
header: 'CSV',
dataIndex: 'csv',
flex:1,
renderer: function(grid, rowIndex, rowdata) {
var csv_data=rowdata.data.csv;
var crs_data=rowdata.data.crs;
if (csv_data != crs_data) {
rowIndex.tdCls = "red-class";
}
return csv_data;
}
},
{
header: 'CSV',
dataIndex: 'csv',
flex:1,
renderer: function(value, meta, record) {
var crs = record.get('crs');
if (value && crs)
{
if (value != crs) {
meta.tdCls = "red-class";
}
}
return value;
}
},
Rewritten for ExtJS 4, you can use the value as you set it in the dataIndex, and because that's a record the recommended extjs 4 way to get the value would be the get method.
Just a heads up there are a ton more properties on renderer, you can find the full list here:
http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.grid.column.Column-cfg-renderer

Resources