htmlangularangular5elementrefangular-renderer2

How to Apply styled using Renderer2 to a child node of ElementRef -> nativeElement in Angular


Suppose I have the following Angular Template:

<p class="margin0 text-overflow table-cell" #ParentP>
  <span id="managerDisplayName" #FirstSpan class="bold" title="{{ someBinding 1}}">{{ someBinding2 }}</span>
  <span id="regionName" #SecondSpan class="bold regionName" title="{{ someBinding3 }}"> {{ someBinding4 }}</span> 
  <span class="service-level-icon">
    <b placement="bottom" #IconHolder triggers="mouseenter:mouseleave" container="body" *ngIf=" someCondition1 " popoverTitle="" [ngbPopover]="servicelevelcontent"><i class="somecon"></i></b>                                        
  </span>
</p>

This section of code is being generated multiple times using *ngFor.

I want to dynamically change the left alignment of the <b> tag by calculating the width of the first and second <span>.

I already tried using [style.left.px]="FirstSpan.offsetWidth + SecondSpan.offsetWidth + 3", but this throws the Expression Changed after Checked error.

I then thought of trying out using QueryList<ElementRef> but I am facing the problem that the icon is present only in some of the cases and hence, I am not able to add style to the <b> by using the children property of the ElementRef as Renderer2 is throwing error stating unable to find style property.

Please help me solve this issue.


Solution

  • Just go with [style.left.px] & use @viewChildren for selecting those elements. Update your view code to:

    <p class="margin0 text-overflow table-cell" #ParentP>
      <span id="managerDisplayName" #FirstSpan class="bold" title="{{ someBinding 1}}">{{ someBinding2 }}</span>
      <span id="regionName" #SecondSpan class="bold regionName" title="{{ someBinding3 }}"> {{ someBinding4 }}</span> 
      <span class="service-level-icon">
        <b placement="bottom" #IconHolder [style.left.px]="updatedLeftStyle" triggers="mouseenter:mouseleave" container="body" *ngIf=" someCondition1 " popoverTitle="" [ngbPopover]="servicelevelcontent"><i class="somecon"></i></b>                                        
      </span>
    </p>
    

    Then in component class code make sure you import ChangeDetectorRef :

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

    This's for updating value of context variable after ViewInit. Now following changes in class:

    @ViewChildren('FirstSpan,SecondSpan') spans:QueryList<ElementRef>;
    
    constructor(private cdRef:ChangeDetectorRef) { }
    
    ngAfterViewInit() {
        console.debug(this.spans); 
        let eleArr: any = this.spans.toArray();
        this.updatedLeftStyle = eleArr[0].nativeElement.offsetWidth + eleArr[1].nativeElement.offsetWidth;
        this.cdRef.detectChanges();
      }
    

    Demo Example