/**
* @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 Angular Stuff
// #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: `
Super Hero
`,
})
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;
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
// #enddocregion
// #docregion Angular 1 Stuff
// #docregion ng1-module
// This Angular 1 module represents the AngularJS pieces of the application
export const ng1AppModule: ng.IModule = angular.module('ng1AppModule', []);
// #enddocregion
// #docregion ng1-hero
// This AngularJS component will be "upgraded" to be used in Angular
ng1AppModule.component('ng1Hero', {
bindings: {hero: '<', onRemove: '&'},
transclude: true,
template: `
{{ $ctrl.hero.name }}
{{ $ctrl.hero.description }}
`
});
// #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: `
Heroes
There are {{ $ctrl.heroesService.heroes.length }} heroes.
`
});
// #enddocregion
// #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