| 
									
										
										
										
											2017-08-02 17:03:17 +03:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2017-08-02 17:03:17 +03:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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, Inject, Injectable, Injector, Input, NgModule, Output, StaticProvider} from '@angular/core'; | 
					
						
							|  |  |  | import {BrowserModule} from '@angular/platform-browser'; | 
					
						
							|  |  |  | // #docregion basic-how-to
 | 
					
						
							|  |  |  | // Alternatively, we could import and use an `NgModuleFactory` instead:
 | 
					
						
							|  |  |  | // import {MyLazyAngularModuleNgFactory} from './my-lazy-angular-module.ngfactory';
 | 
					
						
							|  |  |  | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; | 
					
						
							|  |  |  | // #enddocregion
 | 
					
						
							|  |  |  | /* tslint:disable: no-duplicate-imports */ | 
					
						
							|  |  |  | import {UpgradeComponent} from '@angular/upgrade/static'; | 
					
						
							|  |  |  | import {downgradeComponent} from '@angular/upgrade/static'; | 
					
						
							|  |  |  | // #docregion basic-how-to
 | 
					
						
							|  |  |  | import {downgradeModule} from '@angular/upgrade/static'; | 
					
						
							|  |  |  | // #enddocregion
 | 
					
						
							|  |  |  | /* tslint:enable: no-duplicate-imports */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | declare var angular: ng.IAngularStatic; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | interface Hero { | 
					
						
							|  |  |  |   name: string; | 
					
						
							|  |  |  |   description: string; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This Angular service will use an "upgraded" AngularJS service.
 | 
					
						
							|  |  |  | @Injectable() | 
					
						
							|  |  |  | 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'} | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor(@Inject('titleCase') titleCase: (v: string) => string) { | 
					
						
							|  |  |  |     // Change all the hero names to title case, using the "upgraded" AngularJS service.
 | 
					
						
							|  |  |  |     this.heroes.forEach((hero: Hero) => hero.name = titleCase(hero.name)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   addHero() { | 
					
						
							|  |  |  |     const newHero: Hero = {name: 'Kamala Khan', description: 'Epic shape-shifting healer'}; | 
					
						
							|  |  |  |     this.heroes = this.heroes.concat([newHero]); | 
					
						
							|  |  |  |     return newHero; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |   removeHero(hero: Hero) { | 
					
						
							|  |  |  |     this.heroes = this.heroes.filter((item: Hero) => item !== hero); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-08-02 17:03:17 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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: `
 | 
					
						
							|  |  |  |     <div class="ng2-heroes"> | 
					
						
							|  |  |  |       <header><ng-content selector="h1"></ng-content></header> | 
					
						
							|  |  |  |       <ng-content selector=".extra"></ng-content> | 
					
						
							|  |  |  |       <div *ngFor="let hero of this.heroesService.heroes"> | 
					
						
							|  |  |  |         <ng1-hero [hero]="hero" (onRemove)="onRemoveHero(hero)"> | 
					
						
							|  |  |  |           <strong>Super Hero</strong> | 
					
						
							|  |  |  |         </ng1-hero> | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |       <button (click)="onAddHero()">Add Hero</button> | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  |   `,
 | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class Ng2HeroesComponent { | 
					
						
							|  |  |  |   @Output() private addHero = new EventEmitter<Hero>(); | 
					
						
							|  |  |  |   @Output() private removeHero = new EventEmitter<Hero>(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor( | 
					
						
							|  |  |  |       @Inject('$rootScope') private $rootScope: ng.IRootScopeService, | 
					
						
							|  |  |  |       public heroesService: HeroesService) {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   onAddHero() { | 
					
						
							|  |  |  |     const newHero = this.heroesService.addHero(); | 
					
						
							|  |  |  |     this.addHero.emit(newHero); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // When a new instance of an "upgraded" component - such as `ng1Hero` - is created, we want to
 | 
					
						
							|  |  |  |     // run a `$digest` to initialize its bindings. Here, the component will be created by `ngFor`
 | 
					
						
							|  |  |  |     // asynchronously, thus we have to schedule the `$digest` to also happen asynchronously.
 | 
					
						
							|  |  |  |     this.$rootScope.$applyAsync(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   onRemoveHero(hero: Hero) { | 
					
						
							|  |  |  |     this.heroesService.removeHero(hero); | 
					
						
							|  |  |  |     this.removeHero.emit(hero); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This Angular directive will act as an interface to the "upgraded" AngularJS component.
 | 
					
						
							|  |  |  | @Directive({selector: 'ng1-hero'}) | 
					
						
							|  |  |  | 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.
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |   @Input() hero!: Hero; | 
					
						
							|  |  |  |   @Output() onRemove!: EventEmitter<void>; | 
					
						
							| 
									
										
										
										
											2017-08-02 17:03:17 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   constructor(elementRef: ElementRef, injector: Injector) { | 
					
						
							|  |  |  |     // We must pass the name of the directive as used by AngularJS to the super.
 | 
					
						
							|  |  |  |     super('ng1Hero', elementRef, injector); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This Angular module represents the Angular pieces of the application.
 | 
					
						
							|  |  |  | @NgModule({ | 
					
						
							|  |  |  |   imports: [BrowserModule], | 
					
						
							|  |  |  |   declarations: [Ng2HeroesComponent, Ng1HeroComponentWrapper], | 
					
						
							|  |  |  |   providers: [ | 
					
						
							|  |  |  |     HeroesService, | 
					
						
							|  |  |  |     // Register an Angular provider whose value is the "upgraded" AngularJS service.
 | 
					
						
							|  |  |  |     {provide: 'titleCase', useFactory: (i: any) => i.get('titleCase'), deps: ['$injector']} | 
					
						
							|  |  |  |   ], | 
					
						
							|  |  |  |   // All components that are to be "downgraded" must be declared as `entryComponents`.
 | 
					
						
							|  |  |  |   entryComponents: [Ng2HeroesComponent] | 
					
						
							|  |  |  |   // Note that there are no `bootstrap` components, since the "downgraded" component
 | 
					
						
							|  |  |  |   // will be instantiated by ngUpgrade.
 | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | class MyLazyAngularModule { | 
					
						
							|  |  |  |   // Empty placeholder method to prevent the `Compiler` from complaining.
 | 
					
						
							|  |  |  |   ngDoBootstrap() {} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // #docregion basic-how-to
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The function that will bootstrap the Angular module (when/if necessary).
 | 
					
						
							|  |  |  | // (This would be omitted if we provided an `NgModuleFactory` directly.)
 | 
					
						
							|  |  |  | const ng2BootstrapFn = (extraProviders: StaticProvider[]) => | 
					
						
							|  |  |  |     platformBrowserDynamic(extraProviders).bootstrapModule(MyLazyAngularModule); | 
					
						
							|  |  |  | // #enddocregion
 | 
					
						
							| 
									
										
										
										
											2020-02-02 20:49:28 -05:00
										 |  |  | // (We are using the dynamic browser platform, as this example has not been compiled AOT.)
 | 
					
						
							| 
									
										
										
										
											2017-08-02 17:03:17 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // #docregion basic-how-to
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This AngularJS module represents the AngularJS pieces of the application.
 | 
					
						
							|  |  |  | const myMainAngularJsModule = angular.module('myMainAngularJsModule', [ | 
					
						
							|  |  |  |   // We declare a dependency on the "downgraded" Angular module.
 | 
					
						
							|  |  |  |   downgradeModule(ng2BootstrapFn) | 
					
						
							|  |  |  |   // or
 | 
					
						
							|  |  |  |   // downgradeModule(MyLazyAngularModuleFactory)
 | 
					
						
							|  |  |  | ]); | 
					
						
							|  |  |  | // #enddocregion
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This AngularJS component will be "upgraded" to be used in Angular.
 | 
					
						
							|  |  |  | myMainAngularJsModule.component('ng1Hero', { | 
					
						
							|  |  |  |   bindings: {hero: '<', onRemove: '&'}, | 
					
						
							|  |  |  |   transclude: true, | 
					
						
							|  |  |  |   template: `
 | 
					
						
							|  |  |  |     <div class="ng1-hero"> | 
					
						
							|  |  |  |       <div class="title" ng-transclude></div> | 
					
						
							|  |  |  |       <h2>{{ $ctrl.hero.name }}</h2> | 
					
						
							|  |  |  |       <p>{{ $ctrl.hero.description }}</p> | 
					
						
							|  |  |  |       <button ng-click="$ctrl.onRemove()">Remove</button> | 
					
						
							|  |  |  |     </div> | 
					
						
							|  |  |  |   `
 | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This AngularJS service will be "upgraded" to be used in Angular.
 | 
					
						
							|  |  |  | myMainAngularJsModule.factory( | 
					
						
							|  |  |  |     'titleCase', () => (value: string) => value.replace(/(^|\s)[a-z]/g, m => m.toUpperCase())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This directive will act as the interface to the "downgraded" Angular component.
 | 
					
						
							|  |  |  | myMainAngularJsModule.directive( | 
					
						
							|  |  |  |     'ng2Heroes', downgradeComponent({ | 
					
						
							|  |  |  |       component: Ng2HeroesComponent, | 
					
						
							|  |  |  |       // Optionally, disable `$digest` propagation to avoid unnecessary change detection.
 | 
					
						
							|  |  |  |       // (Change detection is still run when the inputs of a "downgraded" component change.)
 | 
					
						
							|  |  |  |       propagateDigest: false | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is our top level application component.
 | 
					
						
							|  |  |  | myMainAngularJsModule.component('exampleApp', { | 
					
						
							|  |  |  |   // This template makes use of the "downgraded" `ng2-heroes` component,
 | 
					
						
							|  |  |  |   // but loads it lazily only when/if the user clicks the button.
 | 
					
						
							|  |  |  |   // (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"> | 
					
						
							|  |  |  |     <button ng-click="$ctrl.toggleHeroes()">{{ $ctrl.toggleBtnText() }}</button> | 
					
						
							|  |  |  |     <ng2-heroes | 
					
						
							|  |  |  |         ng-if="$ctrl.showHeroes" | 
					
						
							|  |  |  |         (add-hero)="$ctrl.setStatusMessage('Added hero ' + $event.name)" | 
					
						
							|  |  |  |         (remove-hero)="$ctrl.setStatusMessage('Removed hero ' + $event.name)"> | 
					
						
							|  |  |  |       <h1>Heroes</h1> | 
					
						
							|  |  |  |       <p class="extra">Status: {{ $ctrl.statusMessage }}</p> | 
					
						
							|  |  |  |     </ng2-heroes> | 
					
						
							|  |  |  |   `,
 | 
					
						
							|  |  |  |   controller: function() { | 
					
						
							|  |  |  |     this.showHeroes = false; | 
					
						
							|  |  |  |     this.statusMessage = 'Ready'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.setStatusMessage = (msg: string) => this.statusMessage = msg; | 
					
						
							|  |  |  |     this.toggleHeroes = () => this.showHeroes = !this.showHeroes; | 
					
						
							|  |  |  |     this.toggleBtnText = () => `${this.showHeroes ? 'Hide' : 'Show'} heroes`; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // We bootstrap the Angular module as we would do in a normal Angular app.
 | 
					
						
							|  |  |  | angular.bootstrap(document.body, [myMainAngularJsModule.name]); |