I have grid which I want to export:
initializeColumnDefs() {
this.columnDefs = [];
this.columnDefs.push({
headerName: 'time,
field: 'completedTimestamp',
cellRenderer: (params: any) => {
if (params.data.isMomentarily)
return '';
return DatagridComponent.DefaultDatetimeCellRenderer(params);
},
comparator: (valueA: number, valueB: number) => {
return DatagridComponent.DefaultDatetimeCellComparator(valueA, valueB);
}
},
{
headerName: 'people',
field: 'people',
cellRenderer: (params: any) => {
if (!params || !params.value || params.value.length <= 0)
return '';
let titles = '';
params.value.forEach(element => {
if (element.name) {
titles += element.name + ',';
}
});
return titles.substring(0, titles.length - 1);
}
}
);
}
Above there's example of two columns: one with timestamp, one with object.
My export() method which I use to export as csv:
export() {
let header = this.columnDefs.map(columnDef => {
let id = columnDef.field || columnDef.colId || columnDef.value;
let headerName = columnDef.headerName;
return headerName;
});
let a: any;
let params: any = {
fileName: 'export.csv',
columnSeparator: ';',
skipHeader: true,
columnKeys: this.columnDefs.map(c => c.field || c.colId).filter(c => !!c)
};
params.customHeader = header.join(params.columnSeparator) + '\n';
this.grid.api.exportDataAsCsv(params);
}
However, I have trouble finding how format values before exporting, because here I only get header and field and no value?
And when I export my grid to csv instead of datetime I get e.g.
which is timestamp and for my object I get
Instead of having Tom, Bob, Ben
Does anyone know how to format these values before exporting?
In your export() function, you will have to add a parameter processCellCallback.
Something like this:
export() {
let header = this.columnDefs.map(columnDef => {
let id = columnDef.field || columnDef.colId || columnDef.value;
let headerName = columnDef.headerName;
return headerName;
});
let a: any;
let params: any = {
fileName: 'export.csv',
columnSeparator: ';',
skipHeader: true,
columnKeys: this.columnDefs.map(c => c.field || c.colId).filter(c => !!c)
};
params.customHeader = header.join(params.columnSeparator) + '\n';
params.processCellCallback = function(cellParams) {
if(cellParams && cellParams.column.colId === 'yourTimestampfield') {
return this.formatter; //apply your timestamp formatter
} else if(cellParams && cellParams.column.colId === 'yourObjectfield') {
return this.formatter; //apply your object formatter
} else
return cellParams.value // no formatting
}
this.grid.api.exportDataAsCsv(params);
}
Read more in the example and docs here.
Related
I have created line chart using Fushionchart in Angular with API data. I have method to add custom color to the chart but it doesn't work. Here I have provide full code to figure the flow. Function pass two parameters and need to set to the label of chart.FYI: *getColorCode() is located in dashboard.service.ts(in below). Here's the full code.
dashboard.component.html
<div *ngIf="multipleConsump" class="chart-card-body p-3">
<app-consumption-summery-chart></app-consumption-summery-chart>
</div>
dashboard.component.ts
getConsumptionData(): void {
this.typeChart.reset();
if (this.trendLogTabIndex == 0 || this.trendLogTabIndex == 1) {
this.tempIntervalid = 2;
} else if (this.trendLogTabIndex == 2) {
this.tempIntervalid = 3;
} else if (this.trendLogTabIndex == 3) {
this.tempIntervalid = 4;
}
this.ltDashboardService
.getCategoryTrends(
this.buildingId,
this.tempIntervalid,
this.intervalTime.from,
this.intervalTime.to,
)
.subscribe(
(res: Object[]) => {
if (res.length) {
const labels = res[0]["data"].map((x) => x.label);
const values = res
.filter((x: any) => this.selectedTypes.indexOf(x.itemId) > -1)
.map((x: any) => {
return {
seriesname: x.name,
data: x.data.map((d) => {
return {
value: d.value,
};
}),
};
});
this.typeChart.setData(labels, values);
}
},
(err) => console.log("Err")
);
}
dashboard.service.ts
getCategoryTrends(building: number, interval: intervalGroup, from:Date, to:Date): Observable<any> {
return this.dataService.get(this.config.endPoints['building-type-consumption-group'], {
startDate: from,
endDate: to,
groupId: GroupBy.MeterType,
serviceTypeId: ServiceType.ELECTRICAL,
buildingId: building,
interval: interval,
siteId: this.config.siteConfigurations.siteId,
dataMode: DataMode.CategorySum,
})
}
Get color code function (meter type is ChartGroupType.METER_TYPES for above function )
Get color code
private getColorCode(chartGroupType: ChartGroupType, itemId: number) {
let item;
let color;
switch(chartGroupType) {
case ChartGroupType.METER:
return '';
case ChartGroupType.BUILDING_CATEGORY:
item = find(this.initialService.navigationStore.buildingCategories, {buildingCategoryID: itemId});
return this.getDefaultColorCode(item);
case ChartGroupType.METER_TYPES:
item = find(this.initialService.navigationStore.meterTypes, {meterTypeID: itemId});
return this.getDefaultColorCode(item);
case ChartGroupType.HT_LOOPS:
return '';
}
}
private getDefaultColorCode(item) {
let color;
if(item === undefined || item.attributes.length === 0) {
return this.config.config.defaultColorCode;
} else {
color = find(
item.attributes,
{
textId: this.config.config.attributes.COLOR_CODE
});
console.log(item + 'chart colors are: ')
return color===null || color === undefined?this.config.config.defaultColorCode:color.value;
}
}
I have a config object. Using this config object, I populate required elements by appending a string to the key of this object.I need help updating values
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => {
var newMemberObjValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
let updateValue = Object.entries(newMemberObjValue).forEach(([key, value]) => {
[`${key}_${type}`]: value; //I'm stuck here. not sure how to proceed
delete key;
});
return updateValue;
};
updateInitialValue = ('applicant', 'SELF');
updateInitialValue = ('spouse', 'DEPENDANT');
Expected Result:
{
type: 'applicant',
dateOfBirth_applicant: '',
seekingCoverage_applicant: true
relationshipToPrimary: 'SELF'
};
{
type: 'spouse',
dateOfBirth_spouse: '',
seekingCoverage_spouse: true
relationshipToPrimary: 'DEPENDANT'
};
Since you're not updating the original object, you can simplify this greatly:
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => ({
type,
relationshipToPrimary,
[`dateOfBirth_${type}`]: MEMBER_INITIAL_VALUE.dateOfBirth_,
[`seekingCoverage_${type}`]: MEMBER_INITIAL_VALUE.seekingCoverage_
});
let updatedValue = updateInitialValue('applicant', 'SELF');
updatedValue = updateInitialValue('spouse', 'DEPENDANT');
This should do the trick:
const MEMBER_INITIAL_VALUE = {
type: '',
dateOfBirth_: '',
seekingCoverage_: true,
relationshipToPrimary: ''
};
const updateInitialValue = (type, relationshipToPrimary) => {
let newMemberInitialValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
Object.keys(newMemberInitialValue).forEach((key) => {
if(!['type', 'relationshipToPrimary'].includes(key)) {
newMemberInitialValue[`${key}_${type}`] = newMemberInitialValue[key];
delete newMemberInitialValue[key];
}
});
newMemberInitialValue.type = type;
newMemberInitialValue.relationshipToPrimary = relationshipToPrimary;
console.log(newMemberInitialValue);
};
let applicantValues = updateInitialValue('applicant', 'SELF');
let spouseValues = updateInitialValue('spouse', 'DEPENDANT');
EDIT: Missed returning the value from the function and then assigning to a new variable.
Although an answer was posted, because i also solved it and my solution is a bit different (though the other answer looks way too slimmer) i would post it here.
const MEMBER_INITIAL_VALUE = {
type: "",
dateOfBirth_: "",
seekingCoverage_: true,
relationshipToPrimary: "",
};
const updateInitialValue = (type, relationshipToPrimary) => {
var newMemberObjValue = JSON.parse(JSON.stringify(MEMBER_INITIAL_VALUE));
Object.entries(newMemberObjValue).forEach(([key, value]) => {
if (key === "type") {
newMemberObjValue[key] = type;
} else if (key === "dateOfBirth_") {
Object.defineProperty(
newMemberObjValue,
[`${key}_${type}`],
Object.getOwnPropertyDescriptor(newMemberObjValue, key)
);
delete newMemberObjValue[key];
newMemberObjValue[`${key}_${type}`] = value;
} else if (key === "seekingCoverage_") {
Object.defineProperty(
newMemberObjValue,
[`${key}_${type}`],
Object.getOwnPropertyDescriptor(newMemberObjValue, key)
);
delete newMemberObjValue[key];
newMemberObjValue[`${key}_${type}`] = value;
} else if (key === "relationshipToPrimary") {
newMemberObjValue[key] = relationshipToPrimary;
}
});
return newMemberObjValue;
};
const updatedValue1 = updateInitialValue("applicant", "SELF");
const updatedValue2 = updateInitialValue('spouse', 'DEPENDANT');
Though a few answers have already been posted, I would like to suggest a similar one that does the same thing in a much more clear and concise way:
function Member() {
this.type = '';
this.dateOfBirth = '';
this.seekingCoverage = true;
this.relationshipToPrimary = '';
}
function UpdateInitialValue(type, relationshipToPrimary) {
var newMember = new Member();
newMember.type = type;
newMember.relationshipToPrimary = relationshipToPrimary;
return newMember;
}
console.log(UpdateInitialValue('applicant', 'SELF'));
console.log(UpdateInitialValue('spouse', 'DEPENDANT'));
genderPie()
let filter = {};
async function genderPie() {
const d = await getData();
const g = await d.reduce((a, o) => (o.GEN && a.push(o.GEN), a), []);
const gender = Object.keys(g).length;
const m = await d.reduce((a, o) => (o.GEN == 1 && a.push(o.GEN), a), []);
const male = Object.keys(m).length;
const f = await d.reduce((a, o) => (o.GEN == 2 && a.push(o.GEN), a), []);
const female = Object.keys(f).length;
var data = [{
name: 'male',
y: male,
id: 1
}, {
name: 'female',
y: female,
id: 2
}];
chart = new Highcharts.Chart({
plotOptions: {
pie: {
innerSize: '80%',
dataLabels: {
connectorWidth: 0
}
}
},
series: [{
"data": data,
type: 'pie',
animation: false,
point: {
events: {
click: function(event) {
filter.GEN = '' + this.id + '';
}
}
}
}],
"chart": {
"renderTo": "gender"
},
});
}
async function getData() {
buildFilter = (filter) => {
let query = {};
for (let keys in filter) {
if (filter[keys].constructor === Array && filter[keys].length > 0) {
query[keys] = filter[keys];
}
}
return query;
}
//FILTER DATA
//Returns the filtered data
filterData = (dataset, query) => {
const filteredData = dataset.filter((item) => {
for (let key in query) {
if (item[key] === undefined || !query[key].includes(item[key])) {
return false;
}
}
return true;
});
return filteredData;
};
//FETCH JSON
const dataset = [{
"GEN": "2"
}, {
"GEN": "1"
}, {
"GEN": "1"
}, {
"GEN": "2"
},
{
"GEN": "2"
}, {
"GEN": "2"
}, {
"GEN": "2"
}, {
"GEN": "1"
}
]
//BUILD THE FILTER
const query = buildFilter(filter);
const result = filterData(dataset, query);
console.log(result)
return result
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="gender"></div>
does anyone can explain me how to handle the following?
I have two functions that filter data and than I build a chart with Hichart
Each time a user click for example a slice of a pie chart an event is fired and an object is populated.
That object allows me to filter the dataset and redraw the chart
The last thing I'm missing is about to update the filtering functions based on the object to be populated
first I'll do this
async function getData() {
buildFilter = (filter) => {
let query = {};
for (let keys in filter) {
if (filter[keys].constructor === Array && filter[keys].length > 0) {
query[keys] = filter[keys];
}
}
return query;
}
then
filterData = (data, query) => {
const filteredData = data.filter( (item) => {
for (let key in query) {
if (item[key] === undefined || !query[key].includes(item[key])) {
return false;
}
}
return true;
});
return filteredData;
};
const query = buildFilter(filter);
const result = filterData(data, query);
my object is
let filter = {}
when a user click the slice myobject become for example
let filter = {
gen: "1"
}
Take a look at this StackBlitz project.
In getData(), I simplified your filter to this one:
return data.filter(item => {
for (const property of Object.keys(filter)) {
if (item[property] !== filter[property]) {
return false;
}
}
return true;
});
and when a slice is clicked, I call genderPie() again, after updating the filter.
You might want to separate the data request from the filtering, so that the data is downloaded only once, not every time a filter is changed.
I am trying to add a Quick Launch functionality to a this homepage project which would, with a key press, launch all the URLs with quickLaunch property set to true.
I have set the scene by adding a quickLaunch property to the CONFIG object like so:
const CONFIG = {
commands: [{
{
category: 'General',
name: 'Mail',
key: 'm',
url: 'https://gmail.com',
search: '/#search/text={}',
color: 'linear-gradient(135deg, #dd5145, #dd5145)',
icon: 'mail.png',
quickLaunch: true,
},
{
category: 'General',
name: 'Drive',
key: 'd',
url: 'https://drive.google.com',
search: '/drive/search?q={}',
color: 'linear-gradient(135deg, #FFD04B, #1EA362, #4688F3)',
icon: 'drive.png',
quickLaunch: false,
},
{
category: 'Tech',
name: 'GitHub',
key: 'g',
url: 'https://github.com',
search: '/search?q={}',
color: 'linear-gradient(135deg, #2b2b2b, #3b3b3b)',
icon: 'github.png',
quickLaunch: true,
},
...and so on
and then added a condition to launch all the websites with quickLaunch option enabled:
class QueryParser {
constructor(options) {
this._commands = options.commands;
this._searchDelimiter = options.searchDelimiter;
this._pathDelimiter = options.pathDelimiter;
this._protocolRegex = /^[a-zA-Z]+:\/\//i;
this._urlRegex = /^((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)$/i;
this.parse = this.parse.bind(this);
}
parse(query) {
const res = {
query: query,
split: null
};
if (this._urlRegex.test(query)) {
const hasProtocol = this._protocolRegex.test(query);
res.redirect = hasProtocol ? query : 'http://' + query;
} else {
const trimmed = query.trim();
const splitSearch = trimmed.split(this._searchDelimiter);
const splitPath = trimmed.split(this._pathDelimiter);
this._commands.some(({
category,
key,
name,
search,
url,
quickLaunch
}) => {
if (query === key) {
res.key = key;
res.isKey = true;
res.redirect = url;
return true;
}
if (splitSearch[0] === key && search) {
res.key = key;
res.isSearch = true;
res.split = this._searchDelimiter;
res.query = QueryParser._shiftAndTrim(splitSearch, res.split);
res.redirect = QueryParser._prepSearch(url, search, res.query);
return true;
}
if (splitPath[0] === key) {
res.key = key;
res.isPath = true;
res.split = this._pathDelimiter;
res.path = QueryParser._shiftAndTrim(splitPath, res.split);
res.redirect = QueryParser._prepPath(url, res.path);
return true;
}
if (key === '*') {
res.redirect = QueryParser._prepSearch(url, search, query);
}
/* ---> */ if (query === 'q!') {
for (let i = 0; i < this._commands.length; i++) {
if (this._commands[i].quickLaunch === true) {
window.open(this._commands[i].url);
}
}
return true;
}
});
}
res.color = QueryParser._getColorFromUrl(this._commands, res.redirect);
return res;
}
static _getColorFromUrl(commands, url) {
const domain = new URL(url).hostname;
return (
commands
.filter(c => new URL(c.url).hostname.includes(domain))
.map(c => c.color)[0] || null
);
}
static _prepPath(url, path) {
return QueryParser._stripUrlPath(url) + '/' + path;
}
static _prepSearch(url, searchPath, query) {
if (!searchPath) return url;
const baseUrl = QueryParser._stripUrlPath(url);
const urlQuery = encodeURIComponent(query);
searchPath = searchPath.replace('{}', urlQuery);
return baseUrl + searchPath;
}
static _shiftAndTrim(arr, delimiter) {
arr.shift();
return arr.join(delimiter).trim();
}
static _stripUrlPath(url) {
const parser = document.createElement('a');
parser.href = url;
return `${parser.protocol}//${parser.hostname}`;
}
}
I expected the marked condition (commented with "-->") to fire only once but it is doing the whole process four times all over. I logged the URLs it is trying to launch and it looks like this:
Am I missing an obvious core concept?
Seems like this._commands.some( runs through all your this._commands, and then you check if query is query === 'q!' and i guess thats always true, and if thats the case then you loop through this._commands again. giving you this._commands.length * this._commands.length amount of output.
Using rollup, buble, flow-remove-types,
Is it possible to create an ENUM of classes instances for chess board representation, as types, like this:
// a Ref is a class or a type
class Ref { /* ... */ }
// Refs is an ENUM
Refs.forEach((ref: Ref, key: string) => {
console.log(key) // outputs: "a1", ..., "h8" successively
})
// type checking should work
typeof Refs.a1 === Ref // true
// etc...
typeof Refs.h8 === Ref // true
// move(ref) --> ref will work
Refs.a1.move(7, 7) === Refs.h8 // true
Refs.h8.move(-7, -7) === Refs.h8 // true
// with...
Refs.a1.move(0, 0) === Refs.a1 // true
// void reference
Refs.a1.move(-1, -1) === null
// or
Refs.a1.move(-1, -1) === Refs.EMPTY
A possible modular implementation would be packing the Ref class and the Refs collection in the same file, with a initialization code, like Enuify lib does... But how to make the Ref#move method working properly ??
The same as :
TicTacToe.X.us =TicTacToe.X
TicTacToe.X.them =TicTacToe.O
TicTacToe.O.us =TicTacToe.O
TicTacToe.O.them =TicTacToe.X
something like this, is perfectible, but works fine for me...
type TF = 'a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'
type TR = '1'|'2'|'3'|'4'|'5'|'6'|'7'|'7'
type TRefDef = {
file: TF,
fidx: number,
rank: TR,
ridx: number
}
interface IRef {
move (df: number, dr: number) : IRef
}
const FILES: Array <TF> = 'abcdefgh'.split('')
const RANKS: Array <TR> = '12345678'.split('')
const all: {
[key:string] : IRef
} = {}
const compute = function(fidx: number, ridx: number): IRef {
const file: TF = FILES[fidx]
const rank: TR = RANKS[ridx]
return all[file + rank]
}
const select = function(key: string) : IRef {
return all[key]
}
const get = function(arg1: string | number, arg2: ?number) : IRef {
if(arguments.length === 1) {
return select (arg1)
}
if(arguments.length === 2) {
return compute (arg1, arg2)
}
}
const each = function (callback) {
Object.keys(all).forEach((key, idx) => {
callback.call(this, all[key], idx)
})
}
class Ref implements IRef {
constructor (refdef: TRefDef) {
this.file = refdef.file
this.fidx = refdef.fidx
this.rank = refdef.rank
this.ridx = refdef.ridx
this.key = this.file + this.rank
}
toString() : string {
return 'Ref: ' + '(' + this.fidx + ',' + this.ridx + ')' + ' ' + this.file + this.rank
}
move (df: number, dr: number) : Ref {
let f = FILES.indexOf(fidx)
let r = RANKS.indexOf(ridx)
f += df
r += dr
return all[FILES[f] + RANKS[r]]
}
}
FILES.forEach((file, fidx) => {
RANKS.forEach( (rank, ridx) => {
const key: string = file + rank
const ref: Ref = new Ref({ file, fidx, rank, ridx })
all[key] = ref
})
})
Ref.empty = new Ref('', -1, '', -1)
const Refs = { compute, select, each, get }
// let f = { compute, each, selection }
// console.log(f)
// export { compute, each, select, Ref }
export { Refs, Ref }