1
votes

My directive is losing it's style when a value is passed to it dynamically inside a ngFor loop.

I have this directive for a datatable column, where I set the name of the column.

@Directive({ selector: '[my-dt-column]' })
export class MyDTColumn{
    @Output() onSort: EventEmitter<any> = new EventEmitter();

    @HostBinding('class.sorted-asc') sortedAsc;
    @HostBinding('class.sorted-desc') sortedDesc;
    _sortOrder: string;
    public sort(order?: string){
        this._sortOrder = order !== undefined ? order: (!this._sortOrder || this._sortOrder=='desc') ? 'asc' : 'desc'
        this.sortedAsc = this.sortOrder === 'asc';
        this.sortedDesc = this.sortOrder === 'desc';
    }
    get sortOrder(): string{
        return this._sortOrder;
    }
    get isSorted(): boolean{
        return this._sortOrder !== undefined && this._sortOrder !== null && this._sortOrder !== '';
    }
    column: string;
    @Input('my-dt-column')
    set setItem(column: any){
        this.column = column;

    }

    @HostListener('click') onClick(){
        this.sort();
        this.onSort.emit({orderBy:this.column, sortOrder:this._sortOrder});
    }

    constructor(private el: ElementRef){}
}

And some style for it, which basically sets the direction of an arrow-icon, indicating if the column is sorted or not, and its order.

:host >>>> table [my-dt-column]{
    cursor: pointer;
}
:host >>>> table  [my-dt-column] md-icon{
    font-size: 10px;
    height: 16px;
    line-height: 16px;
    vertical-align: middle;
    opacity: 0;
    color: rgba(0, 0, 0, 0.38);
    transition: transform .25s, opacity .25s;
    transform-origin: center center;
    text-align: center;
}
:host >>>> table [my-dt-column].sorted-asc,
:host >>>> table [my-dt-column].sorted-desc{
    color: rgba(0, 0, 0, 0.87);
}
:host >>>> table [my-dt-column].sorted-asc md-icon,
:host >>>> table [my-dt-column].sorted-desc md-icon{
    opacity: 1;
    color: rgba(0, 0, 0, 0.87);
}
:host >>>> table [my-dt-column].sorted-asc md-icon{
    transform: rotate(0deg);
}
:host >>>> table [my-dt-column].sorted-desc md-icon{
    transform: rotate(180deg);
}
:host >>>> table [my-dt-column]:hover md-icon{
    opacity: 1;
}

I was adding table headers with this directive manually in html, to something like this:

<th my-dt-column="ColumnName">some code</th>
<th my-dt-column="Column2Name">some code</th>
<th my-dt-column="Column3Name">some code</th>
<th my-dt-column="Column4Name">some code</th>

But in order to prevent typing errors, I decided to make an array in my parent component with the name of my columns, and added dynamically in html with ngFor. So my code became something like this:

Style over directive won't work, but the column's name is set.

<ng-container *ngFor="let column of columns">
   <th [my-dt-column]="column.name">some code</th>
</ng-container>

But suddenly my style over this directive stopped working. If I change the above code directive to some random string, my style will work, but the name of the column won't be correct.

Style over directive work, but the column name is incorrect.

<ng-container *ngFor="let column of columns">
   <th my-dt-column="randomstring">some code</th>
</ng-container>

I couldn't find any answer for a problem like this, and any help would be nice.

EDIT: 1 more clue: when I bind a random string to this directive, it is set in my HTML(my-dt-column directive):

<th _ngcontent-c11="" mdtooltipposition="right" my-dt-column="randomstring" ng-reflect-position="right" ng-reflect-message="" ng-reflect-set-item="randomstring" class="">some code</th>

but when I try to pass the name of my column generated by ngFor, it won't set EVEN in HTML (note that in the example below, my-dt-column directive is missing.

<th _ngcontent-c11="" mdtooltipposition="right" ng-reflect-position="right" ng-reflect-message="" ng-reflect-set-item="InstrumentTypeName" class="">some code</th>
1
create a plunker to reproduce the issue - Aravind
It will require a lot of time since i don't have any prattice with plunker. But i will try, altough my question is more to "why it work with a string, but won't work with a value passed?".Is it related to any delay from ngFor to provide the value in the loop, or something like this ? - mumumilk

1 Answers

1
votes

If you want to keep attribute i can advice you use @HostBinding like

import { HostBinding } from '@angular/core';

....
column: string;
@HostBinding('attr.my-dt-column') @Input('my-dt-column')
set setItem(column: any){
    this.column = column;
}
get setItem() {
  return this.column
}

as you can see you also have to declare getter.

Plunker Example

Another approach is using host property on your component

host: {
  '[attr.my-dt-column]': 'column'
}

Plunker Example