1

I have a very simple Angular attribute directive that looks something like this:

import { Directive, Input, OnInit } from "@angular/core";

@Directive({
  selector: "[foo]",
})
export class FooDirective implements OnInit {
  @Input() public foo: string;

  public ngOnInit(): void {
    // Do something here once
  }
}

It will be used like this:

<div [foo]="bar"></div>

bar might be a simple string, but could also be a string getter that is relatively expensive to run. I would therefore like to read the value once on initialization, but ignore it afterwards. I know that the ngOnInit is only called once, but change detection will cause the bar property to be called repeatedly (depending on how the containing component works).

Using @Attributes would have been nice, since they only get evaluated once. But it doesn't seem like @Attribute works for directives (with the same name?):

import { Directive, OnInit, Attribute } from "@angular/core";

@Directive({
  selector: "[foo]",
})
export class FooDirective implements OnInit {
  public constructor(
    @Attribute("foo")
    private readonly foo: string
  ) {}

  public ngOnInit(): void {
    // Do something with this.foo here
  }
}

How do I avoid that the input property is evaluated more than once? I would prefer to unsubscribe from any changes, so to speak.

Directives don't seem to support ChangeDetectionStrategy.OnPush, but something similar could be nice.

3
  • 1
    There is actually something to achieve this called ChangedetectorRef.detach(). However, this completely removes change detection and not only for the desired Input(). I agree with the answer that this is not a problem a directive can solve. Commented May 18, 2022 at 14:03
  • So what does the ChangeDetectorRef.detach() in this case detach change detection for? Is that the component where the attribute is use? Or just everywhere (whatever that might mean)? Commented May 18, 2022 at 17:55
  • 1
    The documentation isn't very clear about this and just says detaches this view. Basically, no change detection for the component or its children. you can use .detectChanges() to implement local change detection but... you can't do that from a directive alone either. Commented May 18, 2022 at 21:32

1 Answer 1

2

I guess this is not a problem to solve on the directive definition side. Every time that change detection runs on the component that host that div the expensive calculation on the string getter takes place. So I would not use that expensive string getter in the template, instead I would work around to assign that result to a variable in my class and use that variable on the template. You could do the expensive calculation on the ngOnInit or other lifecycle hook that fits better your conditions, in the component that host that div.

Just for clarification, this is the div that I'm talking about:

  <div [foo]="bar"></div>

To quickly sumarize, my suggestion is use the lifecycle hooks in the components that host this div to get the value, pass to a variable, and use that variable on the template instead of the expensive string getter.

Sign up to request clarification or add additional context in comments.

1 Comment

I think this is the right approach, specifically ngOnChanges. It is possible to detect if the change is the first change too and act accordingly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.