How to use custom global functions inside field value expressions in Cx? - javascript

I have a somewhat complex case where I need to apply custom formatting to a JavaScript expression that calculates the value of a field inside a Grid.
<Grid records:bind="$page.data"
columns={[
{
field: 'seatbeltViolations',
header: 'Seatbelt Violations',
format:'n;0',
aggregate: 'sum',
aggregateField: 'seatbelts',
align: 'right'
},{
field: "distance",
header: "Distance",
format: "n;0",
aggregate: "sum",
aggregateField: "distance",
align: "right"
},{
field: 'seatbeltViolationsPer100Km',
header: 'Seatbelts per 100km',
format: 'n;1',
footer: {
expr: '0.1 * Math.round(10.0 * {$group.seatbelts}/{$group.distance})'
},
align: 'right'
}]} />
Is there a way to use custom global functions, that perform the given operation, within the expression? Something like this:
// this does not work
expr: 'Format.value({$group.seatbelts}/{$group.distance}, "n;1")'
I hope my question was clear enough :)

I think the easiest way would be to use computable here instead of an expression. Something along the lines of:
...
footer: computable("$group.seatbelts", "$group.distance", (p, q) =>
{
return q != 0 ? Format.value(100.0 * p / q, "n;1") : '--';
}),
...
This way you can have footers as complex as you like, and you can easily abstract the logic away into a generic factory function returning anything you want. For an example of this, take a look at this fiddle:
https://cxjs.io/fiddle/?f=xWw8ob40

There's also an undocumented feature of using expressions within string templates:
footer: {
tpl: '{[{$group.seatbelts} != 0 ? {$group.distance}/{$group.seatbelts} : null]:n;1}'
}

Related

how to render a specific field dynamically in Material Table React

HI I am using Material Table of React, what I want to do is generate a label tag for every cell, what I did is:
<Table
onChangePage={handleChangePage}
page={props.currentPage}
totalCount={props.totalCount}
options={{
paging:true,
pageSizeOptions:[10, 15, 25, 50],
pageSize: props.rowsPerPage,
padding: "dense",
search: false,
toolbar:false
}}
columns={columns.map((tableColumn) =>{
return{
...tableColumn,
render: (rowData:VulnerabilityData) =>
(<label>
{rowData[tableColumn.field]}. <---- error 'undefined'
</label>)
}
})}
data={vulnerabilityDataAct}
/>
In order to get the specific field, I passed in the tableColumn.field in the render,
but I got the error
TS2538: Type 'undefined' cannot be used as an index type.
I think tableColumn.field is not allowed here, so how do I dynamically pass the tableColumn field and used as an index to render the value???
Edit:
columns variables in above code:
const columns: TableColumn<VulnerabilityData>[] = [
{
title: 'Due Date',
field: 'remediation_due_date',
},
{
title: 'Application',
field: 'primaryApplication'
},
{
title: 'Impact',
field: 'consequence'
},
{
title: 'Mitigation',
field: 'solution'
},
{
title: 'CVE Description',
field: 'cve_urls'
},
{
title: 'Vulnerability Fix',
field: 'vendor_urls'
}
// and other 100 columns
];
and the definition of VulnerabilityData
export interface VulnerabilityData {
primaryApplication: string;
networkEnvironment: string;
remediation_due_date: string,
// ... other fields
}
The First thing is that you don't have to specify index numbers in the custom column rendering. In custom column rendering Material table provides single rowData for that specific talbe field. This means you're not required to specify index number Just use the rowData.FieldName. You can do it as follows
columns={[
{ title: "Any Title", render: rowData => rowData.FieldYouWantToDisplay },
]}
Taking a shot at this. It looks like the error you are seeing here may be a typescript compiler error.
See this post: Type 'undefined' cannot be used as index type
If it is a compiler error, you may fix it by explicitly defining the type of tableColumns in your map function. This type could be {field: string}, in order to let the typescript compiler know that tableColumn.Field will not be undefined.
You could also tackle this by setting a type on columns field before it is mapped.
Or you could also use rowData[tableColumns.field!], as in the SO link.
I hope this solves your problem!

Finding results that are part of a set with FlexSearch

I have an app that uses FlexSearch. In this app, I have an array of items that looks like this:
let results = [
{ id:'a', name:'The title', status:'in-stock' },
{ id:'b', name:'Another title', status:'out-of-stock' },
{ id:'c', name:'Some other', status:'discontinued' },
];
const resultSchema = {
id: 'id',
field: [
'name',
'status'
]
};
const resultIndex = new FlexSearch({
profile:'score',
doc: resultSchema
});
resultIndex.add(results);
My page has checkboxes for the statuses (in-stock, out-of-stock, and discontinued). My question is, how do I find results that are either a) in-stock or b) out-of-stock. I do not see a way to perform logical ORs with the where function. For example, I'd like to be able to say, give me all results that are either in-stock or out-of-stock. From a logical perspective, one could say, give me everything that is NOT discontinued. However, this is not my entire data set. For that reason, I'm trying to figure out how to do ORs within Flexsearch.
Thank you!
See the section Logical Operators in the readme. This seems to work:
const res = resultIndex.search([
{ field: 'status', query: 'in-stock', bool: 'or' },
{ field: 'status', query: 'out-of-stock', bool: 'or' },
]);
Strangely, { field: 'status', query: 'discontinued', bool: 'not' } didn't work when I tried it.
Doesn't using a custom function solve your problem?(found it in the document)
Something like this:
resultIndex.where(function(item){
return item.status === "in-stock" || item.status === "out-of-stock";
});

How to split binding string ExtJS

How can I split string and get length if she bind?
I tried this, but displayed value is empty:
{
xtype: 'displayfield',
fieldLabel: __('sending_to'),
bind: {
value: '{recipients.split("\n").length}'
}
}
You can use formulas to put more logic in it. And about your question, here's the FIDDLE

Add a param to a function that contains a default param

I have a code as following :
{
headerName: "A",
valueGetter: 'data.a',
field: 'a',
cellRenderer:ACellRenderer,
width: 100,
filter: 'number'
}
And this :
function ACellRenderer(params){
if(params.data && params.data.a){
// Do something
}
}
I don't understand where that params parameter came from, as I only call the ACellRenderer without any params.
I also want to call ACellRenderer with a param I pass to it, apparently doing this : cellRenderer:ACellRenderer(myParam) won't work.
I'm having a hard time understanding your question from the way it's worded, but perhaps you're looking to do this?
{
headerName: "A",
valueGetter: 'data.a',
field: 'a',
cellRenderer:function(){ACellRenderer(<put your parameter here>);},
width: 100,
filter: 'number'
}
Also, for future reference, the common term is argument, not param. Not that it matters that much, but it'll help you be understood better by other Javascript programmers.
This :
var Obj = {
headerName: "A",
valueGetter: 'data.a',
field: 'a',
cellRenderer:ACellRenderer,
width: 100,
filter: 'number'
}
Is just a definition of an object.
This line : cellRenderer:ACellRenderer Stores the function in that particular variable Basic JS like this :
var func = function(){}
Now calling it will be like :
Obj['cellRenderer']();
OR
Obj.cellRenderer();
The function expects an object as a parameter, i don't see how that is confusing.

Extjs undefined method when extending through define

Can any tell me why i keep getting a method buildItems not defined in the following code ? Am i escaping something essential ?
Ext.define('MyApp.view.Viewport', {
extend: 'Ext.container.Viewport',
requires: [
'Ext.layout.container.Border'
],
layout : 'border',
items : [this.buildItems()],
buildItems : function() {
return { region:'center',xtype:'panel'}
}
});
The buildItems method has no reason to be a public method, i was only trying this way first. This is the way i'm doing it now:
(function() {
function buildItems () {
return [
{
region : 'center',
xtype : 'panel',
}, {
region : 'west',
xtype : 'panel',
width : 225
},{
region : 'south',
xtype : 'panel',
height : 50
},{
region : 'north',
xtype : 'panel',
height : 50
}
]
}
return Ext.define('MyApp.view.Viewport', {
extend: 'Ext.container.Viewport',
requires: [
'Ext.layout.container.Border'
],
layout : 'border',
items : buildItems()
});
})();
Is this an overstretch ?
thx
The problem is: At the time of execution of the line
items : [this.buildItems()],
the scope is the Global Object, i.e. this evaluates to window.
You should not put items into a class anyway, since the instances may modify the item's items, so the right way to do it is
initComponent: function () {
this.items = this.buildItems(); // now, "this" is the current component instance
// superclass.initComponent...
}
Edit: As an answer to the second part of the question
This has been discussed a million times, and there's nothing wrong with making that helper function private. I personally tend to keep methods public, since that increases readability of the code. Usually I use doc comments (#private) as a marker, and quite simply avoid calling supposedly private methods. I think this is not a big deal, since I'm mostly not building libraries or anything reusable for third-party developers.
It is because the function is defined in another function and the viewport will be accessed outside this self-executing function. So, put this buildItems() outside the self-executing function and try the same.

Categories