0

I'm very new to angular, and I can't figure out how to generate a routing link programmatically without navigating to it.

I'm using angular-slickgrid and I have a custom formatter on some columns that needs to generate some HTML to render the cell, and I'd like to have a link to a separate component in there :

const formatZoneCell: Formatter = (row: number, cell: number, value: any, columnDef: Column, dataContext: any, grid?: any) =>
{
  if (value === undefined) { return (""); }
  var zone = 0;
  return (`<span>${value} - <a ?>details</a></span>`);
}

I've tried a few things where the ? is, like '[routerLink]="[...]"' but clearly it doesn't get evaluated. Is there a way in that function to generate the link before the return, or do I have to hardcode it ?

I know a solution is to have an onclick event and call a function that will then do a router.navigate call, but I haven't gone this way since I imagine that'll generate links the user can't see when hovering.

Bonus question, if anyone knows of a way to define a context menu per cell with angular-slickgrid I'd be grateful. It looks like there's a few context menu options but none of them seem to have a good way to know which cell was clicked on, which is why I'm trying to add a link in the cell itself through a formatter, but that's not very pretty.

I'd love to be able to add an entry "details" to that right click menu that would link to my other page with the proper parameters depending on which cell was right clicked on.

ghiscoding
  • 12,308
  • 6
  • 69
  • 112
Ulrar
  • 895
  • 8
  • 17

1 Answers1

5

Note, I'm the author of Angular-Slickgrid

You shouldn't add an href link in a Formatter, it's not safe (XSS injection, like <script> tag) and it shouldn't be trusted, also you don't want to use a plain href since that will reload the entire page (outside of the SPA)... What you should do instead is use the click event and when that particular column is clicked then do something. That is exactly what I've done in our project, my Formatter makes it look like a link but in reality the user can click anywhere in the cell (most user will never notice that, especially so if your column width is the size of the text) and from an event handler you can do an Angular route change while staying in the SPA (Single Page App).

So like I said, I have a Custom Formatter to make it look like an hyperlink and here is the code (with translation in our case)

import { Formatter, Column } from 'angular-slickgrid';

export const openFormatter: Formatter = (row: number, cell: number, value: any, columnDef: Column, dataContext: any, grid: any) => {
  const gridOptions = (grid && typeof grid.getOptions === 'function') ? grid.getOptions() : {};
  const translate = gridOptions.i18n;
  let output = '';

  if (translate && translate.instant) {
    const openTxt = translate.instant('OPEN');
    const openForDetailTxt = translate.instant('OPEN_FOR_DETAILS');
    output = `<span class="fake-hyperlink" title="${openForDetailTxt}">${openTxt}</span>`;
  }

  return output;
};

For the fake hyperlink, I have this simple SASS styling

.fake-hyperlink {
    cursor: pointer;
    color: blue;
    &:hover {
        text-decoration: underline;
    }
}

in the View I watch a cell click event via the (sgOnClick)

<angular-slickgrid
         gridId="userGrid"
         [columnDefinitions]="columnDefinitions"
         [gridOptions]="gridOptions"
         (onAngularGridCreated)="angularGridReady($event)"
         (sgOnClick)="handleOnCellClick($event.detail.eventData, $event.detail.args)">
</angular-slickgrid>

and in the Component, for the cell click handler I do a switch case on the column name and when I know the user clicked on that column I then do my action (in the example below, it's the column field userNumber), from the args you can get the grid and get most of what you want with regular SlickGrid methods.

handleOnCellClick(event, args) {
  const grid = args.grid;
  const selectedUser = grid && grid.getDataItem(args.row) as User;
  const columnDef = grid && grid.getColumns()[args.cell];
  const field = columnDef && columnDef.field || '';

  switch (field) {
    case 'userNumber':
      const url = `/users/${selectedUser.id}`;
   
      // we can also look at the Event used to handle Ctrl+Click and/or Shift+Click
      if (event.ctrlKey || event.shiftKey) {
        const openUrl = this.location.prepareExternalUrl(url);
        window.open(openUrl, '_blank'); // open url in a new Browser Tab
      } else {
        this.router.navigateByUrl(url); // or open url in same Tab
      }
      break;

    default:
      // clicked on any other column, do something else?
      // for example in our project we open a sidebar detail view using Bootstrap columns
      // this.openSidebar(); 
      break;
  }
}

On a final note and to give you a more detailed description of our project, if the user clicks on the column which has the fake link it will then execute the click handler pass through the switch case and will route to the particular user detail page, if however the user clicks anywhere else it will open a sidebar (we use Bootstrap Columns. For a quick explanation of how I do a sidebar, I simply change the grid container with Bootsrap classes from col-sm-12 to col-sm-8 and the sidebar to col-sm-4 and then call a grid resize (and vice-versa when I close it). Our sidebar is like a quick view (a condensed detail page) on the other end if they click on the fake hyperlink column it will open the full user detail page.

You can also reuse this concept with a Context Menu, Grid Menu, etc...

Give it a try... and a Star upvote if you like the lib ;)

ghiscoding
  • 12,308
  • 6
  • 69
  • 112
  • Hey, Thanks for the very detailed answer ! But it sounds like any click in the cell will trigger the redirection, which means the user won't be able to copy the content of the cell easily right ? We have other apps that work that way and they drive me crazy. But I guess the context menu does have a copy option for that purpose so if I just take the habit of using that instead it'll be great. I'll give it a shot today and report back, thanks ! – Ulrar Jun 29 '20 at 09:56
  • 1
    I actually like the idea of a sidebar a lot, so I went with that. Used your onClick example to have a sidebar open (also using changing col-x from bootstrap) when the user clicks on the cells, works like a charm. Thanks again ! – Ulrar Jun 29 '20 at 11:13