简体   繁体   中英

Angular, when can I use template references variables within angular directives?

I'm new in angular and I have this doubt. I have a very strange behaviour using the next code:

 <select #selectColor class="btn btn-primary" name="button"> <option [value]="color" *ngFor="let color of COLORS">{{color}}</option> </select> <p customDirective [ngStyle]="{'color':selectColor.value}"> Test paragraph </p>

This code shows the next error in browser console and it doesn't work:

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'color: '. Current value: 'color: yellow'.

But it "works" and change the color when I pass mouse over the element because the customDirective has defined the next methods:

@HostListener('mouseenter') mouseEnter(){
    //Some code
}

@HostListener('mouseleave') mouseLeave(){
    //Some code
}

I upload a demo at: https://angular-zxhhml.stackblitz.io

Thanks.

You are trying to bind the value of a template variable to a directive. You do not want to use property binding.

You can force the bound template variable to update by adding a (change) event handler with some arbitrary expression.

component.html

<select class="btn btn-primary" name="button" #selectColor (change)="0">
  <option [value]="color" *ngFor="let color of COLORS">{{color}}</option>
</select>

<p customDirective [ngStyle]="{'color':selectedColor}">
  Test paragraph
</p>

Of course, the more standard (in my opinion) - and easier - way to do this is with property binding.

[(ngModel)]="selectedColor" , and then bind to selectedColor . No property is strictly required in the component, although I would always recommend adding one for clarity.

DEMO: https://stackblitz.com/edit/angular-lz1cae

On Component/Directive creation, Angular executes a set of methods that's is known as the lifecycle hook. This mechanism is described here:

https://angular.io/guide/lifecycle-hooks

The problem is that all methods of this lifecycle are executed on the same javascript vm turn and when your select is created the first value (here yellow) will be selected, and causes that error.

A solution, as said in previous answer is to use ngModel to keep track of the color that is selected. In order to select the first color by default, I use the setTimeout function that allow to postpone execution on next javascript vm turn and avoid the previous error.

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements AfterViewInit {
  name = 'Angular';
  COLORS = ['yellow', 'red', 'blue'];
  selectedColor: string;

  ngAfterViewInit(): void {
    setTimeout(() => {
      this.selectedColor = this.COLORS[0];
    });    
  }
}

Then link selected color to your directive using an Input.

@Directive({
  selector: '[customDirective]'
})
export class CustomDirectiveDirective {

  @Input() color: string;

  constructor(private readonly element: ElementRef) { }

  @HostListener('mouseenter') mouseEnter(){
    // Some code
  }

  @HostListener('mouseleave') mouseLeave(){
    // Some code
  }

}

Here is an example on stackblitz: https://stackblitz.com/edit/angular-ssq5af

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM