Search code examples
angularangular-directive

Angular Directive should not rerun after navigation


I have a directive which is supposed to animate an div content. The animation works fine but it replays after every navigation. Is there a way to only let it run on the initial page load?

**edit

The element I am animating is re-rendered on navigating.

@Directive({
  selector: '[appAnimateText]'
})
export class AnimateTextDirective implements AfterViewInit, OnInit {

  @HostBinding('style.white-space') whiteSpace = 'pre';
  private animateTo: string | undefined;

  constructor(private el: ElementRef) {
  }

  get innerText(): string {
    return this.el.nativeElement.innerText;
  }

  set innerText(newText: string) {
    this.el.nativeElement.innerText = newText;
  }

  ngOnInit(): void {
    this.animateTo = this.innerText;
    // placeholder nbsp string which is than transformed back to the text
    this.innerText = this.innerText.split('').map(() => ' ').join('');
  }

  ngAfterViewInit(): void {
    if (this.animateTo) {
      this.animate(this.animateTo.split(''));
    }
  }

  animate(animateTo: string[]): Promise<void> {
     // animation placeholder
     return new Promise((res) => setTimeout(res, 2000))
  }


Solution

  • I was able so solve the issue by including the animation state in my a service.

    I added a map with the ngContentId and a boolean weather it was finished or not.

    
    // Service
    export class AppService {
      // ...
      public appAnimationState = new Map<string, boolean>();
    
      public getAnimationState(elementId: string): boolean | undefined {
        return this.appAnimationState.get(elementId);
      }
    
      public setAnimationState(elementId: string, state: boolean): void {
        this.appAnimationState.set(elementId, state);
      }
    
      // ...
    }
    
    // Directive
    export class AnimateTextDirective implements OnInit {
    
      @HostBinding('style.white-space') whiteSpace = 'pre';
      private animateTo: string | undefined;
      private ngContentId: string | undefined;
    
      constructor(private el: ElementRef, private appService: AppService) {
      }
    
      get innerText(): string {
        return this.el.nativeElement.innerText;
      }
    
      set innerText(newText: string) {
        this.el.nativeElement.innerText = newText;
      }
    
      ngOnInit(): void {
        this.ngContentId = this.el.nativeElement.attributes[0].name;
    
        // I use the ngContentId assigned by angular, which does not change on
        // navigation, that the animation only runs once
        if (this.ngContentId && !this.appService.getAnimationState(this.ngContentId)) {
          this.animateTo = this.innerText;
          this.innerText = this.innerText.split('').map(() => ' ').join('');
          this.animate(this.animateTo.split(''));
          this.appService.setAnimationState(this.ngContentId, true);
        }
      }
    
      animate(animateTo: string[]): Promise<void> {
      // ...
      }
    
    }