trofinao
trofinao

Reputation: 11

How to access to an element created on the fly in angular 12

I am using a table similar to datatables.net (it is not the same, but very similar) in an angular 12 project.

I need to populate the table with data like:

Id Name Action
0 John Delete
1 Conn Delete

The "Delete" action is a button that looks like a link and it would delete the current row.

When the user comes to the table for first time it needs to be empty. With a button the user imports data from a csv file.

Id Name Action

In the html component the table looks like this:

<table data-table="tt__table">
</table>
<input type="button" value="Import" (click)="importData()" />

When I import the data from csv in typescript I have something like:

ngAfterViewInit(): void {       
  let options = {
    data: {
      headings: ["Id", "Name", "Actions"],
      rows: [
        {
          data: ["", "", ""]
        }
       ]
     }
   };
        
   this.tableElement = document.querySelector('[data-table=\"tt__table\"]') as Element;
   this.table = new Table(this.tableElement, options);            
}
...
importData(): void{
  let importedData = {
          "type": 'csv',
          "data": '0,John,<button data-toggle="tt__btn" class="tt__btn-link (click)="deleteRow($event)">Delete</button>'
       };
        
  this.table.deleteAll();
  this.table.import(importedData); // rows are created on the fly using vanilla js.
        
  const tableButtons = document.querySelectorAll('[data-toggle=\"tt__btn\"]');
  tableButtons.forEach(button => {
    new Button(button, {})
  });
}
        
deleteRow(event): void{
             // Delete row.
          }

The imported data looks fine after click on "Add" button:

Id Name Action
0 John Delete

But when I click in "Delete" button it does not do anything.

I suspect the reason is that Angular cannot see the new DOM elements created because the HTML after clicking on "Add" button looks like this:

<tbody>
  <tr>
     <td data-content="0">0</td>
     <td data-content="John">John</td>
     <td data-content="Delete">
        <button data-toggle="tt__btn" class="tt__btn-link" (click)="deleteRow($event)">Delete</button>
     </td>
  </tr>
</tbody>

But in other components where I use "(click)" angular directive, the final HTML does not show the "(click)" directive.

I am very new to angular, could you please help me how to solve this problem?

Upvotes: 0

Views: 716

Answers (2)

trofinao
trofinao

Reputation: 11

I solved it by using Renderer2 to listen DOM events.

  1. First I injected the renderer2 in constructor:

    import { AfterViewInit, Renderer2, OnDestroy } from '@angular/core';
    unlistener: () => void;
    ...
    constructor(private renderer: Renderer2) {
    ...
    }
    
  2. Then I added a listener to renderer instance, which listen to DOM table "click" events and call to "deleteRow" function:

    ngAfterViewInit(): void {
      ...
      this.tableElement = document.querySelector('[data-table=\"tt__table\"]') as Element;
    
      unlistener = this.renderer.listen(this.tableElement, "click", event => 
      this.deleteRow(event));
    
      this.table = new Table(this.tableElement, options);
      ...
    }
    
  3. "deleteRow" function catch the click events in the table and delete rows according to the button id (which has the user id):

    deleteRow(event: any): void {
      let userId: string = '';
      let userToDelete: User;
      let userIndex: number = -1;
    
      if (event !== null && event !== undefined) {
        if (event.target instanceof HTMLButtonElement) {
          userId = event.target.id;
            if (userId !== '') {
               userToDelete = this.selectedUsers.find(u => u.userId === userId) as User;
              if (userToDelete !== null && userToDelete !== undefined) {
                userIndex = this.selectedUsers.indexOf(userToDelete);
                this.selectedUsers.splice(userIndex, 1);
                this.updateTable();
              }
            }
          }
        }
      }
    
  4. In ngOnDestroy() hook add unlistener to stop listening DOM events:

     ngOnDestroy(): void {
       this.unlistener();
     }
    

Thanks for your replies, specially to @Mohamed Babei, his answer shed the light to my research.

This solution is based on Four ways of listening to DOM events in Angular (Part 3: Renderer2.listen) article.

Upvotes: 1

Mohammad Babaei
Mohammad Babaei

Reputation: 493

You can not bind angular events this way so you need to render your data in table angular way for example useing '''*ngFor''' or if you want go your way use addEventListener( click ) after rendering content

Upvotes: 0

Related Questions