1384 lines
42 KiB
HTML

<html lang="en"><head></head><body>
<form id="mainForm" method="post" action="https://run.stackblitz.com/api/angular/v1?file=src/app/app.component.ts" target="_self"><input type="hidden" name="files[src/app/app-config.ts]" value="/*
Must put this interface in its own file instead of app.config.ts
or else TypeScript gives a (bogus) warning:
WARNING in ./src/app/... .ts
&quot;export 'AppConfig' was not found in './app.config'
*/
export interface AppConfig {
apiEndpoint: string;
title: string;
}
/*
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
*/"><input type="hidden" name="files[src/app/app.component.ts]" value="import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';
import { UserService } from './user.service';
@Component({
selector: 'app-root',
template: `
<h1>{{title}}</h1>
<app-car></app-car>
<app-injectors></app-injectors>
<app-tests></app-tests>
<h2>User</h2>
<p id=&quot;user&quot;>
{{userInfo}}
<button (click)=&quot;nextUser()&quot;>Next User</button>
<p>
<app-heroes id=&quot;authorized&quot; *ngIf=&quot;isAuthorized&quot;></app-heroes>
<app-heroes id=&quot;unauthorized&quot; *ngIf=&quot;!isAuthorized&quot;></app-heroes>
<app-heroes-tsp id=&quot;tspAuthorized&quot; *ngIf=&quot;isAuthorized&quot;></app-heroes-tsp>
<app-providers></app-providers>
`
})
export class AppComponent {
title: string;
constructor(
@Inject(APP_CONFIG) config: AppConfig,
private userService: UserService) {
this.title = config.title;
}
get isAuthorized() { return this.user.isAuthorized; }
nextUser() { this.userService.getNewUser(); }
get user() { return this.userService.user; }
get userInfo() {
return `Current user, ${this.user.name}, is ` +
`${this.isAuthorized ? '' : 'not'} authorized. `;
}
}
/*
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
*/"><input type="hidden" name="files[src/app/app.config.ts]" value="import { AppConfig } from './app-config';
export { AppConfig } from './app-config';
import { InjectionToken } from '@angular/core';
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
export const HERO_DI_CONFIG: AppConfig = {
apiEndpoint: 'api.heroes.com',
title: 'Dependency Injection'
};
/*
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
*/"><input type="hidden" name="files[src/app/app.module.ts]" value="import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
import { AppComponent } from './app.component';
import { CarComponent } from './car/car.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroesTspComponent } from './heroes/heroes-tsp.component';
import { HeroListComponent } from './heroes/hero-list.component';
import { InjectorComponent } from './injector.component';
import { Logger } from './logger.service';
import { TestComponent } from './test.component';
import { UserService } from './user.service';
import { ProvidersModule } from './providers.module';
@NgModule({
imports: [
BrowserModule,
ProvidersModule
],
declarations: [
AppComponent,
CarComponent,
HeroesComponent,
HeroesTspComponent,
HeroListComponent,
InjectorComponent,
TestComponent
],
providers: [
Logger,
UserService,
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],
exports: [ CarComponent, HeroesComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
/*
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
*/"><input type="hidden" name="files[src/app/car/car-creations.ts]" value="// Examples with car and engine variations
import { Car, Engine, Tires } from './car';
///////// example 1 ////////////
export function simpleCar() {
// Simple car with 4 cylinders and Flintstone tires.
const car = new Car(new Engine(), new Tires());
car.description = 'Simple';
return car;
}
///////// example 2 ////////////
class Engine2 {
constructor(public cylinders: number) { }
}
export function superCar() {
// Super car with 12 cylinders and Flintstone tires.
const bigCylinders = 12;
const car = new Car(new Engine2(bigCylinders), new Tires());
car.description = 'Super';
return car;
}
/////////// example 3 //////////
class MockEngine extends Engine { cylinders = 8; }
class MockTires extends Tires { make = 'YokoGoodStone'; }
export function testCar() {
// Test car with 8 cylinders and YokoGoodStone tires.
const car = new Car(new MockEngine(), new MockTires());
car.description = 'Test';
return car;
}
/*
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
*/"><input type="hidden" name="files[src/app/car/car-factory.ts]" value="import { Engine, Tires, Car } from './car';
// BAD pattern!
export class CarFactory {
createCar() {
const car = new Car(this.createEngine(), this.createTires());
car.description = 'Factory';
return car;
}
createEngine() {
return new Engine();
}
createTires() {
return new Tires();
}
}
/*
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
*/"><input type="hidden" name="files[src/app/car/car-injector.ts]" value="import { Injector } from '@angular/core';
import { Car, Engine, Tires } from './car';
import { Logger } from '../logger.service';
export function useInjector() {
let injector: Injector;
/*
// Cannot instantiate an Injector like this!
let injector = new Injector([
{ provide: Car, deps: [Engine, Tires] },
{ provide: Engine, deps: [] },
{ provide: Tires, deps: [] }
]);
*/
injector = Injector.create({
providers: [
{ provide: Car, deps: [Engine, Tires] },
{ provide: Engine, deps: [] },
{ provide: Tires, deps: [] }
]
});
const car = injector.get(Car);
car.description = 'Injector';
injector = Injector.create({
providers: [{ provide: Logger, deps: [] }]
});
const logger = injector.get(Logger);
logger.log('Injector car.drive() said: ' + car.drive());
return car;
}
/*
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
*/"><input type="hidden" name="files[src/app/car/car-no-di.ts]" value="// Car without DI
import { Engine, Tires } from './car';
export class Car {
public engine: Engine;
public tires: Tires;
public description = 'No DI';
constructor() {
this.engine = new Engine();
this.tires = new Tires();
}
// Method using the engine and tires
drive() {
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
}
}
/*
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
*/"><input type="hidden" name="files[src/app/car/car.component.ts]" value="import { Component } from '@angular/core';
import { Car, Engine, Tires } from './car';
import { Car as CarNoDi } from './car-no-di';
import { CarFactory } from './car-factory';
import { testCar,
simpleCar,
superCar } from './car-creations';
import { useInjector } from './car-injector';
@Component({
selector: 'app-car',
template: `
<h2>Cars</h2>
<div id=&quot;di&quot;>{{car.drive()}}</div>
<div id=&quot;nodi&quot;>{{noDiCar.drive()}}</div>
<div id=&quot;injector&quot;>{{injectorCar.drive()}}</div>
<div id=&quot;factory&quot;>{{factoryCar.drive()}}</div>
<div id=&quot;simple&quot;>{{simpleCar.drive()}}</div>
<div id=&quot;super&quot;>{{superCar.drive()}}</div>
<div id=&quot;test&quot;>{{testCar.drive()}}</div>
`,
providers: [Car, Engine, Tires]
})
export class CarComponent {
factoryCar = (new CarFactory()).createCar();
injectorCar = useInjector();
noDiCar = new CarNoDi();
simpleCar = simpleCar();
superCar = superCar();
testCar = testCar();
constructor(public car: Car) {}
}
/*
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
*/"><input type="hidden" name="files[src/app/car/car.ts]" value="import { Injectable } from '@angular/core';
export class Engine {
public cylinders = 4;
}
export class Tires {
public make = 'Flintstone';
public model = 'Square';
}
@Injectable()
export class Car {
public description = 'DI';
constructor(public engine: Engine, public tires: Tires) { }
// Method using the engine and tires
drive() {
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
}
}
/*
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
*/"><input type="hidden" name="files[src/app/heroes/hero-list.component.ts]" value="/* tslint:disable:one-line */
import { Component } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'app-hero-list',
template: `
<div *ngFor=&quot;let hero of heroes&quot;>
{{hero.id}} - {{hero.name}}
({{hero.isSecret ? 'secret' : 'public'}})
</div>
`,
})
export class HeroListComponent {
heroes: Hero[];
constructor(heroService: HeroService)
{
this.heroes = heroService.getHeroes();
}
}
/*
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
*/"><input type="hidden" name="files[src/app/heroes/hero.module.ts]" value="import { NgModule } from '@angular/core';
@NgModule({})
export class HeroModule {
}
/*
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
*/"><input type="hidden" name="files[src/app/heroes/hero.service.provider.ts]" value="/* tslint:disable:one-line */
import { HeroService } from './hero.service';
import { Logger } from '../logger.service';
import { UserService } from '../user.service';
const heroServiceFactory = (logger: Logger, userService: UserService) => {
return new HeroService(logger, userService.user.isAuthorized);
};
export let heroServiceProvider =
{ provide: HeroService,
useFactory: heroServiceFactory,
deps: [Logger, UserService]
};
/*
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
*/"><input type="hidden" name="files[src/app/heroes/hero.service.ts]" value="import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';
import { UserService } from '../user.service';
@Injectable({
providedIn: 'root',
useFactory: (logger: Logger, userService: UserService) =>
new HeroService(logger, userService.user.isAuthorized),
deps: [Logger, UserService],
})
export class HeroService {
constructor(
private logger: Logger,
private isAuthorized: boolean) { }
getHeroes() {
const auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
this.logger.log(`Getting heroes for ${auth} user.`);
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
}
}
/*
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
*/"><input type="hidden" name="files[src/app/heroes/hero.ts]" value="export interface Hero {
id: number;
name: string;
isSecret: boolean;
}
/*
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
*/"><input type="hidden" name="files[src/app/heroes/heroes-tsp.component.ts]" value="import { Component } from '@angular/core';
/**
* A version of `HeroesComponent` that does not provide the `HeroService` (and thus relies on its
* `Injectable`-declared provider) in order to function.
*
* TSP stands for Tree-Shakeable Provider.
*/
@Component({
selector: 'app-heroes-tsp',
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`
})
export class HeroesTspComponent { }
/*
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
*/"><input type="hidden" name="files[src/app/heroes/heroes.component.ts]" value="import { Component } from '@angular/core';
import { heroServiceProvider } from './hero.service.provider';
@Component({
selector: 'app-heroes',
providers: [ heroServiceProvider ],
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`
})
export class HeroesComponent { }
/*
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
*/"><input type="hidden" name="files[src/app/heroes/mock-heroes.ts]" value="import { Hero } from './hero';
export const HEROES: Hero[] = [
{ id: 11, isSecret: false, name: 'Dr Nice' },
{ id: 12, isSecret: false, name: 'Narco' },
{ id: 13, isSecret: false, name: 'Bombasto' },
{ id: 14, isSecret: false, name: 'Celeritas' },
{ id: 15, isSecret: false, name: 'Magneta' },
{ id: 16, isSecret: false, name: 'RubberMan' },
{ id: 17, isSecret: false, name: 'Dynama' },
{ id: 18, isSecret: true, name: 'Dr IQ' },
{ id: 19, isSecret: true, name: 'Magma' },
{ id: 20, isSecret: true, name: 'Tornado' }
];
/*
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
*/"><input type="hidden" name="files[src/app/injector.component.ts]" value="import { Component, Injector, OnInit } from '@angular/core';
import { Car, Engine, Tires } from './car/car';
import { Hero } from './heroes/hero';
import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service';
@Component({
selector: 'app-injectors',
template: `
<h2>Other Injections</h2>
<div id=&quot;car&quot;>{{car.drive()}}</div>
<div id=&quot;hero&quot;>{{hero.name}}</div>
<div id=&quot;rodent&quot;>{{rodent}}</div>
`,
providers: [Car, Engine, Tires, heroServiceProvider, Logger]
})
export class InjectorComponent implements OnInit {
car: Car;
heroService: HeroService;
hero: Hero;
constructor(private injector: Injector) { }
ngOnInit() {
this.car = this.injector.get(Car);
this.heroService = this.injector.get(HeroService);
this.hero = this.heroService.getHeroes()[0];
}
get rodent() {
const rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
return this.injector.get(ROUS, rousDontExist);
}
}
/**
* R.O.U.S. - Rodents Of Unusual Size
* // https://www.youtube.com/watch?v=BOv5ZjAOpC8
*/
class ROUS { }
/*
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
*/"><input type="hidden" name="files[src/app/logger.service.ts]" value="import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class Logger {
logs: string[] = []; // capture logs for testing
log(message: string) {
this.logs.push(message);
console.log(message);
}
}
/*
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
*/"><input type="hidden" name="files[src/app/providers.component.ts]" value="/*
* A collection of demo components showing different ways to provide services
* in @Component metadata
*/
import { Component, Inject, Injectable, OnInit } from '@angular/core';
import {
APP_CONFIG,
AppConfig,
HERO_DI_CONFIG } from './app.config';
import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service';
import { UserService } from './user.service';
const template = '{{log}}';
@Component({
selector: 'provider-1',
template,
providers: [Logger]
})
export class Provider1Component {
log: string;
constructor(logger: Logger) {
logger.log('Hello from logger provided with Logger class');
this.log = logger.logs[0];
}
}
//////////////////////////////////////////
@Component({
selector: 'provider-3',
template,
providers:
[{ provide: Logger, useClass: Logger }]
})
export class Provider3Component {
log: string;
constructor(logger: Logger) {
logger.log('Hello from logger provided with useClass:Logger');
this.log = logger.logs[0];
}
}
//////////////////////////////////////////
export class BetterLogger extends Logger {}
@Component({
selector: 'provider-4',
template,
providers:
[{ provide: Logger, useClass: BetterLogger }]
})
export class Provider4Component {
log: string;
constructor(logger: Logger) {
logger.log('Hello from logger provided with useClass:BetterLogger');
this.log = logger.logs[0];
}
}
//////////////////////////////////////////
@Injectable()
export class EvenBetterLogger extends Logger {
constructor(private userService: UserService) { super(); }
log(message: string) {
const name = this.userService.user.name;
super.log(`Message to ${name}: ${message}`);
}
}
@Component({
selector: 'provider-5',
template,
providers:
[ UserService,
{ provide: Logger, useClass: EvenBetterLogger }]
})
export class Provider5Component {
log: string;
constructor(logger: Logger) {
logger.log('Hello from EvenBetterlogger');
this.log = logger.logs[0];
}
}
//////////////////////////////////////////
export class NewLogger extends Logger {}
export class OldLogger {
logs: string[] = [];
log(message: string) {
throw new Error('Should not call the old logger!');
}
}
@Component({
selector: 'provider-6a',
template,
providers:
[ NewLogger,
// Not aliased! Creates two instances of `NewLogger`
{ provide: OldLogger, useClass: NewLogger}]
})
export class Provider6aComponent {
log: string;
constructor(newLogger: NewLogger, oldLogger: OldLogger) {
if (newLogger === oldLogger) {
throw new Error('expected the two loggers to be different instances');
}
oldLogger.log('Hello OldLogger (but we want NewLogger)');
// The newLogger wasn't called so no logs[]
// display the logs of the oldLogger.
this.log = newLogger.logs[0] || oldLogger.logs[0];
}
}
@Component({
selector: 'provider-6b',
template,
providers:
[ NewLogger,
// Alias OldLogger w/ reference to NewLogger
{ provide: OldLogger, useExisting: NewLogger}]
})
export class Provider6bComponent {
log: string;
constructor(newLogger: NewLogger, oldLogger: OldLogger) {
if (newLogger !== oldLogger) {
throw new Error('expected the two loggers to be the same instance');
}
oldLogger.log('Hello from NewLogger (via aliased OldLogger)');
this.log = newLogger.logs[0];
}
}
//////////////////////////////////////////
// An object in the shape of the logger service
function silentLoggerFn() {}
export const SilentLogger = {
logs: ['Silent logger says &quot;Shhhhh!&quot;. Provided via &quot;useValue&quot;'],
log: silentLoggerFn
};
@Component({
selector: 'provider-7',
template,
providers:
[{ provide: Logger, useValue: SilentLogger }]
})
export class Provider7Component {
log: string;
constructor(logger: Logger) {
logger.log('Hello from logger provided with useValue');
this.log = logger.logs[0];
}
}
/////////////////
@Component({
selector: 'provider-8',
template,
providers: [heroServiceProvider, Logger, UserService]
})
export class Provider8Component {
// must be true else this component would have blown up at runtime
log = 'Hero service injected successfully via heroServiceProvider';
constructor(heroService: HeroService) { }
}
/////////////////
@Component({
selector: 'provider-9',
template,
/*
// Can't use interface as provider token
[{ provide: AppConfig, useValue: HERO_DI_CONFIG })]
*/
providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
})
export class Provider9Component implements OnInit {
log: string;
/*
// Can't inject using the interface as the parameter type
constructor(private config: AppConfig){ }
*/
constructor(@Inject(APP_CONFIG) private config: AppConfig) { }
ngOnInit() {
this.log = 'APP_CONFIG Application title is ' + this.config.title;
}
}
//////////////////////////////////////////
// Sample providers 1 to 7 illustrate a required logger dependency.
// Optional logger, can be null
import { Optional } from '@angular/core';
const someMessage = 'Hello from the injected logger';
@Component({
selector: 'provider-10',
template,
providers: [{ provide: Logger, useValue: null }]
})
export class Provider10Component implements OnInit {
log: string;
constructor(@Optional() private logger?: Logger) {
if (this.logger) {
this.logger.log(someMessage);
}
}
ngOnInit() {
this.log = this.logger ? this.logger.logs[0] : 'Optional logger was not available';
}
}
/////////////////
@Component({
selector: 'app-providers',
template: `
<h2>Provider variations</h2>
<div id=&quot;p1&quot;><provider-1></provider-1></div>
<div id=&quot;p3&quot;><provider-3></provider-3></div>
<div id=&quot;p4&quot;><provider-4></provider-4></div>
<div id=&quot;p5&quot;><provider-5></provider-5></div>
<div id=&quot;p6a&quot;><provider-6a></provider-6a></div>
<div id=&quot;p6b&quot;><provider-6b></provider-6b></div>
<div id=&quot;p7&quot;><provider-7></provider-7></div>
<div id=&quot;p8&quot;><provider-8></provider-8></div>
<div id=&quot;p9&quot;><provider-9></provider-9></div>
<div id=&quot;p10&quot;><provider-10></provider-10></div>
`
})
export class ProvidersComponent { }
/*
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
*/"><input type="hidden" name="files[src/app/providers.module.ts]" value="import { NgModule } from '@angular/core';
import {
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
ProvidersComponent,
} from './providers.component';
@NgModule({
declarations: [
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
ProvidersComponent,
],
exports: [ ProvidersComponent ]
})
export class ProvidersModule {}
/*
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
*/"><input type="hidden" name="files[src/app/test.component.ts]" value="/* tslint:disable */
// Simulate a simple test
// Reader should look to the testing chapter for the real thing
import { Component } from '@angular/core';
import { Hero } from './heroes/hero';
import { HeroService } from './heroes/hero.service';
import { HeroListComponent } from './heroes/hero-list.component';
@Component({
selector: 'app-tests',
template: `
<h2>Tests</h2>
<p id=&quot;tests&quot;>Tests {{results.pass}}: {{results.message}}</p>
`
})
export class TestComponent {
results = runTests();
}
/////////////////////////////////////
function runTests() {
const expectedHeroes = [{name: 'A'}, {name: 'B'}]
const mockService = <HeroService> {getHeroes: () => expectedHeroes }
it('should have heroes when HeroListComponent created', () => {
// Pass the mock to the constructor as the Angular injector would
const component = new HeroListComponent(mockService);
expect(component.heroes.length).toEqual(expectedHeroes.length);
});
return testResults;
}
//////////////////////////////////
// Fake Jasmine infrastructure
var testName: string;
var testResults: {pass: string; message: string};
function expect(actual: any) {
return {
toEqual: (expected: any) => {
testResults = actual === expected ?
{pass: 'passed', message: testName} :
{pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};
}
};
}
function it(label: string, test: () => void) {
testName = label;
test();
}
/*
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
*/"><input type="hidden" name="files[src/app/tree-shaking/app.module.ts]" value="import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { ServiceModule } from './service-and-module';
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot([]),
ServiceModule,
],
})
export class AppModule {
}
/*
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
*/"><input type="hidden" name="files[src/app/tree-shaking/service-and-module.ts]" value="import { Injectable, NgModule } from '@angular/core';
@Injectable()
export class Service {
doSomething(): void {
}
}
@NgModule({
providers: [Service],
})
export class ServiceModule {
}
/*
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
*/"><input type="hidden" name="files[src/app/tree-shaking/service.ts]" value="import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class Service {
}
/*
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
*/"><input type="hidden" name="files[src/app/user.service.ts]" value="import { Injectable } from '@angular/core';
export class User {
constructor(
public name: string,
public isAuthorized = false) { }
}
// TODO: get the user; don't 'new' it.
const alice = new User('Alice', true);
const bob = new User('Bob', false);
@Injectable({
providedIn: 'root'
})
export class UserService {
user = bob; // initial user is Bob
// swap users
getNewUser() {
return this.user = this.user === bob ? alice : bob;
}
}
/*
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
*/"><input type="hidden" name="files[src/environments/environment.prod.ts]" value="export const environment = {
production: true
};
/*
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
*/"><input type="hidden" name="files[src/environments/environment.ts]" value="// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
/*
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
*/"><input type="hidden" name="files[src/main.ts]" value="import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
/*
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
*/"><input type="hidden" name="files[src/polyfills.ts]" value="/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called &quot;evergreen&quot; browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch
* requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch
* specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/*
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
*/"><input type="hidden" name="files[src/styles.css]" value="/* Global Styles */
* {
font-family: Arial, Helvetica, sans-serif;
}
h1 {
color: #264D73;
font-size: 2.5rem;
}
h2, h3 {
color: #444;
font-weight: lighter;
}
h3 {
font-size: 1.3rem;
}
body {
padding: .5rem;
max-width: 1000px;
margin: auto;
}
@media (min-width: 600px) {
body {
padding: 2rem;
}
}
body, input[text] {
color: #333;
font-family: Cambria, Georgia, serif;
}
a {
cursor: pointer;
}
button {
background-color: #eee;
border: none;
border-radius: 4px;
cursor: pointer;
color: black;
font-size: 1.2rem;
padding: 1rem;
margin-right: 1rem;
margin-bottom: 1rem;
}
button:hover {
background-color: black;
color: white;
}
button:disabled {
background-color: #eee;
color: #aaa;
cursor: auto;
}
/* Navigation link styles */
nav a {
padding: 5px 10px;
text-decoration: none;
margin-right: 10px;
margin-top: 10px;
display: inline-block;
background-color: #e8e8e8;
color: #3d3d3d;
border-radius: 4px;
}
nav a:hover {
color: white;
background-color: #42545C;
}
nav a.active {
background-color: black;
color: white;
}
hr {
margin: 1.5rem 0;
}
input[type=&quot;text&quot;] {
box-sizing: border-box;
width: 100%;
padding: .5rem;
}
/*
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
*/"><input type="hidden" name="files[src/index.html]" value="<!DOCTYPE html>
<html lang=&quot;en&quot;>
<head>
<title>Dependency Injection</title>
<base href=&quot;/&quot;>
<meta charset=&quot;UTF-8&quot;>
<meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;>
</head>
<body>
<app-root></app-root>
</body>
</html>
<!--
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
-->"><input type="hidden" name="files[angular.json]" value="{
&quot;$schema&quot;: &quot;./node_modules/@angular/cli/lib/config/schema.json&quot;,
&quot;version&quot;: 1,
&quot;newProjectRoot&quot;: &quot;projects&quot;,
&quot;projects&quot;: {
&quot;angular.io-example&quot;: {
&quot;projectType&quot;: &quot;application&quot;,
&quot;schematics&quot;: {
&quot;@schematics/angular:application&quot;: {
&quot;strict&quot;: true
}
},
&quot;root&quot;: &quot;&quot;,
&quot;sourceRoot&quot;: &quot;src&quot;,
&quot;prefix&quot;: &quot;app&quot;,
&quot;architect&quot;: {
&quot;build&quot;: {
&quot;builder&quot;: &quot;@angular-devkit/build-angular:browser&quot;,
&quot;options&quot;: {
&quot;outputPath&quot;: &quot;dist&quot;,
&quot;index&quot;: &quot;src/index.html&quot;,
&quot;main&quot;: &quot;src/main.ts&quot;,
&quot;polyfills&quot;: &quot;src/polyfills.ts&quot;,
&quot;tsConfig&quot;: &quot;tsconfig.app.json&quot;,
&quot;aot&quot;: true,
&quot;assets&quot;: [
&quot;src/favicon.ico&quot;,
&quot;src/assets&quot;
],
&quot;styles&quot;: [
&quot;src/styles.css&quot;
],
&quot;scripts&quot;: []
},
&quot;configurations&quot;: {
&quot;production&quot;: {
&quot;fileReplacements&quot;: [
{
&quot;replace&quot;: &quot;src/environments/environment.ts&quot;,
&quot;with&quot;: &quot;src/environments/environment.prod.ts&quot;
}
],
&quot;optimization&quot;: true,
&quot;outputHashing&quot;: &quot;all&quot;,
&quot;sourceMap&quot;: false,
&quot;namedChunks&quot;: false,
&quot;extractLicenses&quot;: true,
&quot;vendorChunk&quot;: false,
&quot;buildOptimizer&quot;: true,
&quot;budgets&quot;: [
{
&quot;type&quot;: &quot;initial&quot;,
&quot;maximumWarning&quot;: &quot;500kb&quot;,
&quot;maximumError&quot;: &quot;1mb&quot;
},
{
&quot;type&quot;: &quot;anyComponentStyle&quot;,
&quot;maximumWarning&quot;: &quot;2kb&quot;,
&quot;maximumError&quot;: &quot;4kb&quot;
}
]
}
}
},
&quot;serve&quot;: {
&quot;builder&quot;: &quot;@angular-devkit/build-angular:dev-server&quot;,
&quot;options&quot;: {
&quot;browserTarget&quot;: &quot;angular.io-example:build&quot;
},
&quot;configurations&quot;: {
&quot;production&quot;: {
&quot;browserTarget&quot;: &quot;angular.io-example:build:production&quot;
}
}
},
&quot;extract-i18n&quot;: {
&quot;builder&quot;: &quot;@angular-devkit/build-angular:extract-i18n&quot;,
&quot;options&quot;: {
&quot;browserTarget&quot;: &quot;angular.io-example:build&quot;
}
},
&quot;test&quot;: {
&quot;builder&quot;: &quot;@angular-devkit/build-angular:karma&quot;,
&quot;options&quot;: {
&quot;main&quot;: &quot;src/test.ts&quot;,
&quot;polyfills&quot;: &quot;src/polyfills.ts&quot;,
&quot;tsConfig&quot;: &quot;tsconfig.spec.json&quot;,
&quot;karmaConfig&quot;: &quot;karma.conf.js&quot;,
&quot;assets&quot;: [
&quot;src/favicon.ico&quot;,
&quot;src/assets&quot;
],
&quot;styles&quot;: [
&quot;src/styles.css&quot;
],
&quot;scripts&quot;: []
}
},
&quot;lint&quot;: {
&quot;builder&quot;: &quot;@angular-devkit/build-angular:tslint&quot;,
&quot;options&quot;: {
&quot;tsConfig&quot;: [
&quot;tsconfig.app.json&quot;,
&quot;tsconfig.spec.json&quot;,
&quot;e2e/tsconfig.json&quot;
],
&quot;exclude&quot;: [
&quot;**/node_modules/**&quot;
]
}
},
&quot;e2e&quot;: {
&quot;builder&quot;: &quot;@angular-devkit/build-angular:protractor&quot;,
&quot;options&quot;: {
&quot;protractorConfig&quot;: &quot;e2e/protractor.conf.js&quot;,
&quot;devServerTarget&quot;: &quot;angular.io-example:serve&quot;
},
&quot;configurations&quot;: {
&quot;production&quot;: {
&quot;devServerTarget&quot;: &quot;angular.io-example:serve:production&quot;
}
}
}
}
}
},
&quot;defaultProject&quot;: &quot;angular.io-example&quot;
}
"><input type="hidden" name="files[tsconfig.json]" value="{
&quot;compileOnSave&quot;: false,
&quot;compilerOptions&quot;: {
&quot;baseUrl&quot;: &quot;./&quot;,
&quot;outDir&quot;: &quot;./dist/out-tsc&quot;,
&quot;forceConsistentCasingInFileNames&quot;: true,
&quot;noImplicitReturns&quot;: true,
&quot;noFallthroughCasesInSwitch&quot;: true,
&quot;sourceMap&quot;: true,
&quot;declaration&quot;: false,
&quot;downlevelIteration&quot;: true,
&quot;experimentalDecorators&quot;: true,
&quot;moduleResolution&quot;: &quot;node&quot;,
&quot;importHelpers&quot;: true,
&quot;target&quot;: &quot;es2015&quot;,
&quot;module&quot;: &quot;es2020&quot;,
&quot;lib&quot;: [
&quot;es2018&quot;,
&quot;dom&quot;
]
},
&quot;angularCompilerOptions&quot;: {
&quot;strictInjectionParameters&quot;: true,
&quot;strictInputAccessModifiers&quot;: true,
&quot;strictTemplates&quot;: true,
&quot;enableIvy&quot;: true
}
}"><input type="hidden" name="tags[0]" value="angular"><input type="hidden" name="tags[1]" value="example"><input type="hidden" name="tags[2]" value="dependency"><input type="hidden" name="tags[3]" value="di"><input type="hidden" name="description" value="Angular Example - Dependency Injection"><input type="hidden" name="dependencies" value="{&quot;@angular/animations&quot;:&quot;~11.0.1&quot;,&quot;@angular/common&quot;:&quot;~11.0.1&quot;,&quot;@angular/compiler&quot;:&quot;~11.0.1&quot;,&quot;@angular/core&quot;:&quot;~11.0.1&quot;,&quot;@angular/forms&quot;:&quot;~11.0.1&quot;,&quot;@angular/platform-browser&quot;:&quot;~11.0.1&quot;,&quot;@angular/platform-browser-dynamic&quot;:&quot;~11.0.1&quot;,&quot;@angular/router&quot;:&quot;~11.0.1&quot;,&quot;angular-in-memory-web-api&quot;:&quot;~0.11.0&quot;,&quot;rxjs&quot;:&quot;~6.6.0&quot;,&quot;tslib&quot;:&quot;^2.0.0&quot;,&quot;zone.js&quot;:&quot;~0.11.4&quot;,&quot;jasmine-core&quot;:&quot;~3.6.0&quot;,&quot;jasmine-marbles&quot;:&quot;~0.6.0&quot;}"></form>
<script>
var embedded = 'ctl=1';
var isEmbedded = window.location.search.indexOf(embedded) > -1;
if (isEmbedded) {
var form = document.getElementById('mainForm');
var action = form.action;
var actionHasParams = action.indexOf('?') > -1;
var symbol = actionHasParams ? '&' : '?'
form.action = form.action + symbol + embedded;
}
document.getElementById("mainForm").submit();
</script>
</body></html>