1

I am using the Angular-Datatables package and using the render function to show a button, except this button needs a click event to redirect to another page, at the moment it's with an onlick but obviously this does a full redirect which defeats the purpose of the Angular being a SPA.

This is my implementation of the datatable

<table datatable [dtOptions]="dtOptions"></table>

this.dtOptions = {
  lengthChange:false,
  ajax: (dataTablesParameters: any, callback) => {
    this.userService.getUsers().subscribe(resp => {
        callback({
          data: resp
        });
      });
  },
  columns: [{
    title: 'Username',
    data: 'name'
  }, {
    title: 'Email',
    data: 'email'
  }, {
    title: 'Phone Number',
    data: 'phoneNumber'
  }, {
    title:'',
    data:null,
    render: function (data, type, row, meta) {
      const temp = JSON.stringify(data);
      return `<button class="btn btn-primary" onclick="location.href='/userdetails?user=`+JSON.parse(JSON.stringify(data)).id+`'">Profile</button>`;
   }
  }],
  responsive: true
};

This question uses the rowCallback function which assigns an click event to the whole row, but I cannot do this because of the responsive:true flag, as without this, in a small window, my table goes off screen, so the responsive fits the row to the width of the screen with a click event to expand the remaining fields which conflicts with the rowCallback if I used it (well specifically the rowCallback takes priority).

What can I do to get around this? I have tried <button class="btn btn-primary" routerLink='/testroute'">Profile</button> but that doesn't do anything, it can be seen via inspect element but route does not change, nor is there errors in the console

5
  • The documentation states you should be able to use router links for actions according to this example. Have you tried it? Commented Nov 17, 2023 at 10:41
  • @Tommi I don't know how I managed to miss that, but I just tried it, and I am not sure how the example works, as it in the example the event is only on the button but in my version it is still on the full row Commented Nov 17, 2023 at 11:12
  • Yes I agree that the example doesn't seem to give the whole solution but it looks like they capture a global clickevent and if it matches the buttons id it then use the navigate() to route internally in angular. If you can update your code with what you've tried it may be easier to help! Commented Nov 17, 2023 at 12:52
  • @Tommi I didn't actually go too in depth with it, all I did was add this ngAfterViewInit(): void { this.renderer.listen('document', 'click', (event) => { console.log(event) }); } just to see it working Commented Nov 17, 2023 at 16:01
  • hi @Tommi I have just posted an answer if you wanted to take a look Commented Nov 20, 2023 at 12:25

1 Answer 1

1

I got a solution that works, it may not be the "right" way but it works for me.

The docs used ngAfterViewInit() and added a click listener but the example wasn't very clear, it adds a click listener to the whole document not the button itself and the docs example the click only works on the button because of a property it looks for which they don't show how is defined. So what I ended up doing was checking the target of the click and checking it's innerHTML to match the button I defined, so it looks like this;

this.renderer.listen('document', 'click', (event) => {
    if(event.target.innerHTML === 'Profile'){
        //navigate function
    }
});

but doing this, I have now "lost" the ID of the user for the table rows, so I assign the user's ID as the ID of the button in the render function, like so;

render: function (data: any, type: any, full: any) {
    return `<button class="btn btn-primary" id="${JSON.parse(JSON.stringify(data)).id}">Profile</button>`;
}

so the navigate function inside my renderer.listen is now;

this.router.navigate(['/userdetails'], {queryParams:{user: event.target.id}});

but now because this assigns a click listener on the whole document you get this being triggered everywhere on every page, so it has to be removed in the ngOnDestroy like this; credit yurzui and AqeelAshiq

listenerFn = () => {};

ngAfterViewInit(): void {
    this.listenerFn = this.renderer.listen(...)
}

ngOnDestroy() { //stop listener from working on every click in every page in site
    this.listenerFn();
}

so to put it all together my component now looks like this;

dtOptions: DataTables.Settings = {};
columns = [{ prop: 'Name' }, { name: 'Email' }, { name: 'Phone Number' }];
listenerFn = () => {};
constructor(private userService: UserService,
  private router:Router,
  private renderer: Renderer2) { }
ngOnInit(): void {
  this.dtOptions = {
    lengthChange:false,
    ajax: (dataTablesParameters: any, callback) => {
      this.userService.getUsers().subscribe(resp => {
          callback({
            data: resp
          });
        });
    },
    columns: [{
      title: 'Username',
      data: 'name'
    }, {
      title: 'Email',
      data: 'email'
    }, {
      title: 'Phone Number',
      data: 'phoneNumber'
    }, {
      title:'',
      data:null,
      render: function (data: any, type: any, full: any) {
        return `<button class="btn btn-primary" id="${JSON.parse(JSON.stringify(data)).id}">Profile</button>`;
      }
    }],
    responsive: true
  };
}
ngAfterViewInit(): void {
  this.listenerFn = this.renderer.listen('document', 'click', (event) => {
    if(event.target.innerHTML === 'Profile'){
      this.router.navigate(['/userdetails'], {queryParams:{user: event.target.id}});
    }
  });
}
ngOnDestroy() { //stop listener from working on every click in every page in site
  this.listenerFn();
}

Now for why I mentioned it might not be the "right" way, as mentioned the click listener is everywhere, not just this table, as mentioned I have mitigated it to be removed when the page navigated away from, but its still there for everywhere on this page, hence the need for the check of it being my actual button. Ideally my solution wouldn't need this as it would be just on the buttons, but that is a problem for another day.

Sign up to request clarification or add additional context in comments.

2 Comments

Nice that you got it to work! Possible refactors to make it more "the right way" would maybe to listen on the table element only (if possible?). I also think the example stored the data in a data attribute on the element, but yours works aswell it seems. And couldnt you make use of a id on the button to select it rather than using the innerHTML ? Just a thought! But as long as it works as intended.
@Tommi listening on just the table would be nice, but I will investigate that a later date,. I am using the id of the button to pass the user GUID so I couldn't reliably check that if statement to see if it's a guid without a complex regex, there is probably another property I could use on the target, but innerHTML is what I chose, mainly for readability. I have used this same process somewhere else now, (obviously not for a user profile) but using innerHTML makes it more of a standard approach I can reuse

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.