Angular: ChangeDetection Interferes with HostBinding

Published

I’m trying to implement an Animate On Scroll library/helper based on this idea Wizdm Genesys

I have a service that emits an IntersectionInfo (IsIntersecting, Direction, IsEntering, etc. ) object when an element intersects with the viewport.

I subscribe to it and if the element is entering the viewport, I trigger the enter animation.

It works fine unless I use ChangeDetection.OnPush on any of the components containing the element or any of it’s parents.
detectChanges() doesn’t fix it.

The weird thing is that variable is changeing and I can see it in the developer-tools but it is not triggering the animation.

Component using the animate on scroll library (HelloComponent.html):

<button mat-button color="primary" (click)="_replay = !_replay" >Primary</button>


<ng-container *ngFor="let item of items; let idx = index">

  <div class="item-container" myAos [animate]="_replay">
    <div class="item">
      {{item}}
    </div>
  </div>

</ng-container>

In AosComponnent I use selector: '[myAos]' so it can be used like a directive.

Here’s the AosComponent

@Component({
  selector: '[myAos]',
  template: `
    <!-- H5 is just for testing info -->
    <h5>{{ trigger | json }}</h5>
    <ng-content></ng-content>
  `,
  styleUrls: ['./aos.component.scss'],
  animations: [
    trigger('aosAnimate', [
      state('idle-bumpIn', style({ opacity: 0 })),

      transition(
        '* => bumpIn',
        [
          style({ transform: 'scale(0.5)', opacity: 0 }),

          animate(
            '{{timing}} {{delay}} cubic-bezier(.8, -0.6, 0.2, 1.5)',

            style({ transform: 'scale(1)', opacity: 1 })
          )
        ],
        { params: { timing: '500ms', delay: '' } }
      ),
      // None
      state('none', style('*')),
      state('idle-none', style('*'))
    ])
  ]
})
export class AosComponent implements OnInit {
  @Input('debounce') _debounce = 0;
  @Input('rootMargin') _rootMargin = '0px';
  @Input('root') _root: HTMLElement | undefined = undefined;
  @Input('threshold') _threshold: number = 0;
  @Input('delaySecs') _delay: number = 0.25;
  @Input('durationSecs') _duration: number = 0.5;
  private destroy$ = new Subject();

  @HostBinding('@aosAnimate')
  public trigger: any = 'none';


  @Input()
  set animate(trgr: boolean) {
    this.trigger = !trgr
      ? 'idle-bumpIn'
      : {
          value: 'bumpIn',
          params: {
            timing: `${this._duration}s`,
            delay: `${this._delay}s`
          }
        };
    this._cd.detectChanges();

    console.log('animate Input', trgr, this.trigger);
  }

  //-----------------------------------------------------//

  constructor(
    private _element: ElementRef,
    private renderer: Renderer2,
    private _scroll: IntersectionService,
    private _cd: ChangeDetectorRef
  ) {} //ctor

  //-----------------------------------------------------//

  ngOnInit(): void {
    // const me = this

    this._scroll
      .observeIntersection(
        this._element,
        this._threshold,
        this._root,
        this._rootMargin
      )
      .pipe(takeUntil(this.destroy$))
      .subscribe(info => {
        this.animate = info.isEntering;
        console.log('observeIntersection', info.isEntering);
        this._cd.detectChanges();
      });
  } //ngOnInit

  //-----------------------------------------------------//

  ngOnDestroy() {
    this.destroy$.next();
  } //ngOnDestroy

  //-----------------------------------------------------//
  
} //Cls

If I set @Input() animate to true from inside HelloComponent it always works.
If I set @Input() animate to true from subscribe it only works when I remove changeDetection: ChangeDetectionStrategy.OnPush from HelloComponent

HelloComponent.ts

@Component({
  selector: 'hello',
  templateUrl: './hello.component.html',
  styleUrls: ['./hello.component.scss']
  // changeDetection: ChangeDetectionStrategy.OnPush  <--- this is the problem
})
export class HelloComponent {
  _replay = false;
  items = ['item 1', 'item 2', 'item 3', 'item 4', 'item 5'];
}

StackBlitz here: My Stackblitz

Any ideas???

Source: Angular Questions

Answers

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Still Have Questions?


Our dedicated development team is here for you!

We can help you find answers to your question for as low as 5$.

Contact Us
faq