WU Upgrade Guide

NG2.0 - TS2.0

Changes

  • Angular 2.0 stable
  • Typescript 2.0 stable
  • Providers strategy
  • Unit tests strategy

Angular

@NgModule

  • Should be in index.ts
  • We'll have one NgModule per base directory.
  • All declarables from that folder should be in that module.
  • Export only what's necessary for others.
  • Import everything you need, even CommonModule
// lib imports
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
// external imports
import {WuTopCitiesModule} from 'shared/wu-top-cities';
import {WuDisplayUnitModule} from 'shared/wu-display-unit';
import {LazyLoadComponent} from 'helpers/lazy-load-component';
// internal imports
import {WuFavorites} from './wu-favorites';
import {WuFavoritesBar} from './wu-favorites-bar';
import {WuFavoritesMore} from './wu-favorites-more';
import {WuFavoritesShowMore} from './wu-favorites-show-more';
// exports
export * from './globals';

@NgModule({
  imports: [
    CommonModule,
    WuTopCitiesModule,
    WuDisplayUnitModule
  ],
  providers: [
    {provide: LazyLoadComponent, useValue: WuFavorites}
  ],
  declarations: [
    WuFavorites,
    WuFavoritesBar,
    WuFavoritesMore,
    WuFavoritesShowMore
  ],
  entryComponents: [
    WuFavorites
  ]
})
export class WuFavoritesModule {}

@NgModule Fields

  • imports
    • All the external modules that our code makes reference to
  • providers
    • ​All the custom providers for our module. Providers in here will create a new instance, so we'll only provide local/internal services in here
  • declarations
    • All the components, directives and pipes defined by our module

@NgModule Fields

  • exports
    • All the declarations we want to share with other modules
  • entryComponents
    • Components that might be an entry point of the app
  • bootstrap(don't use)
    • Components we want angular bootstrap for us. We have another strategy for this

Index Structure

header comment

 

 

 

lib imports

external imports

internal imports

exports & re-exports

 

ngModule

/**
 * @module WuFavorites
 * @preferred
 * @author Diego Barahona
 * @since 4/12/16
 */ /** */

// lib imports
import {NgModule} from '@angular/core';
// external imports
import {..} from '...';
// internal imports
import {...} from './...';
// exports
export * from './globals';

@NgModule({...})
export class WuFavoritesModule {}

Lazy Load

We have to use a custom setup for lazy load components into the page. This is why we don't use bootstrap.

import {LazyLoadComponent} from 'helpers/lazy-load-component';
...
@NgModule({
  ...
  providers: [
    {provide: LazyLoadComponent, useValue: WuFavorites}
  ],
  ...
})
export class WuFavoritesModule {}

@Component

Components no longer declare have the directives or pipes fields, and we use @NgModule for the providers.

// lib imports
import {Component} from '@angular/core';

@Component({
  moduleId: module.id,
  selector: 'wu-favorites',
  templateUrl: './wu-favorites.html'
})
export class WuFavorites {...}

Prepare for AoT

Whit AoT compile and server side rendering we need some rules:

  • Make public any property/method used in a template
  • Ng hook methods need to be declared with all the necesary parameters
export class WuFavorites implements OnChanges {
  // All needed variables for the template
  public favorites: any;
  public popular: any;
  public recents: any;
  public cities: any;

  public ngOnChanges(changes: SimpleChanges): void {...}
}

Typescript

Changes

  • Path aliases
  • Strict Null Check

Path Aliases

We have now aliases to avoid long & ugly imports.

  • "components/*" => "src/components/*/index"
  • "components/*/globals" => "src/components/*/globals"
  • "shared/*" => "src/shared/*/index"
  • "shared/*/globals" => "src/shared/*/globals"
  • "helpers/*" => "src/helpers/*/index"
  • "testing/*" => "src/testing/*/index"
  • "testing" => "src/testing/index"

Path Aliases

// src/herlpers/globals/index.ts
import {mergeGlobals} from 'helpers/globals';
// src/testing/index.ts
import {TestComponentBuilderFactory} from 'testing';
// src/shared/wu-top-cities/index.ts
import {WuTopCitiesModule} from 'shared/wu-top-cities';
// src/shared/wu-top-cities/globals.ts
import {WuTopCitiesGlobals} from 'shared/wu-top-cities/globals';

Strict Null Check

It's better to read about this in the documentation.

// WRONG

// error, string is not ensured to be returned
// actual type is string|undefined
function Test(value: boolean): string {
  if (value) {
    return 'test';
  }
}

// error, value is not ensured to exist
// actual type is string|undefined
function Test2(value?: string): string {
  return value.replace('-', '');
}

// CORRECT

function Test(value: boolean): string|undefined {
  if (value) {
    return 'test';
  }
}

function Test2(value?: string): string {
  if (value) {
    // now value is type string
    return value.replace('-', '');
  } else {
    return '';
  }
}

Globals

(Providers)

Globals File

We're not using the old export const at the end of the file. Instead we're using a proper file for it.

 

We call it globals because now we're not only dealing with providers, but with modules as well.

/**
 * @module WuCache
 */ /** */

// lib imports
import {JsonpModule} from '@angular/http';
// external imports
import {mergeGlobals} from 'helpers/globals';
import {WuQueryGlobals} from 'shared/wu-query/globals';
import {WuCacheGlobals} from 'shared/wu-cache/globals';
// internal imports
import {WuApiService} from './wu-api.service';
import {WuUrlGenerator} from './wu-url-generator';

export const WuApiGlobals = mergeGlobals({
  providers: [
    WuApiService,
    WuUrlGenerator
  ],
  imports: [
    JsonpModule
  ]
}, [
  WuCacheGlobals,
  WuQueryGlobals
]);
/**
 * @module WuAds
 */ /** */

// external imports
import {mergeGlobals} from 'helpers/globals';
// internal imports
import {WuAdsService} from './wu-ads';

export const WuAdsGlobals = mergeGlobals({
  providers: [
    WuAdsService
  ]
});
/**
 * @module WuSearchAutocomplete
 * @author Diego Barahona
 * @since 2/9/16
 */ /** */

// external imports
import {mergeGlobals} from 'helpers/globals';
import {WuApiGlobals} from 'shared/wu-api/globals';
import {WuQueryGlobals} from 'shared/wu-query/globals';
import {WuTopCitiesGlobals} from 'shared/wu-top-cities/globals';
import {WuGeolocationGlobals} from 'shared/wu-geolocation/globals';
import {WuUserProfileGlobals} from 'shared/wu-user-profile/globals';

export const WuSearchGlobals = mergeGlobals({}, [
  WuApiGlobals,
  WuQueryGlobals,
  WuTopCitiesGlobals,
  WuGeolocationGlobals,
  WuUserProfileGlobals
]);

Unit-Tests

It actually works with the same TestComponentBuilder but with some modifications.

 

Options now accept a preCompileHook, which is a function to be called before compile all the modules.

Also a globals and modules array to be provided with what you need.

@Component({
  moduleId: module.id,
  templateUrl: './template.html'
})
class TestComponent {
  @ViewChild(WuBlogFeatured)
  public wuBlogFeatured: WuBlogFeatured;

  public blog = Jeffblog;
}

@NgModule({
  imports: [WuNewsModule],
  declarations: [TestComponent]
})
class TestModule {}

const myOptions = new TestComponentOptions();
myOptions.modules = [TestModule];
myOptions.globals = [WuNewsGlobals];

myOptions.preCompileHook = () => {
  TestBed.overrideModule(WuNewsModule, {
    add: {exports: [WuBlogFeatured]}
  });
};

deck

By Diego Barahona