Skip to content

Animations modules (Browser and Noop) cause strange rendering #27917

@pauldraper

Description

@pauldraper

🐞 bug report

Affected Package

@angular/animation or @angular/platform-browser

Is this a regression?

No.

Description

The BrowserAnimationsModule and NoopAnimationsModule cause incomplete renders where there was none before.

🔬 Minimal Reproduction

https://stackblitz.com/edit/angular-after-view

When the button is toggled, the ngAfterViewChecked() shows that the DOM from both states are in the DOM simultaneously.

If you remove the NoopAnimationsModule altogether, there is only one state when ngAfterViewChecked() is run.

import { AfterViewChecked, ChangeDetectionStrategy, Component, ElementRef, Injectable, NgModule, VERSION } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { BehaviorSubject } from 'rxjs';

@Injectable()
export class LogService {
  private readonly _logs: string[] = [];

  readonly logs = new BehaviorSubject<string[]>(this.logs);

  clear() {
    this._logs.length = 0;
    this.logs.next(this._logs);
  }

  log(log: string) {
    console.log(log);
    this._logs.push(log);
    this.logs.next(this._logs);
  }
}

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'app',
  template: `
    <p class="block">Angular {{ version }}</p>
    <container>
      <button class="block" (click)="state = !state" type="button">Toggle content</button>
      <div *ngIf="state" class="block state">On</div>
      <div *ngIf="!state" class="block state">Off</div>
    </container>
    <log></log>
  `,
})
export class AppComponent {
  readonly version = VERSION.full;
  state = false;
}

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'container',
  template: `
    <ng-content></ng-content>
  `,
})
export class ContainerComponent implements AfterViewChecked {
  constructor(private readonly elementRef: ElementRef<HTMLElement>, private readonly logService: LogService) {}

  ngAfterViewChecked() {
    this.logService.log(`${this.elementRef.nativeElement.querySelectorAll('.state').length} states`);
  }
}

@Component({
  selector: 'log',
  styles: [`
    .log:not(:first-child) {
      margin-top: 2px;
    }
    .log:not(:last-child) {
      margin-bottom: 2px;
    }
  `],
  template: `
    <button class="block" (click)="logService.clear()" type="button">Clear logs</button>
    <div class="block log" *ngFor="let log of logService.logs | async">{{ log }}</div>
  `,
})
export class LogComponent {
  constructor(readonly logService: LogService) {}
}

@NgModule({
  declarations: [
    AppComponent,
    ContainerComponent,
    LogComponent,
  ],
  imports: [
    BrowserModule,
    NoopAnimationsModule,
  ],
  bootstrap: [AppComponent],
  providers: [
    LogService,
  ],
})
export class AppModule { }

🌍 Your Environment

Angular Version:

7.1.4

Anything else relevant?

Because of this bug, there is no way (that I'm aware of) to reliably measure the DOM after updating the view but before browser render.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions