Grouping Transitions to run multiple animation steps in parallel in Angular

app.component.ts

import {Component} from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition, useAnimation, keyframes, group,
  // ...
} from '@angular/animations';
import {bounceIn} from 'ng-animate';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('preventAnimation', [
      transition('void => *', [])
    ]),
    trigger('list1', [state('in', style({
      opacity: 1,
      transform: 'translateX(0px)'
    })),
      transition('void => *', [
        animate(1000, keyframes([
          style({
            transform: 'translateX(-100px)',
            opacity: 0,
            offset: 0
          }),
          style({
            transform: 'translateX(-50px)',
            opacity: 0.5,
            offset: 0.2 // 0.3 * 1000 = 300
          }),
          style({
            transform: 'translateX(-20px)',
            opacity: 1,
            offset: 0.4
          }),
          style({
            transform: 'translateX(0px)',
            opacity: 1,
            offset: 1
          }),
        ]))
      ]),
      transition('* => void', [
        group([
          animate(300, style({
            backgroundColor: 'red'
          })),
          animate(800, style({
            opacity: 0,
            transform: 'translateX(100px)'
          }))
        ])
      ])
    ])
  ],
})
export class AppComponent {
  title = 'animations';
  list = ['Milk', 'Sugar', 'Bread'];

  onAdd(item) {
    this.list.push(item);
  }

  onDelete(item: string) {
    this.list.splice(this.list.indexOf(item), 1);
  }
}

References
https://angular.io/guide/complex-animation-sequences#parallel-animation-using-group-function

Using Keyframes for Animations in Angular

app.component.html

<div class="container">

  <div class="row">
    <div class="col-xs-12">
      <input type="text" #input>
      &nbsp;
      <button class="btn btn-primary" (click)="onAdd(input.value)">Add Item!</button>
      <hr>

      <!--
            prevent animation at page load
      -->
      <div [@preventAnimation]>
        <ul class="list-group">
          <!--[@list1] should not be bound to anything because the state is void-->
          <li
            class="list-group-item"
            (click)="onDelete(item)"
            [@list1]
            *ngFor="let item of list">
            {{ item }}
          </li>
        </ul>
      </div>

    </div>
  </div>
</div>

app.component.ts

import {Component} from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition, useAnimation, keyframes,
  // ...
} from '@angular/animations';
import {bounceIn} from 'ng-animate';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('preventAnimation', [
      transition('void => *', [])
    ]),
    trigger('list1', [state('in', style({
      opacity: 1,
      transform: 'translateX(0px)'
    })),
      transition('void => *', [
        animate(1000, keyframes([
          style({
            transform: 'translateX(-100px)',
            opacity: 0,
            offset: 0
          }),
          style({
            transform: 'translateX(-50px)',
            opacity: 0.5,
            offset: 0.2 // 0.3 * 1000 = 300
          }),
          style({
            transform: 'translateX(-20px)',
            opacity: 1,
            offset: 0.4
          }),
          style({
            transform: 'translateX(0px)',
            opacity: 1,
            offset: 1
          }),
        ]))
      ]),
      transition('* => void', [
        animate(300, style({
          opacity: 0,
          transform: 'translateX(100px)'
        }))
      ])
    ])
  ],
})
export class AppComponent {
  title = 'animations';
  list = ['Milk', 'Sugar', 'Bread'];

  onAdd(item) {
    this.list.push(item);
  }

  onDelete(item: string) {
    this.list.splice(this.list.indexOf(item), 1);
  }
}

References
https://angular.io/guide/transition-and-triggers#keyframes

Animating entering and leaving a view in Angular

app.component.html

<div class="container">

  <div class="row">
    <div class="col-xs-12">
      <input type="text" #input>
      &nbsp;
      <button class="btn btn-primary" (click)="onAdd(input.value)">Add Item!</button>
      <hr>

      <!--
            prevent animation at page load
      -->
      <div [@preventAnimation]>
        <ul class="list-group">
          <!--[@list1] should not be bound to anything because the state is void-->
          <li
            class="list-group-item"
            (click)="onDelete(item)"
            [@list1]
            *ngFor="let item of list">
            {{ item }}
          </li>
        </ul>
      </div>

    </div>
  </div>
</div>

app.component.ts

import {Component} from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition, useAnimation,
  // ...
} from '@angular/animations';
import {bounceIn} from 'ng-animate';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('preventAnimation', [
      // use an empty animation as parent of the list1 to prevent animation for it
      transition('void => *', [])
    ]),
    trigger('list1', [state('in', style({
      // this is the final style that the animation goes to it
      opacity: 1,
      transform: 'translateX(0px)'
    })),
      // void is for items not present in the DOM
      transition('void => *', [
        style({
          // this style will be applied before animation starts
          // so this would be the initial style of element
          opacity: 0,
          transform: 'translateX(-100px)'
        }),
        animate(300)]),
      // animation for removing item from DOM
      transition('* => void', [
        animate(300, style({
          opacity: 0,
          transform: 'translateX(100px)'
        }))
      ])
    ])
  ],
})
export class AppComponent {
  title = 'animations';
  list = ['Milk', 'Sugar', 'Bread'];

  onAdd(item) {
    this.list.push(item);
  }

  onDelete(item: string) {
    this.list.splice(this.list.indexOf(item), 1);
  }
}

References
https://angular.io/guide/transition-and-triggers#animating-entering-and-leaving-a-view

Using Animation Callbacks in Angular

app.component.html

<div style="margin-left: 10px;margin-top: 10px;">
  <button class="btn btn-primary" (click)="onAnimate()">Animate</button>
  <br><br>
  <div style="width: 100px;height: 100px;background-color: cornflowerblue"
       [@animation1]="state1"
       (@animation1.start)="onAnimationStarted($event)"
       (@animation1.done)="onAnimationDone($event)"
  ></div>
</div>

app.component.ts

import {Component} from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition, useAnimation,
  // ...
} from '@angular/animations';
import {bounceIn} from 'ng-animate';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('animation1', [transition('1 <=> 2', useAnimation(bounceIn, {
      params: {timing: 2, delay: 0.05}
    }))])
  ],
})
export class AppComponent {
  title = 'animations';
  state1 = 1;

  onAnimate() {
    // switch state
    if (this.state1 === 1) {
      this.state1 = 2;
    } else {
      this.state1 = 1;
    }
  }

  onAnimationStarted(event) {
    console.log(event);
  }

  onAnimationDone(event) {
    console.log(event);
  }
}

 

Use ng-animate Library in Angular for Animation

npm install ng-animate --save

app.component.html

<div style="margin-left: 10px;margin-top: 10px;">
  <button class="btn btn-primary" (click)="onAnimate()">Animate</button>
  <br><br>
  <div style="width: 100px;height: 100px;background-color: cornflowerblue"
       [@animation1]="state1"></div>
</div>

app.component.ts

import {Component} from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition, useAnimation,
  // ...
} from '@angular/animations';
import {bounceIn} from 'ng-animate';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('animation1', [transition('1 <=> 2', useAnimation(bounceIn, {
      params: {timing: 2, delay: 0.05}
    }))])
  ],
})
export class AppComponent {
  title = 'animations';
  state1 = 1;

  onAnimate() {
    // switch state
    if (this.state1 === 1) {
      this.state1 = 2;
    } else {
      this.state1 = 1;
    }
  }
}

References
https://github.com/jiayihu/ng-animate

Introduction to Angular animations

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<div style="margin-left: 10px;margin-top: 10px;">
  <button class="btn btn-primary" (click)="onAnimate()">Animate</button>
  <br><br>
  <div [@divState]="state" style="width: 100px;height: 100px;"></div>
</div>

app.component.ts

import {Component} from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition,
  // ...
} from '@angular/animations';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('divState', [
      state('normal', style({
        'background-color': 'red',
        transform: 'translateX(0px)'
      })),
      state('highlighted', style({
        'background-color': 'blue',
        transform: 'translateX(100px)',
      })),
      transition('normal => highlighted', [animate(300)]),
      transition('highlighted => normal', [animate(800)])
    ])
  ]
})
export class AppComponent {
  title = 'animations';
  state = 'normal';

  onAnimate() {
    // switch state
    if (this.state === 'normal') {
      this.state = 'highlighted';
    } else {
      this.state = 'normal';
    }
  }
}

References
https://angular.io/guide/animations

Modifying Http Responses with Interceptors in Angular

logging.interceptor.ts

import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {tap} from 'rxjs/operators';

export class LoggingInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      tap(event => {
        console.log('Logging interceptor', event);
      })
    );
  }
}

app.module.ts

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';

import {AppComponent} from './app.component';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {AuthInterceptor} from './auth.interceptor';
import {LoggingInterceptor} from './logging.interceptor';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [
    {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true},
    {provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

References
https://angular.io/guide/http#intercepting-requests-and-responses

Show progress with HttpClientModule in Angular

const request = new HttpRequest('GET', 'https://angularsamples.firebaseio.com/data.json',
  {reportProgress: true});

this.http.request(request).subscribe(value => {
  if (value.type === HttpEventType.DownloadProgress) {
    const newValue = value as HttpProgressEvent;
    const percent = Math.round(100 * newValue.loaded / newValue.total);
    console.log(percent);
  } else if (value.type === HttpEventType.Response) {
    console.log('File was completely downloaded!');
  }
});

 

References
https://angular.io/guide/http#listening-to-progress-events

Debouncing requests with HttpClientModule in Angular

withRefresh = false;
packages$: Observable<NpmPackageInfo[]>;
private searchText$ = new Subject<string>();

search(packageName: string) {
  this.searchText$.next(packageName);
}

ngOnInit() {
  this.packages$ = this.searchText$.pipe(
    debounceTime(500),
    distinctUntilChanged(),
    switchMap(packageName =>
      this.searchService.search(packageName, this.withRefresh))
  );
}

constructor(private searchService: PackageSearchService) { }

References
https://angular.io/guide/http#configuring-the-request

URL Parameters with HttpClientModule in Angular

/* GET heroes whose name contains search term */
searchHeroes(term: string): Observable<Hero[]> {
  term = term.trim();

  // Add safe, URL encoded search parameter if there is a search term
  const options = term ?
   { params: new HttpParams().set('name', term) } : {};

  return this.http.get<Hero[]>(this.heroesUrl, options)
    .pipe(
      catchError(this.handleError<Hero[]>('searchHeroes', []))
    );
}

References
https://angular.io/guide/http#url-parameters