-->

Change element width dynamically on Angular 5

2020-07-19 03:36发布

问题:

I have two tables.

I want to set the width of the column of one table to equal the width of the column of another table.

Like this :

    <table class="table-header table">
        <thead class="table-bordered">
          <tr>
              <th>Name</th>
              <th>Last Name</th>
              <th>Gender</th>
              <th>Education</th>
            </tr>
        </thead>
      </table>

And the other table:

          <table class="table-header table">
         <thead class="table-bordered">
          <tr>
              <th>Name</th>
              <th>Last Name</th>
              <th>Gender</th>
              <th>Education</th>
            </tr>
        </thead>
      <tbody> 
         <tr> 
            <td> Ross </td>
             <td> Ross </td>
            <td> M</td>
            <td> BsC </td>
        ..
      </tbody>

What I want to do is have all the headers of the first table to always equal the headers width of the second table. Because on the second table they get resized based on the content.

So I know I can add a HostListener('window:resize') But how do I assign these values in Angular ? So that they're always the same. As if it's a duplicate of one another. Any guidance?

回答1:

First you need to get a reference of your source table columns and your target table columns, you can do this with a template variables and the @ViewChildren decorator.

In your template:

<!-- Source table -->
<table class="table-header table">
  <thead class="table-bordered">
    <tr>
        <th #sourceTh>Name</th>
        <th #sourceTh>Last Name</th>
        <th #sourceTh>Gender</th>
        <th #sourceTh>Education</th>
      </tr>
  </thead>
</table>

<!-- Target table -->
<table class="table-header table">
  <thead class="table-bordered">
    <tr>
      <th #targetTh>Name</th>
      <th #targetTh>Last Name</th>
      <th #targetTh>Gender</th>
      <th #targetTh>Education</th>
    </tr>
  </thead>
  <tbody>
    <!-- table body content -->
  </tbody>
</table>

And then inside you component class

@Component({ ... })  
export class MyClass {
  @ViewChildren('sourceTh')
  sourceTh: QueryList<ElementRef>;

  @ViewChildren('targetTh')
  targetTh: QueryList<ElementRef>;

  // ...
}

This way you can get a QueryList of elements references, so you can access the nativeElement of each table heading (the actual DOM Nodes) and get each individual offsetWidth. Notice @ViewChild and @ViewChildren decorated properties are only available after the AfterViewInit lifecycle hook. A QueryList is an array-like data structure and it has some array methods like forEach, map, etc...

Inside ngAfterViewInit you want to iterate over the list of source table header cells, get each one width and then iterate over target header cell list and set the width. For this, you can use the setStyle method from Renderer2. Then you can call this method also on window.resize event or whatever

@Component({ ... })  
export class MyClass implements AfterViewInit {
  @ViewChildren('sourceTh')
  sourceTh: QueryList<ElementRef>;

  @ViewChildren('targetTh')
  targetTh: QueryList<ElementRef>;

  constructor( private renderer: Renderer2 ) {}

  ngAfterViewInit() {
    this.resizeColumns();
  }

  resizeColumns() {
    let widths = this.sourceTh.map(th => th.nativeElement.offsetWidth);

    this.targetTh.forEach((th, index) => {
      this.renderer.setStyle(
        th.nativeElement, 
        'width', 
        `${widths[index]}px`
      );
    });
  }
}


回答2:

This does work but if you were to do a ctrl f5 it breaks the table. In order for me to fixed this issue I had to use ngAfterVeiwChecked but however life cycle hook constantly triggers when the dom changes or screen size changes ect... I'm not sure I can find a efficient way.

In the picture you will see the headers break if you were to do a ctrl + f5 but however after that if you just simply refreshed your page via the refresh button it's fine. It's just when you do a ctrl f5

Broken Headers