Angular Upgrade Workshop – By @thoughtram



Angular Upgrade Workshop – By @thoughtram

2 3


angular-upgrade-slides

Slides for ngconf fair day workshop on upgrading apps to Angular 2

On Github thoughtram / angular-upgrade-slides

Angular Upgrade Workshop

By @thoughtram

About us

Christoph Burgdorf

@cburgdorf

Pascal Precht

@PascalPrecht

Todays plans

Demo

We will focus on upgrading an existing (legacy) app by example.

Upgrading

Preparing the upgrade

There some things we can do today to make upgrading our application easier:

  • Layer application by feature/component, use mutiple modules
  • Use .service() instead of .factory()
  • Write new components in ES2015 or TypeScript

Upgrade Strategies

Upgrade Strategies

There are two different ways of how to upgrade an existing app to Angular 2:

  • Big Bang™ - Start a spike in Angular 2 and replace entire app once done
  • Incremental - Upgrade existing app one service or component at a time

Big Bang™

Bang!

Incremental Upgrade

Upgrading using ngUpgrade

Angular 1 + 2 together

For this to work, four things need to interoperate between Angular 1 and Angular 2:

  • Dependency Injection
  • Component Nesting + Transcusion
  • Change Detection

Typical upgrade process

Include Angular 2 and upgrade module Pick component to upgrade and change its template/controller Export (Angular 2) component in Angular 1 app Pick service to upgrade Repeat step 2 and 3 Replace Angular 1 bootstrap with Angular 2 bootstrap

Bootstrapping

import {UpgradeAdapter} from '@angular/upgrade';
export const upgradeAdapter = new UpgradeAdapter();
import {UpgradeAdapter} from '@angular/upgrade';
export const upgradeAdapter = new UpgradeAdapter();
import {upgradeAdapter} from 'upgrade-adapter';

angular.module('contacts-app', [...]);

upgradeAdapter
  .bootstrap(document.body, ['contacts-app']);
import {upgradeAdapter} from 'upgrade-adapter';

angular.module('contacts-app', [...]);

upgradeAdapter
  .bootstrap(document.body, ['contacts-app']);

Demo: Bootstrapping

$ git checkout step-1

Downgrading A2 Components

@Component({
  selector: 'contacts-list-item-component',
  template: `
    <a href="#/contact/{{contact.id}}">
      <img [src]="contact.image">
      <span>{{contact.name}}</span>
    </a>
  `
})
export class ContactsListItemComponent {
  @Input() contact: Contact;
}
import {upgradeAdapter} from 'upgrade-adapter';
import {ContactsListItemComponent} from '...';

angular.module('contacts-list-item-component', [])

.directive('contactsListItemComponent',
  upgradeAdapter
    .downgradeNg2Component(ContactsListItemComponent)
);
import {upgradeAdapter} from 'upgrade-adapter';
import {ContactsListItemComponent} from '...';

angular.module('contacts-list-item-component', [])

.directive('contactsListItemComponent',
  upgradeAdapter
    .downgradeNg2Component(ContactsListItemComponent)
);
import {upgradeAdapter} from 'upgrade-adapter';
import {ContactsListItemComponent} from '...';

angular.module('contacts-list-item-component', [])

.directive('contactsListItemComponent',
  upgradeAdapter
    .downgradeNg2Component(ContactsListItemComponent)
);
angular.module('contacts-app', [
  ...
  'contacts-list-item-component'
]);
<ul>
  <li ng-repeat="contact in contacts">

    <contact-list-item-component [contact]="contact">
    </contact-list-item-component>

  </li>
</ul>
<ul>
  <li ng-repeat="contact in contacts">

    <contact-list-item-component [contact]="contact">
    </contact-list-item-component>

  </li>
</ul>

Demo: Downgrading A2 Component

$ git checkout step-2

...

$ git checkout step-3

Upgrading A1 Components

angular.module('zippy-component', [])

.component('zippyComponent', {
  templateUrl: '...',
  transclude: true,
  bindings: {
    closed: '<',
    title: '@',
    toggle: '&'
  },
  controller: function () { ... }
})
          
          
          
          
@Component({
  selector: 'contact-detail-component',
  templateUrl: '...',

})
export class ContactDetailComponent {
  ...
}
import {upgradeAdapter} from 'upgrade-adapter';

const ZippyComponent = 
  upgradeAdapter.upgradeNg1Component('zippyComponent');

@Component({
  selector: 'contact-detail-component',
  templateUrl: '...',
  directives: [ZippyComponent]
})
export class ContactDetailComponent {
  ...
}
import {upgradeAdapter} from 'upgrade-adapter';

const ZippyComponent = 
  upgradeAdapter.upgradeNg1Component('zippyComponent');

@Component({
  selector: 'contact-detail-component',
  templateUrl: '...',
  directives: [ZippyComponent]
})
export class ContactDetailComponent {
  ...
}
<zippy-component
  [title]="zippyCaption"
  (toggle)="toggleCaption($event.closed)">
</zippy-component>
<zippy-component
  [title]="zippyCaption"
  (toggle)="toggleCaption($event.closed)">
</zippy-component>

Demo: Upgrading A1 Component

$ git checkout step-4

Upgrading A1 Providers



@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor() {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}


@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor() {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}
upgradeAdapter.upgradeNg1Provider('contactsService');
upgradeAdapter.upgradeNg1Provider('$routeParams');

@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor(
    @Inject('contactsService') contactsService,
    @Inject('$routeParams') $routeParams) {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}
upgradeAdapter.upgradeNg1Provider('contactsService');
upgradeAdapter.upgradeNg1Provider('$routeParams');

@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor(
    @Inject('contactsService') contactsService,
    @Inject('$routeParams') $routeParams) {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}

Demo: Upgrading A1 Providers

$ git checkout step-5

Downgrading A2 Providers

import {Injectable} from '@angular/core';
          
@Injectable()
class ContactsService {

  private CONTACT_DATA = [...];

  getContacts() {
    return this.CONTACT_DATA;
  }

  getContact(id: string) {
    return this.CONTACT_DATA
      .find(contact => contact.id === id);
  }
}
import {upgradeAdapter} from 'upgrade-adapter';

angular.module('contacts-service', [])

.service('contactsService',
  upgradeAdapter.downgradeNg2Provider(ContactsService)
);
import {upgradeAdapter} from 'upgrade-adapter';

angular.module('contacts-service', [])

.service('contactsService',
  upgradeAdapter.downgradeNg2Provider(ContactsService)
);

How do we inject ContactsService in A2 components now?


@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor(
    @Inject('contactsService') contactsService,
    @Inject('$routeParams') $routeParams) {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}

@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor(
    @Inject('contactsService') contactsService
    @Inject('$routeParams') $routeParams) {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}
upgradeAdapter.addProvider(ContactsService);
          
@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor(
    contactsService: ContactsService,
    @Inject('$routeParams') $routeParams) {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}
upgradeAdapter.addProvider(ContactsService);
          
@Component()
export class ContactDetailComponent {

  contact: Contact;

  constructor(
    contactsService: ContactsService,
    @Inject('$routeParams') $routeParams) {
    this.contact = 
      contactsService.getContact($routeParams.id);
  }
}

Demo: Downgrading A2 Providers

$ git checkout step-6

Angular Upgrade Workshop By @thoughtram