ghiscoding/aurelia-slickgrid

formatter

Closed this issue · 12 comments

| Software | Version(s) |

| ----------------- | ---------- |
| Aurelia : "aurelia-bootstrap": "^0.1.20",
"aurelia-bootstrapper": "2.3.3",
au cli 1.3.1 systemjs
| Aurelia-Slickgrid | "aurelia-slickgrid": "2.20.1",

| Operating System |
Windows10 or WIndowsServer
| Yarn v1.22.4 |

Context

While I have an easy solution using joins or lookups at database level or hard coded formaters. While not pressing need, I was wondering if there is anyway to to call a service thats holding json data with the the formatter, i.e.

this.appService.adjusters is the service that made a db call and reteived the list below.

hard coded call

const adj = [{ "ID": 1, "AdjusterName": "Bergen Risk Managers", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 0 },
{ "ID": 79, "AdjusterName": "Carol Benja", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 219 },
{ "ID": 77, "AdjusterName": "Donna Luciani", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 117 },
{ "ID": 10, "AdjusterName": "Fran Van Wagner", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 113 },
{ "ID": 76, "AdjusterName": "Irene Markel", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 114 },
{ "ID": 35, "AdjusterName": "Jane Scalzitti", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 112 },
{ "ID": 2, "AdjusterName": "Jennifer Dittemer", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 100 },
{ "ID": 75, "AdjusterName": "Jim Markel", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": null },
{ "ID": 3, "AdjusterName": "John Markel", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 101 },
{ "ID": 30, "AdjusterName": "Kathleen McGuire", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 106 },
{ "ID": 78, "AdjusterName": "Lori Shannon", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 118 },
{ "ID": 29, "AdjusterName": "Lynn McCorry", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 107 },
{ "ID": 17, "AdjusterName": "Michele Riabov", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 111 },
{ "ID": 13, "AdjusterName": "Robert Dittemer", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 108 },
{ "ID": 6, "AdjusterName": "Robert McGuire", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 102 },
{ "ID": 28, "AdjusterName": "Trisha McTigue", "AdjusterPhone": null, "Active": "Y   ", "Staff ID": 109 }]

const myadjusterformatter = (row, cell, value, columnDef, dataContext) => {
 let found = adj.find(f => f.ID === value);
  let val
  if (found === -1) {
    val = 'Not available!';
  } else val = found.AdjusterName
  return val
}
 {
   id: 'AdjusterID',
   name: 'Adjuster',
   field: 'AdjusterID',
   sortable: true,
   filterable: true,
   minWidth: 70,
   formatter: myadjusterformatter
 },

I'm not fully sure to understand your question but if you want to get the entire dataset from the Formatter, you can actually get it from the grid object inside the Formatter. Formatters are totally independent and have no knowledge of the outside context, so you must use what is available by its arguments, the 6th and last argument is grid which can provide a lot, from it you can get the grid options and even the dataView object, for example

const myadjusterformatter = (row, cell, value, columnDef, dataContext, grid) => {
  const gridOptions = grid.getOptions();
  const dataView = grid.getData();
  const dataset = dataView.getItems();
  // ...
};

but if you wish to just get the item object then use the dataContext argument, it holds the entire item object of current row.

I believe that should answer your question

Thanks for reply and I was aware of those capabilities and I tried to properly define use case and failed. What I was trying to ask if there is a way to get data outside of the data content just as you can do for editors and filters as shown below from an example I wrote some time ago. At any rate I think you answered and I will just use joins in database to get lookup value.

   {
        id: 'StaffName',
        name: 'StaffName ',
        field: 'assigntoStaffName',
        filterable: true,
        editable: true,
        sortable: true,
        type: FieldType.string,
        minWidth: 190,
        excludeFromExport: true,
        filter: {
          collectionAsync: this.getstaffNames(),
          model: Filters.multipleSelect,
          filterOptions: {
            offsetLeft: 2,
            width: 190
          },
          placeholder: 'choose an option'
        },
        formatter: Formatters.complexObject,
        onCellClick: (e, args) => {
          this.aureliaGrid.gridService.setSelectedRow(args.row);
        },
        editor: {
          collectionAsync: this.getstaffNames(),
           searchTerms: [true],
            model: Editors.singleSelect
        },
        minWidth: 125,
        params: {
          formatters: Formatters.collectionEditor
        }
      },

Well again Formatters have their own context so you can't access anything outside of what is available in the Formatter arguments.
However if your data isn't too big, you could maybe add it to the params property of the grid options, which you can then read from the Formatters by getting the gridOptions as I wrote earlier.

const myadjusterformatter = (row, cell, value, columnDef, dataContext, grid) => {
  const gridOptions = grid.getOptions();
  const extraData = gridOptions.params.extraData;
  // ...
};

But I wouldn't use that for large dataset, it might impact the performance of the grid in general since the grid options are used across the lib, it's preferable to keep it rather small. I often use the grid options params for couple of flags that I want to pass to my Formatters and that's totally fine, it's small enough and it's global to the grid.

Another option is to use inline Formatters, I typically prefer to move my Custom Formatters into separate files (separation of concerns) but if you create your Formatter inline, then you could access the this inside your Formatter with whatever other data you might have. So that's another option too.

Also note that Formatters are synchronous, the data must be ready when you load the grid, it cannot be asynchronous. If you do have async data, then you can use the asyncRenderer but I rarely ever use that because it doesn't provide a good UX since the rendering is delayed by few seconds and it's not that nice.

Correct me if I'm seing but if you bind the calling this like here formatter: Formatters.complexObject.bind(this) you should get the proper scope and can do the same inside your formatter as in your filters example

damn that is a neat trick, I never thought of doing that. this way is so much better, thanks for the tips @zewa666

Thats the bad side of modern JS and arrow functions which let you forget how you used to do things earlier with bind/call ;)

Thanks for all the suggestions, All my shared datasets (mostly code lookups) get initialized on auth login so ther are available to all view/view models
The extra.Data param worked as suggested but not quite sure how to implement @zewa666 tip.

If you bind your formatter as suggested above, inside its function this will point to your viewmodel so the same this as in filter

ViewModel

this.extraData = [ /*...*/ ];

this.columnDefinitions = [
  id: 'StaffName', name: 'StaffName ', field: 'assigntoStaffName',
  formatter: myadjusterformatter.bind(this)
];

Extarnal Custom Formatter File

const myadjusterformatter = (row, cell, value, columnDef, dataContext, grid) => {
  const extraData = this.extraData;
  // ...
};

The this points to the ViewModel scope because you called .bind(this)

Glad to know I had the correct code but when I tried that I get "this" undefined
const myadjusterformatter = (row, cell, value, columnDef, dataContext, grid) => {
const extraData = this.extraData;
// ...
};

Try defining it like this: function myadjusterformatter(...) the arrow cannot be re-bound see here https://stackoverflow.com/questions/33308121/can-you-bind-this-in-an-arrow-function

I think issue (question) was resolved, so I'll close the issue