NexusCS

Angular 6

JavaScript libraries
Angular 6 introduced ng update/add CLI commands, Angular Elements, RxJS 6, tree-shakable providers, and library support.
archived

Getting started

ng update

ng update
# Check for updates
ng update @angular/core @angular/cli
# Update Angular packages
ng update --all
# Update all dependencies
ng update --dry-run
# Preview changes only

ng add

ng add @angular/material
# Add Material Design
ng add @angular/pwa
# Add PWA support
ng add @angular/elements
# Add Web Components
ng add @ng-bootstrap/schematics
# Add third-party schematics

Tree-Shakable Providers

@Injectable({ providedIn: "root" })
export class DataService {
  constructor(private http: HttpClient) {}
}

Service registers itself automatically.

@NgModule({}) // No providers needed
export class AppModule {}

Unused services removed from bundle.

@Injectable({ providedIn: SomeModule })
export class ScopedService {}

Scope to specific module.

RxJS 6 Migration

Import Changes

// OLD (RxJS 5)
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/map";
import "rxjs/add/observable/of";
// NEW (RxJS 6)
import { Observable, of, from } from "rxjs";
import { map, filter, tap } from "rxjs/operators";

All imports from 'rxjs'.

Pipe Syntax

// OLD
source
  .map((x) => x + x)
  .filter((x) => x % 2 === 0)
  .catch((err) => of("error"))
  .subscribe();
// NEW
source
  .pipe(
    map((x) => x + x),
    filter((x) => x % 2 === 0),
    catchError((err) => of("error")),
  )
  .subscribe();

Use pipe() for operators.

Observable Creation

// OLD
Observable.of(1, 2, 3);
Observable.from([1, 2, 3]);
Observable.throw(new Error("fail"));
Observable.empty();
Observable.never();
// NEW
of(1, 2, 3);
from([1, 2, 3]);
throwError(new Error("fail"));
EMPTY;
NEVER;

Top-level creation functions.

Renamed Operators

Old (RxJS 5) New (RxJS 6)
do tap
catch catchError
switch switchAll
finally finalize
throw throwError

Error Handling

// OLD (REMOVED)
try {
  source$.subscribe(fn);
} catch (err) {
  handle(err);
}

Synchronous errors not caught.

// NEW
source$.subscribe({
  next: fn,
  error: handle,
  complete: completeFn,
});

Use subscribe error callback.

source$
  .pipe(
    catchError((err) => {
      console.error(err);
      return of(fallback);
    }),
  )
  .subscribe();

Use catchError operator.

Migration Tool

npm install -g rxjs-tslint
# Install migration tool
rxjs-5-to-6-migrate -p src/tsconfig.app.json
# Auto-migrate imports
npm install rxjs-compat
# Temporary compatibility layer

Angular Elements

Creating Custom Elements

import { createCustomElement } from "@angular/elements";
import { Injector, NgModule } from "@angular/core";

@NgModule({
  declarations: [PopupComponent],
  entryComponents: [PopupComponent],
})
export class AppModule {
  constructor(private injector: Injector) {
    const PopupElement = createCustomElement(PopupComponent, { injector });
    customElements.define("popup-element", PopupElement);
  }

  ngDoBootstrap() {}
}

Converts Angular components to Web Components.

Using Custom Elements

<!-- Use in non-Angular pages -->
<popup-element message="Hello!"></popup-element>
// Set properties
const popup = document.querySelector("popup-element");
popup.message = "Dynamic message";
// Listen to events
popup.addEventListener("closed", (e) => {
  console.log("Popup closed", e.detail);
});

Component for Elements

@Component({
  selector: "popup-element",
  template: `
    <div class="popup">
      <h2>{{ message }}</h2>
      <button (click)="close()">Close</button>
    </div>
  `,
})
export class PopupComponent {
  @Input() message: string;
  @Output() closed = new EventEmitter<any>();

  close() {
    this.closed.emit({ timestamp: Date.now() });
  }
}

Use @Input/@Output as usual.

CLI Workspaces

angular.json

{
  "version": 1,
  "projects": {
    "my-app": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/my-app"
          },
          "configurations": {
            "production": {
              "optimization": true
            }
          }
        }
      }
    }
  }
}

Replaces .angular-cli.json.

Multiple Projects

ng generate application second-app
# Add application
ng generate library my-lib
# Add library
ng build my-app
# Build specific project
ng serve second-app
# Serve specific app

Library Support

Generating Libraries

ng generate library my-lib

Creates projects/my-lib/ structure.

projects/my-lib/
  src/
    lib/
      my-lib.component.ts
      my-lib.module.ts
      my-lib.service.ts
    public-api.ts
  ng-package.json
  package.json
  tsconfig.lib.json

public-api.ts

/*
 * Public API Surface of my-lib
 */

export * from "./lib/my-lib.service";
export * from "./lib/my-lib.component";
export * from "./lib/my-lib.module";

Define library exports.

Building Libraries

ng build my-lib
# Build library
ng build my-lib --watch
# Watch mode
// Use in app
import { MyLibModule } from "my-lib";

@NgModule({
  imports: [MyLibModule],
})
export class AppModule {}

Publishing Libraries

cd dist/my-lib
npm publish
# Publish to npm
{
  "name": "@myscope/my-lib",
  "version": "1.0.0",
  "peerDependencies": {
    "@angular/core": "^6.0.0"
  }
}

Configure package.json.

Material Schematics

Navigation Schematic

ng generate @angular/material:material-nav --name=my-nav

Creates sidenav with toolbar.

import { LayoutModule } from "@angular/cdk/layout";
import { MatToolbarModule } from "@angular/material/toolbar";
import { MatSidenavModule } from "@angular/material/sidenav";

Imports CDK layout + Material.

Dashboard Schematic

ng generate @angular/material:material-dashboard --name=my-dashboard

Creates responsive dashboard cards.

import { MatGridListModule } from "@angular/material/grid-list";
import { MatCardModule } from "@angular/material/card";
import { MatMenuModule } from "@angular/material/menu";

Table Schematic

ng generate @angular/material:material-table --name=my-table

Creates data table with sorting/paging.

import { MatTableModule } from "@angular/material/table";
import { MatPaginatorModule } from "@angular/material/paginator";
import { MatSortModule } from "@angular/material/sort";

Common Operators (RxJS 6)

Transformation

map((x) => x * 2);

Transform emitted values.

switchMap((id) => this.http.get(`/user/${id}`));

Switch to new observable.

mergeMap((x) => doAsync(x));

Merge concurrent streams.

concatMap((x) => doAsync(x));

Sequential processing.

exhaustMap((x) => doAsync(x));

Ignore while pending.

Filtering

filter((x) => x > 10);

Emit if condition true.

take(5);

Emit first N values.

takeUntil(notifier$);

Complete when notified.

debounceTime(300);

Wait for pause.

distinctUntilChanged();

Skip duplicate consecutive values.

Combination

combineLatest([a$, b$, c$]);

Latest from each.

merge(a$, b$, c$);

Merge all emissions.

concat(a$, b$, c$);

Sequential concatenation.

forkJoin([a$, b$, c$]);

Wait for all completions.

withLatestFrom(other$);

Include latest from other.

Error Handling

catchError((err) => of(fallback));

Recover from errors.

retry(3);

Retry failed observable.

retryWhen((errors$) => errors$.pipe(delay(1000)));

Conditional retry logic.

Utility

tap((x) => console.log(x));

Side effects.

delay(1000);

Delay emissions.

finalize(() => cleanup());

Execute on completion/error.

share();

Share subscription.

startWith(initialValue);

Emit initial value first.

Gotchas

RxJS 6 Breaking Changes

⚠️ Import paths changed — all from 'rxjs'

⚠️ Operators renamed — do → tap, catch → catchError

⚠️ pipe() required — no more chaining

⚠️ Observable.of → of — creation functions top-level

⚠️ Synchronous error handling removed — no try/catch around subscribe

Configuration Changes

⚠️ angular.json replaces .angular-cli.json — migrate config

⚠️ providedIn: 'root' vs providers array — don't mix for same service

⚠️ rxjs-compat is temporary — remove after refactoring

Angular Elements

⚠️ entryComponents still needed — for custom elements in v6

⚠️ Polyfills required — for custom elements in older browsers

⚠️ Bundle size — includes Angular runtime

General

⚠️ Ivy renderer NOT in v6 — preview only, default in v9

⚠️ No ViewEncapsulation.Native — removed in v6.1

Also see