183 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @license
 | 
						|
 * Copyright Google LLC All Rights Reserved.
 | 
						|
 *
 | 
						|
 * Use of this source code is governed by an MIT-style license that can be
 | 
						|
 * found in the LICENSE file at https://angular.io/license
 | 
						|
 */
 | 
						|
// #docplaster
 | 
						|
import {Component, Directive, ElementRef, EventEmitter, Injectable, Injector, Input, NgModule, Output} from '@angular/core';
 | 
						|
import {BrowserModule} from '@angular/platform-browser';
 | 
						|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
 | 
						|
import {downgradeComponent, downgradeInjectable, UpgradeComponent, UpgradeModule} from '@angular/upgrade/static';
 | 
						|
 | 
						|
declare var angular: ng.IAngularStatic;
 | 
						|
 | 
						|
export interface Hero {
 | 
						|
  name: string;
 | 
						|
  description: string;
 | 
						|
}
 | 
						|
 | 
						|
// #docregion ng1-text-formatter-service
 | 
						|
export class TextFormatter {
 | 
						|
  titleCase(value: string) {
 | 
						|
    return value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// #enddocregion
 | 
						|
// #docregion ng2-heroes
 | 
						|
// This Angular component will be "downgraded" to be used in AngularJS
 | 
						|
@Component({
 | 
						|
  selector: 'ng2-heroes',
 | 
						|
  // This template uses the upgraded `ng1-hero` component
 | 
						|
  // Note that because its element is compiled by Angular we must use camelCased attribute names
 | 
						|
  template: `<header><ng-content selector="h1"></ng-content></header>
 | 
						|
             <ng-content selector=".extra"></ng-content>
 | 
						|
             <div *ngFor="let hero of heroes">
 | 
						|
               <ng1-hero [hero]="hero" (onRemove)="removeHero.emit(hero)"><strong>Super Hero</strong></ng1-hero>
 | 
						|
             </div>
 | 
						|
             <button (click)="addHero.emit()">Add Hero</button>`,
 | 
						|
})
 | 
						|
export class Ng2HeroesComponent {
 | 
						|
  @Input() heroes!: Hero[];
 | 
						|
  @Output() addHero = new EventEmitter();
 | 
						|
  @Output() removeHero = new EventEmitter();
 | 
						|
}
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
// #docregion ng2-heroes-service
 | 
						|
// This Angular service will be "downgraded" to be used in AngularJS
 | 
						|
@Injectable()
 | 
						|
export class HeroesService {
 | 
						|
  heroes: Hero[] = [
 | 
						|
    {name: 'superman', description: 'The man of steel'},
 | 
						|
    {name: 'wonder woman', description: 'Princess of the Amazons'},
 | 
						|
    {name: 'thor', description: 'The hammer-wielding god'}
 | 
						|
  ];
 | 
						|
 | 
						|
  // #docregion use-ng1-upgraded-service
 | 
						|
  constructor(textFormatter: TextFormatter) {
 | 
						|
    // Change all the hero names to title case, using the "upgraded" AngularJS service
 | 
						|
    this.heroes.forEach((hero: Hero) => hero.name = textFormatter.titleCase(hero.name));
 | 
						|
  }
 | 
						|
  // #enddocregion
 | 
						|
 | 
						|
  addHero() {
 | 
						|
    this.heroes =
 | 
						|
        this.heroes.concat([{name: 'Kamala Khan', description: 'Epic shape-shifting healer'}]);
 | 
						|
  }
 | 
						|
 | 
						|
  removeHero(hero: Hero) {
 | 
						|
    this.heroes = this.heroes.filter((item: Hero) => item !== hero);
 | 
						|
  }
 | 
						|
}
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
// #docregion ng1-hero-wrapper
 | 
						|
// This Angular directive will act as an interface to the "upgraded" AngularJS component
 | 
						|
@Directive({selector: 'ng1-hero'})
 | 
						|
export class Ng1HeroComponentWrapper extends UpgradeComponent {
 | 
						|
  // The names of the input and output properties here must match the names of the
 | 
						|
  // `<` and `&` bindings in the AngularJS component that is being wrapped
 | 
						|
  @Input() hero!: Hero;
 | 
						|
  @Output() onRemove!: EventEmitter<void>;
 | 
						|
 | 
						|
  constructor(elementRef: ElementRef, injector: Injector) {
 | 
						|
    // We must pass the name of the directive as used by AngularJS to the super
 | 
						|
    super('ng1Hero', elementRef, injector);
 | 
						|
  }
 | 
						|
}
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
// #docregion ng2-module
 | 
						|
// This NgModule represents the Angular pieces of the application
 | 
						|
@NgModule({
 | 
						|
  declarations: [Ng2HeroesComponent, Ng1HeroComponentWrapper],
 | 
						|
  providers: [
 | 
						|
    HeroesService,
 | 
						|
    // #docregion upgrade-ng1-service
 | 
						|
    // Register an Angular provider whose value is the "upgraded" AngularJS service
 | 
						|
    {provide: TextFormatter, useFactory: (i: any) => i.get('textFormatter'), deps: ['$injector']}
 | 
						|
    // #enddocregion
 | 
						|
  ],
 | 
						|
  // All components that are to be "downgraded" must be declared as `entryComponents`
 | 
						|
  entryComponents: [Ng2HeroesComponent],
 | 
						|
  // We must import `UpgradeModule` to get access to the AngularJS core services
 | 
						|
  imports: [BrowserModule, UpgradeModule]
 | 
						|
})
 | 
						|
// #docregion bootstrap-ng1
 | 
						|
export class Ng2AppModule {
 | 
						|
  // #enddocregion ng2-module
 | 
						|
  constructor(private upgrade: UpgradeModule) {}
 | 
						|
 | 
						|
  ngDoBootstrap() {
 | 
						|
    // We bootstrap the AngularJS app.
 | 
						|
    this.upgrade.bootstrap(document.body, [ng1AppModule.name]);
 | 
						|
  }
 | 
						|
  // #docregion ng2-module
 | 
						|
}
 | 
						|
// #enddocregion bootstrap-ng1
 | 
						|
// #enddocregion ng2-module
 | 
						|
 | 
						|
 | 
						|
// This Angular 1 module represents the AngularJS pieces of the application
 | 
						|
export const ng1AppModule: ng.IModule = angular.module('ng1AppModule', []);
 | 
						|
 | 
						|
// #docregion ng1-hero
 | 
						|
// This AngularJS component will be "upgraded" to be used in Angular
 | 
						|
ng1AppModule.component('ng1Hero', {
 | 
						|
  bindings: {hero: '<', onRemove: '&'},
 | 
						|
  transclude: true,
 | 
						|
  template: `<div class="title" ng-transclude></div>
 | 
						|
             <h2>{{ $ctrl.hero.name }}</h2>
 | 
						|
             <p>{{ $ctrl.hero.description }}</p>
 | 
						|
             <button ng-click="$ctrl.onRemove()">Remove</button>`
 | 
						|
});
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
// #docregion ng1-text-formatter-service
 | 
						|
// This AngularJS service will be "upgraded" to be used in Angular
 | 
						|
ng1AppModule.service('textFormatter', [TextFormatter]);
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
// #docregion downgrade-ng2-heroes-service
 | 
						|
// Register an AngularJS service, whose value is the "downgraded" Angular injectable.
 | 
						|
ng1AppModule.factory('heroesService', downgradeInjectable(HeroesService) as any);
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
// #docregion ng2-heroes-wrapper
 | 
						|
// This directive will act as the interface to the "downgraded" Angular component
 | 
						|
ng1AppModule.directive('ng2Heroes', downgradeComponent({component: Ng2HeroesComponent}));
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
// #docregion example-app
 | 
						|
// This is our top level application component
 | 
						|
ng1AppModule.component('exampleApp', {
 | 
						|
  // We inject the "downgraded" HeroesService into this AngularJS component
 | 
						|
  // (We don't need the `HeroesService` type for AngularJS DI - it just helps with TypeScript
 | 
						|
  // compilation)
 | 
						|
  controller: [
 | 
						|
    'heroesService',
 | 
						|
    function(heroesService: HeroesService) {
 | 
						|
      this.heroesService = heroesService;
 | 
						|
    }
 | 
						|
  ],
 | 
						|
  // This template makes use of the downgraded `ng2-heroes` component
 | 
						|
  // Note that because its element is compiled by AngularJS we must use kebab-case attributes
 | 
						|
  // for inputs and outputs
 | 
						|
  template: `<link rel="stylesheet" href="./styles.css">
 | 
						|
          <ng2-heroes [heroes]="$ctrl.heroesService.heroes" (add-hero)="$ctrl.heroesService.addHero()" (remove-hero)="$ctrl.heroesService.removeHero($event)">
 | 
						|
            <h1>Heroes</h1>
 | 
						|
            <p class="extra">There are {{ $ctrl.heroesService.heroes.length }} heroes.</p>
 | 
						|
          </ng2-heroes>`
 | 
						|
});
 | 
						|
// #enddocregion
 | 
						|
 | 
						|
 | 
						|
// #docregion bootstrap-ng2
 | 
						|
// We bootstrap the Angular module as we would do in a normal Angular app.
 | 
						|
// (We are using the dynamic browser platform as this example has not been compiled AOT.)
 | 
						|
platformBrowserDynamic().bootstrapModule(Ng2AppModule);
 | 
						|
// #enddocregion
 |