Home > database >  I have created an angular directive for styling a table. But table loses the styles defined in the d
I have created an angular directive for styling a table. But table loses the styles defined in the d

Time:05-28

I have created the following custom directive in Angular:

@Directive({
    selector: '[tableTheme]'
})
export class TableThemeDirective implements OnInit, AfterViewInit {
    tableElement: HTMLTableElement;

    constructor(private el: ElementRef, private renderer: Renderer2) {}

    ngOnInit(): void {
        this.tableElement = this.el.nativeElement;

        this.renderer.setStyle(this.tableElement, 'width', '100%');
        this.renderer.setStyle(this.tableElement, 'border-collapse', 'separate');
        this.renderer.setStyle(this.tableElement, 'border-spacing', '0 1em');
        this.renderer.setStyle(this.tableElement, 'color', 'white');

        const thElements: NodeListOf<HTMLTableCellElement> = this.tableElement.querySelectorAll('th');

        thElements.forEach((el) => {
            this.renderer.setStyle(el, 'height', '2em');
            this.renderer.setStyle(el, 'margin-bottom', '1em');
            this.renderer.setStyle(el, 'padding', '1em');
        });        
    }

    ngAfterViewInit(): void {
        const tdElements: NodeListOf<HTMLTableCellElement> = this.tableElement.querySelectorAll('td');

        tdElements.forEach((el) => {
            this.renderer.setStyle(el, 'background-color', 'var(--primary-clr-light)');            
        });

        let tdFirstChildElements: HTMLTableCellElement[] = [];
        let tdLastChildElements: HTMLTableCellElement[] = [];

        tdElements.forEach((el) => {
            const parentNode: ParentNode = el.parentNode;
            
            if(el === parentNode.firstElementChild) {
                tdFirstChildElements.push(el);
            } else if(el === parentNode.lastElementChild) {
                tdLastChildElements.push(el);
            }
        });

        console.log(tdFirstChildElements);
        console.log(tdLastChildElements);
        

        tdFirstChildElements.forEach((el) => {
            this.renderer.setStyle(el, 'border-left-style', 'solid');
            this.renderer.setStyle(el, 'border-top-left-radius', '10px');
            this.renderer.setStyle(el, 'border-bottom-left-radius', '10px');
            this.renderer.setStyle(el, 'padding', '1em');
        });

        tdLastChildElements.forEach((el) => {
            this.renderer.setStyle(el, 'border-right-style', 'solid');
            this.renderer.setStyle(el, 'border-top-right-radius', '10px');
            this.renderer.setStyle(el, 'border-bottom-right-radius', '10px');
            this.renderer.setStyle(el, 'padding', '1em');
        });
    }
}

Then I use this directive to style a table:

<table  tableTheme>
    <thead>
        <tr>
            <th>ID</th> 
            <th>Name</th>
            <th>Username</th>
            <th>Role</th>
            <th>Email</th>
            <th>Contact Number</th>
            <th>Action</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let user of displayingUsers">
            <td>{{ user.id }}</td>
            <td>{{ user.name }}</td>
            <td>{{ user.username }}</td>
            <td>{{ user.role }}</td>
            <td>{{ user.email }}</td>
            <td>{{ user.contactNumber }}</td>
            <td>Action</td>
        </tr>
    </tbody>
  </table>

As you can see the directive is attached to the table element.

Then I add some logic for pagination below the table:

  <div >
    <button 
         
        type="button" 
        *ngFor="let item of createRange(collectionLength); let i = index"
        (click)="onPaginationButtonClick(i   1)">
        {{ i   1 }}
    </button>
  </div>

Implementation of the pagination:

export class UsersComponent implements OnInit {

  users: User[];
  displayingUsers: User[];
  currentPage: number = 1;
  perPage = 4;
  collectionLength: number;

  createRange(count: number): number[] {
    return new Array(count);
  }

  onPaginationButtonClick(clickedPage: number): void {
    this.displayingUsers = this.users.slice((clickedPage - 1) * this.perPage, (clickedPage - 1) * this.perPage   this.perPage);
  }

  constructor() { }

  ngOnInit(): void {
    this.users = USERS;
    this.onPaginationButtonClick(1);
    this.collectionLength = Math.ceil(this.users.length / this.perPage)
  }
}

Pagination completely works. But the table looses the style defined in the directive once the pagination buttons are clicked. (Following screenshots shows the problem.)

Before pagination buttons are clicked

After pagination buttons are clicked

As you can see the table loses the styles defined in the directive.

How to keep the styles defined in the directive?

CodePudding user response:

You can use renderer.addClass to add style to the element

ngOnInit(): void {
    this.tableElement = this.el.nativeElement;
    // add class(table-theme) to the component.css
    this.renderer.addClass(this.el.nativeElement, 'table-theme');
  }

In HTML, wrap the table inside a div and apply your directive on the div

<div table-theme>
 <table>......</table>
<div>

Please find the working solution link below
https://stackblitz.com/edit/angular-ivy-9fqtkq?file=src/app/app.component.ts
  • Related