I have an Ext GroupingStore on a list of orders. One of the columns is a DateTime. I would like the user to be able to group by the DateTime column and have all days under one group. At the moment, each date, hour, minute and second is one group... not very useful :-)
Any way short of two columns to accomplish this?
You can modify a GroupingView method to accomplish this.
First create the GroupingView object:
var gview = new Ext.grid.GroupingView({
forceFit: true,
groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
});
Then modify the (in fact "private") method:
gview.getGroup = function(v, r, groupRenderer, rowIndex, colIndex, ds){
// colIndex of your date column
if (colIndex == 2) {
// group only by date
return v.format('m.d.Y');
}
else {
// default grouping
var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);
if(g === ''){
g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;
}
return g;
}
};
Then apply the View to the grid:
var grid = new Ext.grid.GridPanel({
...
view: gview,
...
});
Ah well, a little proof of concept (Click on sort 'Last Updated')
Demo
I think you can specify the date format when you are defining the record type for the store.
It may solve your problem.
Something like
var reader = new Ext.data.JsonReader(
fields:[{
type: 'date',
dateformat: 'd M Y'
},{
.............
..............
},
.............
.............
]
)
var store = new Ext.data.Store({
reader: reader,
data: []
})
Tarnschaf's answer was correct, I just thought I would post my final, modified version of his code for completness. This was how I ended up doing it. Thanks Tarnschaf!
g.view.getGroup = g.view.getGroup.wrap(function(normal,v, r, groupRenderer, rowIndex, colIndex, ds)
{
var cm = g.getColumnModel();
var id=cm.getColumnId(colIndex);
var col=cm.getColumnById(id);
if(col.renderer == formatDateTime)
{
v = (new Date(v)).format('m/d/yy');
groupRenderer = formatDate;
}
return normal(v, r, groupRenderer, rowIndex, colIndex, ds);
});
where g is the GridView. I found that by using g.view.getGroup.wrap I could keep the previous function intact, and only modify the parameters as Tarnschaf's answer did. Also, this method works even if the columns are reordered -- there's no colIndex hard coded in. Furthermore, it automatically applies itself to every column which has a renderer of formatDateTime (my date/time formating function) and switches those to the formatDate groupRenderer so the group headings look cleaner.
Do you actually need to reference the time part of the date? If not, you could use a convert function on the field to call clearTime() on the date object.
In your grid column definition, define a groupRenderer config:
{
groupRenderer : function(v, u, r, row, col, store) {
return //your custom group value
}
}
Related
I'm quite new in ExtJS and JS both.
I have an Ext.Grid and the column that should display the month and year e.g. "august 2019".
By adding a new column the month should decrement, so the result should be:
1st add: august 2019
2nd add: july 2019
3d add: june 2019
...
I also have a widget for choosing data by only month and a year from here:
EXTJS 5 - Date picker year and month only
Maybe I should provide a bit more code or can you advice what I should learn ?
dataIndex: 'picker',
...
renderer: function (value, cell, record) {
if (!value && record.get('year') && record.get('month')) {
value = new Date(record.get('year'), record.get('month') - 1);
record.set('picker', value);
}
return Ext.Date.format(value, 'M, Y');
Now months are incrementing,instead of decrementing.
The renderer function in ExtJS is merely a function that, for the given column, is called for every row in the grid. The output of this function is used to render the text inside that particular cell of the grid.
If what you want to do is allow to dynamically add columns to the grid, with the new columns having the value of month "decremented by 1" compared to the previous month, what you have to do is to build a renderer that has some specific additional parameter attached to it, which you can do for example by using JavaScript's bind method. Every time you add the column, you'll create an instance of such renderer passing a different value.
The following StackOverflow post will give you a hint on how to make columns dynamic:
How to dynamically add columns for Grid ExtJs
As for the renderer function and its usage, it goes more or less as follows:
function customRenderer(value, cell, record) {
var result = value - this.index;
return value + " - " + this.index + ' = ' + result;
}
var columns = [];
for(var i = 0; i < 10; i++) {
var column = {};
column.dataIndex = 'column' + i;
column.text = 'Column ' + i;
column.renderer = customRenderer.bind({ index: i, column: column });
}
reconfigureExtJSColumnsPseudoFunction(columns);
The above code, which you will of course have to tune based on your needs, takes the customRenderer function and changes its scope, thus allowing you to provide it with additional context. Each iteration in the for loop will create a new "version" of that function running in a different scope (through the bind method) thus ensuring that each column will receive a different value for the index property, allowing you to dynamically change the amount you will then subtract based on the column index itself.
I'm trying to make an export of my model which consists out of employees, some properties and a Date.
Visualisation of my model
var myModel= {
employees: [{
pentagram: oMyData.Pentagram,
records: [{
Date: oMyData.Date,
GC: oMyData.Lunch,
CheckIn: oMyData.CheckedIn,
CheckOut: oMyData.CheckedOut
}]
}]
};
Purpose of my application
My app is used to log which employee is in the building by letting them check in and out at the front door. This is registered to a HanaXS database. Each day when an employee checks in, a record is created with the corresponding properties. So if the decide not to eat at the office, they will click on the GC button (which stands for 'no consumption' in dutch).
So in a nutshell. Each employee has their own record per date in the database.
What do I want to do
I want to make an excel sheet which will cover a month.
The most left column will cover the names of the employee's (Pentagram). After that all the columns will be a day in the corresponding month in chronological order.
The content should be an X when they pressed the GC button. Otherwise the cell should be empty.
My problem
I have no clue how to get the dates as columns while keeping the binding with the employees. I've already searched a lot on exporting of the model and of tables but nothing is actually near to what I need.
If anyone has some experience or done this before I would be really gratefull for some help.
Thanks in advance
Hi you can use the following libraries
'sap/ui/core/util/Export',
'sap/ui/core/util/ExportTypeCSV',
This is the sample code you can refer to suit your needs
generateExcel: function(oData, that) {
var oModel = new JSONModel();
oModel.setData(oData); //oData is the model data which is binding to the table
var oTable = this.getViewById("yourTableName").getTable();
var aColumns = oTable.getColumns();
var aItems = oTable.getItems();
var aTemplate = [];
for (var i = 0; i < aColumns.length; i++) {
var oColumn = {
name: aColumns[i].getHeader().getText(),
template: {
content: {
path: null
}
}
};
if (aItems.length > 0) {
oColumn.template.content.path = aItems[0].getCells()[i].getBinding("text").getPath();
}
aTemplate.push(oColumn);
}
var oExport = new Export({
// Type that will be used to generate the content. Own ExportType’s can be created to support other formats
exportType: new ExportTypeCSV({
separatorChar: ",",
charset: "utf-8"
}),
// Pass in the model created above
models: oModel,
// binding information for the rows aggregation
rows: {
path: "/results"
},
// column definitions with column name and binding info for the content
columns: aTemplate
});
oExport.saveFile().always(function() {
this.destroy();
});
}
Hi you can use custom formatter for columns depending on types like below an example
var oColumn = {
name: aColumns[i].getHeader().getText(),
template: {
content: {
path: null,
formatter: function(value) {
if (value instanceof(Date)) {
//Convert to user date format
var oFormat = DateFormat.getDateInstance({
style: "short"
});
value = oFormat.format(value);
} else {
value = (value === null) ? "" : value;
}
return value;
}
}
}
};
I am building a datatable and wish to compare a date string that is passed in, to todays date, and then do something based on the outcome (before today, same as today etc etc)
"createdRow": function (row, data, dataIndex) {
var formattedDate = moment().format("DD-MMM-YYYY");
var dateIncoming = moment(data.NextDue, 'DD-MMM-YYYY').format("DD-MMM-YYYY");
if (dateIncoming < formattedDate) {
dateDue = true;
}
if (dateDue === true && data.AssignedToName !== "With Caller") {
$(row).css({ "color": "red" });
}
}
So it is mainly working, but I have one record where the dateDue is 15-03-2017 and the formattedDate is also 15-03-2017, but the css on my row is still activating.
I would normally expect this to happen if a datetime was also involved, and I also have several other values where dateDue is 15-03-2017 and these filter correctly.
You can use isBefore and isAfter to compare moment objects istead of <.
Moreover you need to remove format() since it trasforms moment object in string, so, as stated in the comments, in your code you are comparing strings intead of moment/dates.
Your code will be:
"createdRow": function (row, data, dataIndex) {
var formattedDate = moment();
var dateIncoming = moment(data.NextDue, 'DD-MMM-YYYY');
if (dateIncoming.isBefore(formattedDate)) {
dateDue = true;
}
if (dateDue === true && data.AssignedToName !== "With Caller") {
$(row).css({ "color": "red" });
}
}
I am working on a backbone application at the moment, and I wanting to order my collection based on various attributes, I have sorting working based on cost, duration, start and finish date (timestamps) but I am struggling to sort the collection based on the name attribute, here is my collection code,
var ProjectCollection = Backbone.Collection.extend({
url: '/projects',
model: app.Project,
sort_key: "finish_date",
sort_order: 1,
parent_filter: false,
initialize: function() {
var pm = this.get('projectmanager');
this.set("project_manager", new app.User(pm));
var sp = this.get('salesperson');
this.set("sales_person", new app.User(sp));
},
comparator: function (item1, item2) {
return (item1.get(this.sort_key) - item2.get(this.sort_key)) * this.sort_order;
},
sortByField: function(fieldName, orderType) {
this.sort_key = fieldName;
this.sort_order = orderType == "desc" ? -1 : 1;
console.log(this.sort_order);
this.sort();
},
});
Is there are way to sort alphabetically on a string?
I have added the new comparator function, and this is output I get when ordering via name in an ascending fashion,
Android Application
Hello World I am a new instance of a project
Java Application
Simon's Project
Some new project
Symfony App,
Windows Application
iPhone Application
As you can see iPhone application should not be last in the list.
i changed your comprator function to support strings along with numerics
the problem in your comprator function is that it subtracting strings which yields NaN
i changed it to be like
comparator: function (item1, item2) {
var sortValue = (item1.get(this.sort_key) > item2.get(this.sort_key)) ? 1 : -1;
return sortValue * this.sort_order;
},
this will just compare, get a value 1 for a > b and then change it for desc sort like you did
check this jsFiddle
EDIT:
to support case insensitive ordering, comparator function should be modified to handle strings separately
comparator: function (item1, item2) {
var val1 = item1.get(this.sort_key);
var val2 = item2.get(this.sort_key);
if (typeof (val1) === "string") {
val1 = val1.toLowerCase();
val2 = val2.toString().toLowerCase();
}
var sortValue = val1 > val2 ? 1 : -1;
return sortValue * this.sort_order;
}
jsFiddle updated
I am relatively new to the JSON notation, and have run into an issue when attempting to reformat. The current format contained in the database needs to be modified to a new format for importation into a project timeline graph.
Here is the current JSON format:
[
{
"name":"5-HP-N/A-N/A-F8",
"node":{
"name":"5",
"id":14
},
"timeline":{
"epc":null,
"m1":null,
"m2":null,
"m3":1554087600000,
"m4":1593572400000,
"m5":1625108400000,
"m6":1641006000000,
"m7":1656644400000
},
"fab":{
"name":"F8",
"id":1
}
},
However, in order to display in the graph, I need the following format:
{
'start': new Date(value from epc, or first non-null milestone),
'end': new Date(value from m1 or first non-null milestone), // end is optional
'content': 'label from start Date milestone'
'group' : ' value from name field above 5-HP'
'classname' : ' value from start Date milestone'
});
I am trying to write a function in order to accomplish this. Only epc, m1, or m2 may have the value of null, but the condition must be checked for to determine if an event range should be created and where it should end. What would be the best way to reformat this json data (preferrably from an external json sheet)?
Edit: Thanks for all the help I see how this is working now! I believe I didn't explain very well the first time though, I actually need multiple class items per "group".
The end result is that these will display inline on a timeline graph 'group' line, and so I am trying to figure out how to create multiple new objects per array element shown above.
So technically, the first one will have start date = m3, and end date = m4. Then, the next object would have the same group as the first (5-HP...), the start date = m4, end date = m5...etc. This would continue until m7 (always an end date but never a start date) is reached.
This is why the loop is not so simple, as many conditions to check.
see a working fiddle here: http://jsfiddle.net/K37Fa/
your input-data seems to be an array, so i build a loop around that. if not just see this fiddle where the input data is a simple object: http://jsfiddle.net/K37Fa/1/
var i
, result = [],
, current
, propCounter
, content = [ { "name":"5-HP-N/A-N/A-F8", "node":{ "name":"5", "id":14 }, "timeline":{ "epc":null, "m1":null, "m2":null, "m3":1554087600000, "m4":1593572400000, "m5":1625108400000, "m6":1641006000000, "m7":1656644400000 }, "fab":{ "name":"F8", "id":1 } }],
// get the milestone in a function
getMileStone = function(obj) {
propCounter = 1;
for(propCounter = 1; propCounter <= 7; propCounter++) {
// if m1, m2 and so on exists, return that value
if(obj.timeline["m" + propCounter]) {
return {key: "m" + propCounter, value: obj.timeline["m" + propCounter]};
}
}
};
// loop over content array (seems like you have an array of objects)
for(i=0;i< content.length;i++) {
current = content[i];
firstMileStone = getMileStone(current);
result.push({
'start': new Date(current.epc || firstMileStone.value),
'end': new Date(current.m1 || firstMileStone.value),
'content': firstMileStone.key,
'group' : current.name,
'classname' : firstMileStone.value
});
}
EDIT:
getMileStone is just a helper-function, so you could just call it with whatever you want. e.g. current[i+1]:
secondMileStone = getMileStone(current[i + 1])
you should just check, if you are not already at the last element of your array. if so current[i+1] is undefined, and the helperfunction should return undefined.
you could then use as fallback the firstMileStone:
secondMileStone = getMileStone(current[i + 1]) || firstMileStone;
see the updated fiddle (including check in the getMileStone-Helperfunction): http://jsfiddle.net/K37Fa/6/