docs: make all examples compatible with strict mode (#41999)

Turns on the `strict` compiler flag and resolves the compilation errors in the various AIO examples.

PR Close #41999
This commit is contained in:
Kristiyan Kostadinov 2021-05-08 16:02:03 +02:00 committed by atscott
parent 5960d4c513
commit e86a1d3441
244 changed files with 615 additions and 621 deletions

View File

@ -89,7 +89,7 @@ describe('AngularJS to Angular Quick Reference Tests', () => {
return element.all(by.css('app-movie-list tbody > tr'));
}
async function testFavoriteHero(heroName: string, expectedLabel: string) {
async function testFavoriteHero(heroName: string | null, expectedLabel: string) {
const movieListComp = element(by.tagName('app-movie-list'));
const heroInput = movieListComp.element(by.tagName('input'));
const favoriteHeroLabel = movieListComp.element(by.tagName('h3'));

View File

@ -16,7 +16,7 @@ export class AppComponent {
eventType = '<not clicked yet>';
isActive = true;
isImportant = true;
movie: IMovie = null;
movie: IMovie;
movies: IMovie[] = [];
showImage = true;
title = 'AngularJS to Angular Quick Ref Cookbook';

View File

@ -1,4 +1,3 @@
/* tslint:disable:no-unused-variable */
// #docplaster
import { Component } from '@angular/core';
import { IMovie } from './movie';
@ -16,7 +15,7 @@ import { MovieService } from './movie.service';
// #docregion class
export class MovieListComponent {
// #enddocregion class
favoriteHero: string;
favoriteHero: string | undefined;
showImage = false;
movies: IMovie[];

View File

@ -31,7 +31,7 @@ import { Hero } from './hero';
// #enddocregion auto-calc
})
export class HeroListAutoComponent {
@Input() heroes: Hero[];
@Input() heroes: Hero[] = [];
@Output() remove = new EventEmitter<number>();

View File

@ -44,7 +44,7 @@ import { Hero } from './hero';
// #enddocregion animationdef
})
export class HeroListEnterLeaveComponent {
@Input() heroes: Hero[];
@Input() heroes: Hero[] = [];
@Output() remove = new EventEmitter<number>();

View File

@ -64,7 +64,7 @@ import { Hero } from './hero';
// #enddocregion animationdef
})
export class HeroListGroupsComponent {
@Input() heroes: Hero[];
@Input() heroes: Hero[] = [];
@Output() remove = new EventEmitter<number>();

View File

@ -4,6 +4,7 @@
import { Component, HostBinding, OnInit } from '@angular/core';
import { trigger, transition, animate, style, query, stagger } from '@angular/animations';
import { HEROES } from './mock-heroes';
import { Hero } from './hero';
// #docregion filter-animations
@Component({
@ -58,7 +59,7 @@ export class HeroListPageComponent implements OnInit {
heroTotal = -1;
// #enddocregion filter-animations
get heroes() { return this._heroes; }
private _heroes = [];
private _heroes: Hero[] = [];
ngOnInit() {
this._heroes = HEROES;

View File

@ -7,5 +7,5 @@ import { Hero } from './hero';
templateUrl: './hero-detail.component.html'
})
export class HeroDetailComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
}

View File

@ -13,8 +13,8 @@ import { HeroService } from './hero.service';
// #docregion class
export class HeroListComponent implements OnInit {
// #enddocregion metadata
heroes: Hero[];
selectedHero: Hero;
heroes: Hero[] = [];
selectedHero: Hero | undefined;
// #docregion ctor
constructor(private service: HeroService) { }

View File

@ -7,5 +7,5 @@ import { Component } from '@angular/core';
})
// #docregion class
export class AppComponent {
color: string;
color = '';
}

View File

@ -19,7 +19,7 @@ export class HighlightDirective {
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
this.highlight('');
}
private highlight(color: string) {

View File

@ -11,7 +11,7 @@ export class HighlightDirective {
constructor(private el: ElementRef) { }
// #docregion input
@Input() appHighlight: string;
@Input() appHighlight = '';
// #enddocregion input
@HostListener('mouseenter') onMouseEnter() {
@ -19,7 +19,7 @@ export class HighlightDirective {
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
this.highlight('');
}
private highlight(color: string) {

View File

@ -9,10 +9,10 @@ export class HighlightDirective {
constructor(private el: ElementRef) { }
// #docregion defaultColor
@Input() defaultColor: string;
@Input() defaultColor = '';
// #enddocregion defaultColor
@Input('appHighlight') highlightColor: string;
@Input('appHighlight') highlightColor = '';
// #docregion mouse-enter
@HostListener('mouseenter') onMouseEnter() {
@ -21,7 +21,7 @@ export class HighlightDirective {
// #enddocregion mouse-enter
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
this.highlight('');
}
private highlight(color: string) {

View File

@ -7,7 +7,7 @@ describe('Binding syntax e2e tests', () => {
// helper function used to test what's logged to the console
async function logChecker(contents) {
async function logChecker(contents: string) {
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
expect(messages.length).toBeGreaterThan(0);

View File

@ -8,7 +8,7 @@ import { Component, ViewChild, ElementRef } from '@angular/core';
})
export class AppComponent {
@ViewChild('bindingInput') bindingInput: ElementRef;
@ViewChild('bindingInput') bindingInput!: ElementRef;
isUnchanged = true;

View File

@ -8,7 +8,7 @@
<p>Current item name: {{currentItem.name}}</p>
<p>
<label for="without">without NgModel:</label>
<input [value]="currentItem.name" (input)="currentItem.name=getValue($event.target)" id="without">
<input [value]="currentItem.name" (input)="currentItem.name=getValue($event)" id="without">
</p>
<p>

View File

@ -13,16 +13,16 @@ export class AppComponent implements OnInit {
isUnchanged = true;
isActive = true;
nullCustomer = null;
nullCustomer: string | null = null;
currentCustomer = {
name: 'Laura'
};
item: Item; // defined to demonstrate template context precedence
items: Item[];
item!: Item; // defined to demonstrate template context precedence
items: Item[] = [];
// #docregion item
currentItem: Item;
currentItem!: Item;
// #enddocregion item
@ -34,11 +34,11 @@ export class AppComponent implements OnInit {
itemIdIncrement = 1;
// #docregion setClasses
currentClasses: {};
currentClasses: Record<string, boolean> = {};
// #enddocregion setClasses
// #docregion setStyles
currentStyles: {};
currentStyles: Record<string, string> = {};
// #enddocregion setStyles
ngOnInit() {
@ -114,8 +114,8 @@ export class AppComponent implements OnInit {
trackById(index: number, item: any): number { return item.id; }
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
getValue(event: Event): string {
return (event.target as HTMLInputElement).value;
}
}

View File

@ -10,7 +10,7 @@ import { Item } from '../item';
export class ItemDetailComponent {
@Input() item: Item;
@Input() item!: Item;
constructor() { }

View File

@ -8,7 +8,7 @@ import { Item } from './item';
// #docregion input
export class StoutItemComponent {
@Input() item: Item;
@Input() item!: Item;
}
// #enddocregion input
@ -18,7 +18,7 @@ export class StoutItemComponent {
template: `This is the brightest {{item.name}} in town.`
})
export class BestItemComponent {
@Input() item: Item;
@Input() item!: Item;
}
@Component({
@ -26,7 +26,7 @@ export class BestItemComponent {
template: `Which is the slimmest {{item.name}}?`
})
export class DeviceItemComponent {
@Input() item: Item;
@Input() item!: Item;
}
@Component({
@ -34,7 +34,7 @@ export class DeviceItemComponent {
template: `Has anyone seen my {{item.name}}?`
})
export class LostItemComponent {
@Input() item: Item;
@Input() item!: Item;
}
@Component({
@ -42,7 +42,7 @@ export class LostItemComponent {
template: `{{message}}`
})
export class UnknownItemComponent {
@Input() item: Item;
@Input() item!: Item;
get message() {
return this.item && this.item.name ?
`${this.item.name} is strange and mysterious.` :

View File

@ -3,7 +3,7 @@ export class Item {
static items: Item[] = [
new Item(
null,
0,
'Teapot',
'stout'
),
@ -15,7 +15,7 @@ export class Item {
constructor(
public id?: number,
public id: number,
public name?: string,
public feature?: string,
public url?: string,
@ -25,6 +25,6 @@ export class Item {
}
clone(): Item {
return Object.assign(new Item(), this);
return Object.assign(new Item(this.id), this);
}
}

View File

@ -2,11 +2,11 @@ import { docRegionChain, docRegionObservable, docRegionUnsubscribe } from './obs
describe('observables', () => {
it('should print 2', (doneFn: DoneFn) => {
const consoleLogSpy = spyOn(console, 'log');
const observable = docRegionObservable(console);
const consoleSpy = jasmine.createSpyObj<Console>('console', ['log']);
const observable = docRegionObservable(consoleSpy);
observable.subscribe(() => {
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
expect(consoleLogSpy).toHaveBeenCalledWith(2);
expect(consoleSpy.log).toHaveBeenCalledTimes(1);
expect(consoleSpy.log).toHaveBeenCalledWith(2);
doneFn();
});
});

View File

@ -2,11 +2,11 @@ import { docRegionError, docRegionPromise } from './promises';
describe('promises', () => {
it('should print 2', (doneFn: DoneFn) => {
const consoleLogSpy = spyOn(console, 'log');
const pr = docRegionPromise(console, 2);
const consoleSpy = jasmine.createSpyObj<Console>('console', ['log']);
const pr = docRegionPromise(consoleSpy, 2);
pr.then((value) => {
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
expect(consoleLogSpy).toHaveBeenCalledWith(2);
expect(consoleSpy.log).toHaveBeenCalledTimes(1);
expect(consoleSpy.log).toHaveBeenCalledWith(2);
expect(value).toBe(4);
doneFn();
});

View File

@ -3,7 +3,7 @@
export function docRegionPromise(console: Console, inputValue: number) {
// #docregion promise
// initiate execution
let promise = new Promise<number>((resolve, reject) => {
let promise = new Promise<number>(resolve => {
// Executer fn...
// #enddocregion promise
// The below is used in the unit tests.

View File

@ -18,7 +18,7 @@ import { Subscription } from 'rxjs';
`
})
export class AstronautComponent implements OnDestroy {
@Input() astronaut: string;
@Input() astronaut = '';
mission = '<no mission announced>';
confirmed = false;
announced = false;

View File

@ -40,7 +40,7 @@ export class CountdownLocalVarParentComponent { }
export class CountdownViewChildParentComponent implements AfterViewInit {
@ViewChild(CountdownTimerComponent)
private timerComponent: CountdownTimerComponent;
private timerComponent!: CountdownTimerComponent;
seconds() { return 0; }

View File

@ -11,7 +11,7 @@ import { Hero } from './hero';
`
})
export class HeroChildComponent {
@Input() hero: Hero;
@Input('master') masterName: string; // tslint:disable-line: no-input-rename
@Input() hero!: Hero;
@Input('master') masterName = ''; // tslint:disable-line: no-input-rename
}
// #enddocregion

View File

@ -13,8 +13,8 @@ import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
`
})
export class VersionChildComponent implements OnChanges {
@Input() major: number;
@Input() minor: number;
@Input() major = 0;
@Input() minor = 0;
changeLog: string[] = [];
ngOnChanges(changes: SimpleChanges) {

View File

@ -10,7 +10,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
`
})
export class VoterComponent {
@Input() name: string;
@Input() name = '';
@Output() voted = new EventEmitter<boolean>();
didVote = false;

View File

@ -12,5 +12,5 @@ import { Hero } from './hero';
`
})
export class HeroAppMainComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
}

View File

@ -17,7 +17,7 @@ import { Hero } from './hero';
})
// #enddocregion inlinestyles
export class HeroControlsComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
activate() {
this.hero.active = true;

View File

@ -11,5 +11,5 @@ import { Hero } from './hero';
styleUrls: ['./hero-details.component.css']
})
export class HeroDetailsComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
}

View File

@ -16,5 +16,5 @@ import { Hero } from './hero';
})
// #enddocregion stylelink
export class HeroTeamComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
}

View File

@ -1,5 +1,5 @@
export class Hero {
active: boolean;
active = false;
constructor(public name: string,
public team: string[]) {

View File

@ -37,6 +37,6 @@ export class ZippyComponent {
contentId = `zippy-${nextId++}`;
@Input() expanded = false;
// #docregion contentchild
@ContentChild(ZippyContentDirective) content: ZippyContentDirective;
@ContentChild(ZippyContentDirective) content!: ZippyContentDirective;
// #enddocregion contentchild
}

View File

@ -1,4 +1,4 @@
import { browser, element, by } from 'protractor';
import { browser, element, by, ElementFinder } from 'protractor';
describe('Dependency Injection Cookbook', () => {
@ -19,7 +19,7 @@ describe('Dependency Injection Cookbook', () => {
expect(await sortedHeroes.isPresent()).toBe(true);
const sortedHeroElems = element.all(by.css('app-sorted-heroes div'));
const sortedHeroNames = await sortedHeroElems.map(elem => elem.getText());
const sortedHeroNames = await sortedHeroElems.map(elem => elem?.getText());
expect(sortedHeroNames).toEqual(['Dr Nice', 'Magma', 'RubberMan']);
});

View File

@ -16,7 +16,7 @@ import { HeroCacheService } from './hero-cache.service';
})
export class HeroBioComponent implements OnInit {
@Input() heroId: number;
@Input() heroId = 0;
constructor(private heroCache: HeroCacheService) { }

View File

@ -7,7 +7,7 @@ import { HeroService } from './hero.service';
// #docregion service
@Injectable()
export class HeroCacheService {
hero: Hero;
hero!: Hero;
constructor(private heroService: HeroService) {}
fetchCachedHero(id: number) {

View File

@ -15,7 +15,7 @@ export class HeroService {
];
getHeroById(id: number): Hero {
return this.heroes.find(hero => hero.id === id);
return this.heroes.find(hero => hero.id === id)!;
}
getAllHeroes(): Array<Hero> {

View File

@ -7,7 +7,7 @@ import { Directive, ElementRef, HostListener, Input } from '@angular/core';
})
export class HighlightDirective {
@Input('appHighlight') highlightColor: string;
@Input('appHighlight') highlightColor = '';
private el: HTMLElement;
@ -20,7 +20,7 @@ export class HighlightDirective {
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
this.highlight('');
}
private highlight(color: string) {

View File

@ -2,8 +2,8 @@
// Class used as a "narrowing" interface that exposes a minimal logger
// Other members of the actual implementation are invisible
export abstract class MinimalLogger {
logs: string[];
logInfo: (msg: string) => void;
abstract logs: string[];
abstract logInfo: (msg: string) => void;
}
// #enddocregion

View File

@ -8,7 +8,7 @@ export abstract class Base { name = 'Count Basie'; }
// Marker class, used as an interface
// #docregion parent
export abstract class Parent { name: string; }
export abstract class Parent { abstract name: string; }
// #enddocregion parent
const DifferentParent = Parent;

View File

@ -15,7 +15,7 @@ import { HeroService } from './hero.service';
export class HeroesBaseComponent implements OnInit {
constructor(private heroService: HeroService) { }
heroes: Array<Hero>;
heroes: Hero[] = [];
ngOnInit() {
this.heroes = this.heroService.getAllHeroes();

View File

@ -7,8 +7,8 @@ import { UserService } from './user.service';
providedIn: 'root'
})
export class UserContextService {
name: string;
role: string;
name = '';
role = '';
loggedInSince: Date;
constructor(private userService: UserService, private loggerService: LoggerService) {

View File

@ -1,6 +1,6 @@
// #docplaster
// #docregion
import { Component, Injector, OnInit } from '@angular/core';
import { Component, Injector } from '@angular/core';
import { Car, Engine, Tires } from './car/car';
import { Hero } from './heroes/hero';
@ -18,7 +18,7 @@ import { Logger } from './logger.service';
`,
providers: [Car, Engine, Tires, heroServiceProvider, Logger]
})
export class InjectorComponent implements OnInit {
export class InjectorComponent {
car: Car;
// #docregion get-hero-service
@ -26,9 +26,7 @@ export class InjectorComponent implements OnInit {
// #enddocregion get-hero-service
hero: Hero;
constructor(private injector: Injector) { }
ngOnInit() {
constructor(private injector: Injector) {
this.car = this.injector.get(Car);
this.heroService = this.injector.get(HeroService);
this.hero = this.heroService.getHeroes()[0];

View File

@ -210,7 +210,7 @@ export class Provider8Component {
// #enddocregion providers-9
})
export class Provider9Component implements OnInit {
log: string;
log = '';
/*
// #docregion provider-9-ctor-interface
// Can't inject using the interface as the parameter type
@ -237,7 +237,7 @@ const someMessage = 'Hello from the injected logger';
providers: [{ provide: Logger, useValue: null }]
})
export class Provider10Component implements OnInit {
log: string;
log = '';
constructor(@Optional() private logger?: Logger) {
if (this.logger) {
this.logger.log(someMessage);

View File

@ -13,7 +13,7 @@ export class AppComponent {
// #enddocregion class-skeleton
title = 'Authors Style Guide Sample';
heroes = HEROES;
selectedHero: Hero;
selectedHero!: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;

View File

@ -18,9 +18,9 @@ import { AdComponent } from './ad.component';
})
// #docregion class
export class AdBannerComponent implements OnInit, OnDestroy {
@Input() ads: AdItem[];
@Input() ads: AdItem[] = [];
currentAdIndex = -1;
@ViewChild(AdDirective, {static: true}) adHost: AdDirective;
@ViewChild(AdDirective, {static: true}) adHost!: AdDirective;
interval: any;
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }

View File

@ -13,7 +13,7 @@ import { AdItem } from './ad-item';
`
})
export class AppComponent implements OnInit {
ads: AdItem[];
ads: AdItem[] = [];
constructor(private adService: AdService) {}

View File

@ -9,7 +9,7 @@ import { QuestionBase } from './question-base';
templateUrl: './dynamic-form-question.component.html'
})
export class DynamicFormQuestionComponent {
@Input() question: QuestionBase<string>;
@Input() form: FormGroup;
@Input() question!: QuestionBase<string>;
@Input() form!: FormGroup;
get isValid() { return this.form.controls[this.question.key].valid; }
}

View File

@ -12,14 +12,14 @@ import { QuestionControlService } from './question-control.service';
})
export class DynamicFormComponent implements OnInit {
@Input() questions: QuestionBase<string>[] = [];
form: FormGroup;
@Input() questions: QuestionBase<string>[] | null = [];
form!: FormGroup;
payLoad = '';
constructor(private qcs: QuestionControlService) { }
constructor(private qcs: QuestionControlService) {}
ngOnInit() {
this.form = this.qcs.toFormGroup(this.questions);
this.form = this.qcs.toFormGroup(this.questions as QuestionBase<string>[]);
}
onSubmit() {

View File

@ -1,6 +1,6 @@
// #docregion
export class QuestionBase<T> {
value: T;
value: T|undefined;
key: string;
label: string;
required: boolean;

View File

@ -47,7 +47,7 @@ export class PopupComponent {
this._message = message;
this.state = 'opened';
}
private _message: string;
private _message = '';
@Output()
closed = new EventEmitter();

View File

@ -20,7 +20,7 @@
<!-- #docregion event-binding-3-->
<input [value]="currentItem.name"
(input)="currentItem.name=getValue($event.target)">
(input)="currentItem.name=getValue($event)">
<!-- #enddocregion event-binding-3-->
without NgModel
</div>

View File

@ -27,8 +27,8 @@ export class AppComponent {
}
// #docregion getValue
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
getValue(event: Event): string {
return (event.target as HTMLInputElement).value;
}
// #enddocregion getValue
}

View File

@ -10,7 +10,7 @@ import { Item } from '../item';
})
export class ItemDetailComponent {
@Input() item;
@Input() item!: Item;
itemImageUrl = 'assets/teapot.svg';
lineThrough = '';
displayNone = '';

View File

@ -1,4 +1,4 @@
export class Item {
name: '';
name = '';
}

View File

@ -18,13 +18,13 @@
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors.required">
<div *ngIf="name.errors?.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
<div *ngIf="name.errors?.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors.forbiddenName">
<div *ngIf="name.errors?.forbiddenName">
Name cannot be Bob.
</div>
</div>
@ -59,7 +59,7 @@
</select>
<div *ngIf="power.invalid && power.touched" class="alert alert-danger">
<div *ngIf="power.errors.required">Power is required.</div>
<div *ngIf="power.errors?.required">Power is required.</div>
</div>
</div>

View File

@ -17,7 +17,7 @@ export class HeroFormReactiveComponent implements OnInit {
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
heroForm: FormGroup;
heroForm!: FormGroup;
ngOnInit(): void {
this.heroForm = new FormGroup({
@ -34,11 +34,11 @@ export class HeroFormReactiveComponent implements OnInit {
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
}
get name() { return this.heroForm.get('name'); }
get name() { return this.heroForm.get('name')!; }
get power() { return this.heroForm.get('power'); }
get power() { return this.heroForm.get('power')!; }
get alterEgo() { return this.heroForm.get('alterEgo'); }
get alterEgo() { return this.heroForm.get('alterEgo')!; }
constructor(private alterEgoValidator: UniqueAlterEgoValidator) { }
}

View File

@ -20,7 +20,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
// #enddocregion directive-providers
})
export class ForbiddenValidatorDirective implements Validator {
@Input('appForbiddenName') forbiddenName: string;
@Input('appForbiddenName') forbiddenName = '';
validate(control: AbstractControl): ValidationErrors | null {
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)

View File

@ -18,7 +18,7 @@ export const identityRevealedValidator: ValidatorFn = (control: AbstractControl)
providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
})
export class IdentityRevealedValidatorDirective implements Validator {
validate(control: AbstractControl): ValidationErrors {
validate(control: AbstractControl): ValidationErrors | null {
return identityRevealedValidator(control);
}
}

View File

@ -19,13 +19,13 @@
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert">
<div *ngIf="name.errors.required">
<div *ngIf="name.errors?.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
<div *ngIf="name.errors?.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors.forbiddenName">
<div *ngIf="name.errors?.forbiddenName">
Name cannot be Bob.
</div>

View File

@ -65,7 +65,7 @@ describe('Getting Started', () => {
await pageElements.productListLinks.get(0).click();
const product = pageElements.productDetailsPage;
const buyButton = await product.element(by.css('button'));
const buyButton = product.element(by.css('button'));
const checkoutLink = pageElements.topBarCheckoutLink;
await buyButton.click();
@ -89,12 +89,12 @@ describe('Getting Started', () => {
const checkoutLink = pageElements.topBarCheckoutLink;
const productDetailsPage = pageElements.productDetailsPage;
const buyButton = await productDetailsPage.element(by.css('button'));
const buyButton = productDetailsPage.element(by.css('button'));
const cartPage = pageElements.cartPage;
const inputFields = cartPage.all(by.css('form input'));
const purchaseButton = await cartPage.element(by.css('button'));
const purchaseButton = cartPage.element(by.css('button'));
const nameField = inputFields.get(0);
const addressField = inputFields.get(1);

View File

@ -2,6 +2,7 @@
// #docregion import-http
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Product } from './products';
// #enddocregion import-http
@Injectable({
@ -10,7 +11,7 @@ import { HttpClient } from '@angular/common/http';
// #docregion props, methods, inject-http, get-shipping
export class CartService {
// #enddocregion get-shipping
items = [];
items: Product[] = [];
// #enddocregion props, methods
constructor(
@ -19,7 +20,7 @@ export class CartService {
// #enddocregion inject-http
// #docregion methods
addToCart(product) {
addToCart(product: Product) {
this.items.push(product);
}

View File

@ -3,6 +3,7 @@
import { Component, OnInit } from '@angular/core';
// #enddocregion as-generated
import { Input } from '@angular/core';
import { Product } from '../products';
// #enddocregion imports
// #docregion as-generated
@ -14,7 +15,7 @@ import { Input } from '@angular/core';
// #docregion input-decorator
export class ProductAlertsComponent implements OnInit {
// #enddocregion as-generated
@Input() product;
@Input() product!: Product;
// #docregion as-generated
constructor() { }

View File

@ -3,6 +3,7 @@
import { Component } from '@angular/core';
import { Input } from '@angular/core';
import { Output, EventEmitter } from '@angular/core';
import { Product } from '../products';
// #enddocregion imports
@Component({
@ -12,6 +13,6 @@ import { Output, EventEmitter } from '@angular/core';
})
// #docregion input-output
export class ProductAlertsComponent {
@Input() product;
@Input() product!: Product;
@Output() notify = new EventEmitter();
}

View File

@ -3,7 +3,7 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { products } from '../products';
import { Product, products } from '../products';
// #enddocregion imports
@Component({
@ -13,7 +13,7 @@ import { products } from '../products';
})
// #docregion props-methods, product-prop
export class ProductDetailsComponent implements OnInit {
product;
product: Product|undefined;
// #enddocregion product-prop
constructor(

View File

@ -3,7 +3,7 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { products } from '../products';
import { Product, products } from '../products';
import { CartService } from '../cart.service';
// #enddocregion cart-service
@ -15,7 +15,7 @@ import { CartService } from '../cart.service';
// #docregion inject-cart-service, add-to-cart
export class ProductDetailsComponent implements OnInit {
// #enddocregion add-to-cart, inject-cart-service
product;
product: Product|undefined;
// #docregion inject-cart-service
constructor(
@ -34,7 +34,7 @@ export class ProductDetailsComponent implements OnInit {
}
// #docregion add-to-cart
addToCart(product) {
addToCart(product: Product) {
this.cartService.addToCart(product);
window.alert('Your product has been added to the cart!');
}

View File

@ -1,4 +1,11 @@
export const products = [
export interface Product {
id: number;
name: string;
price: number;
description: string;
}
export const products: Product[] = [
{
id: 1,
name: 'Phone XL',

View File

@ -5,8 +5,8 @@ import { HeroesService } from './heroes.service';
@Injectable()
export class HeroTaxReturnService {
private currentTaxReturn: HeroTaxReturn;
private originalTaxReturn: HeroTaxReturn;
private currentTaxReturn!: HeroTaxReturn;
private originalTaxReturn!: HeroTaxReturn;
constructor(private heroService: HeroesService) { }

View File

@ -11,9 +11,9 @@ import { Config, ConfigService } from './config.service';
})
export class ConfigComponent {
error: any;
headers: string[];
headers: string[] = [];
// #docregion v2
config: Config;
config: Config | undefined;
// #enddocregion v2
constructor(private configService: ConfigService) {}
@ -21,7 +21,7 @@ export class ConfigComponent {
clear() {
this.config = undefined;
this.error = undefined;
this.headers = undefined;
this.headers = [];
}
// #docregion v1, v2
@ -64,7 +64,7 @@ export class ConfigComponent {
`${key}: ${resp.headers.get(key)}`);
// access the body directly, which is typed as `Config`.
this.config = { ... resp.body };
this.config = { ...resp.body! };
});
}
// #enddocregion showConfigResponse

View File

@ -43,7 +43,7 @@ export class ConfigService {
getConfig_1() {
// #docregion getConfig_1
return this.http.get(this.configUrl);
return this.http.get<Config>(this.configUrl);
}
// #enddocregion getConfig_1

View File

@ -7,7 +7,7 @@ import { DownloaderService } from './downloader.service';
providers: [ DownloaderService ]
})
export class DownloaderComponent {
contents: string;
contents: string | undefined;
constructor(private downloaderService: DownloaderService) {}
clear() {

View File

@ -10,8 +10,8 @@ import { HeroesService } from './heroes.service';
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: Hero[];
editHero: Hero; // the hero currently being edited
heroes: Hero[] = [];
editHero: Hero | undefined; // the hero currently being edited
constructor(private heroesService: HeroesService) {}

View File

@ -70,7 +70,7 @@ export class HeroesService {
// #docregion deleteHero
/** DELETE: delete the hero from the server */
deleteHero(id: number): Observable<{}> {
deleteHero(id: number): Observable<unknown> {
const url = `${this.heroesUrl}/${id}`; // DELETE api/heroes/42
return this.http.delete(url, httpOptions)
.pipe(

View File

@ -2,7 +2,7 @@
<h2>Search Npm Packages</h2>
<p><i>Searches when typing stops. Caches for 30 seconds.</i></p>
<!-- #docregion search -->
<input type="text" (keyup)="search(getValue($event.target))" id="name" placeholder="Search"/>
<input type="text" (keyup)="search(getValue($event))" id="name" placeholder="Search"/>
<!-- #enddocregion search -->
<input type="checkbox" id="refresh" [checked]="withRefresh" (click)="toggleRefresh()">
<label for="refresh">with refresh</label>

View File

@ -14,7 +14,7 @@ import { NpmPackageInfo, PackageSearchService } from './package-search.service';
export class PackageSearchComponent implements OnInit {
// #docregion debounce
withRefresh = false;
packages$: Observable<NpmPackageInfo[]>;
packages$!: Observable<NpmPackageInfo[]>;
private searchText$ = new Subject<string>();
search(packageName: string) {
@ -37,8 +37,8 @@ export class PackageSearchComponent implements OnInit {
toggleRefresh() { this.withRefresh = ! this.withRefresh; }
// #docregion getValue
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
getValue(event: Event): string {
return (event.target as HTMLInputElement).value;
}
// #enddocregion getValue
}

View File

@ -14,17 +14,11 @@ export interface NpmPackageInfo {
export const searchUrl = 'https://npmsearch.com/query';
const httpOptions = {
headers: new HttpHeaders({
'x-refresh': 'true'
})
};
function createHttpOptions(packageName: string, refresh = false) {
// npm package name search api
// e.g., http://npmsearch.com/query?q=dom'
const params = new HttpParams({ fromObject: { q: packageName } });
const headerMap = refresh ? {'x-refresh': 'true'} : {};
const headerMap: Record<string, string> = refresh ? {'x-refresh': 'true'} : {};
const headers = new HttpHeaders(headerMap) ;
return { headers, params };
}

View File

@ -8,16 +8,16 @@ import { UploaderService } from './uploader.service';
providers: [ UploaderService ]
})
export class UploaderComponent {
message: string;
message = '';
constructor(private uploaderService: UploaderService) {}
onPicked(input: HTMLInputElement) {
const file = input.files[0];
const file = input.files?.[0];
if (file) {
this.uploaderService.upload(file).subscribe(
msg => {
input.value = null;
input.value = '';
this.message = msg;
}
);

View File

@ -62,7 +62,7 @@ export class UploaderService {
case HttpEventType.UploadProgress:
// Compute and show the % done:
const percentDone = Math.round(100 * event.loaded / event.total);
const percentDone = Math.round(100 * event.loaded / (event.total ?? 0));
return `File "${file.name}" is ${percentDone}% uploaded.`;
case HttpEventType.Response:

View File

@ -2,7 +2,7 @@ import './testing/global-jasmine';
import 'jasmine-core/lib/jasmine-core/jasmine-html.js';
import 'jasmine-core/lib/jasmine-core/boot.js';
declare var jasmine;
declare var jasmine: any;
import './polyfills';
@ -27,7 +27,7 @@ function bootstrap() {
location.reload();
return;
} else {
window.onload(undefined);
window.onload?.({} as Event);
(window as any).jasmineRef = jasmine.getEnv();
}

View File

@ -0,0 +1 @@
declare module 'jasmine-core/lib/jasmine-core/jasmine.js';

View File

@ -5,7 +5,7 @@ describe('Inputs and Outputs', () => {
beforeEach(() => browser.get(''));
// helper function used to test what's logged to the console
async function logChecker(contents) {
async function logChecker(contents: string) {
const logs = await browser
.manage()
.logs()

View File

@ -13,10 +13,10 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
})
export class AliasingComponent {
input1: string;
input1 = '';
outputEvent1: EventEmitter<string> = new EventEmitter<string>();
@Input('wishListItem') input2: string; // @Input(alias)
@Input('wishListItem') input2 = ''; // @Input(alias)
@Output('wishEvent') outputEvent2 = new EventEmitter<string>(); // @Output(alias) propertyName = ...

View File

@ -34,11 +34,11 @@ export class AppComponent {
console.warn(`Parent says: crossing off ${item}.`);
}
buyClearanceItem(item) {
buyClearanceItem(item: string) {
console.warn(`Parent says: buying ${item}.`);
}
saveForLater(item) {
saveForLater(item: string) {
console.warn(`Parent says: saving ${item} for later.`);
}

View File

@ -17,7 +17,7 @@ export class InTheMetadataComponent {
buyEvent = new EventEmitter<string>();
clearanceItem: string;
clearanceItem = '';
buyIt() {
console.warn('Child says: emiting buyEvent with', this.clearanceItem);

View File

@ -6,7 +6,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
styleUrls: ['./input-output.component.css']
})
export class InputOutputComponent {
@Input() item: string;
@Input() item = '';
@Output() deleteRequest = new EventEmitter<string>();
lineThrough = '';

View File

@ -11,6 +11,6 @@ import { Component, Input } from '@angular/core'; // First, import Input
// #docregion use-input
export class ItemDetailComponent {
@Input() item: string; // decorate the property with @Input()
@Input() item = ''; // decorate the property with @Input()
}
// #enddocregion use-input

View File

@ -1,3 +1,3 @@
export class Customer {
name: string;
name = '';
}

View File

@ -25,7 +25,7 @@ export class AfterContentComponent implements AfterContentChecked, AfterContentI
comment = '';
// Query for a CONTENT child of type `ChildComponent`
@ContentChild(ChildComponent) contentChild: ChildComponent;
@ContentChild(ChildComponent) contentChild!: ChildComponent;
// #enddocregion hooks
constructor(private logger: LoggerService) {

View File

@ -27,7 +27,7 @@ export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
private prevHero = '';
// Query for a VIEW child of type `ChildViewComponent`
@ViewChild(ChildViewComponent) viewChild: ChildViewComponent;
@ViewChild(ChildViewComponent) viewChild!: ChildViewComponent;
// #enddocregion hooks
constructor(private logger: LoggerService) {

View File

@ -20,7 +20,7 @@ import { LoggerService } from './logger.service';
providers: [LoggerService]
})
export class CounterParentComponent {
value: number;
value = 0;
spyLog: string[] = [];
private logger: LoggerService;

View File

@ -16,7 +16,7 @@ import {
`
})
export class MyCounterComponent implements OnChanges {
@Input() counter: number;
@Input() counter = 0;
changeLog: string[] = [];
ngOnChanges(changes: SimpleChanges) {

View File

@ -8,10 +8,10 @@ import { Hero } from './hero';
templateUrl: './do-check-parent.component.html'
})
export class DoCheckParentComponent {
hero: Hero;
power: string;
hero!: Hero;
power = '';
title = 'DoCheck';
@ViewChild(DoCheckComponent) childView: DoCheckComponent;
@ViewChild(DoCheckComponent) childView!: DoCheckComponent;
constructor() {
this.reset();

View File

@ -16,8 +16,8 @@ import { Hero } from './hero';
`
})
export class DoCheckComponent implements DoCheck {
@Input() hero: Hero;
@Input() power: string;
@Input() hero!: Hero;
@Input() power = '';
changeDetected = false;
changeLog: string[] = [];

View File

@ -9,10 +9,10 @@ import { OnChangesComponent } from './on-changes.component';
styles: ['']
})
export class OnChangesParentComponent {
hero: Hero;
power: string;
hero!: Hero;
power = '';
title = 'OnChanges';
@ViewChild(OnChangesComponent) childView: OnChangesComponent;
@ViewChild(OnChangesComponent) childView!: OnChangesComponent;
constructor() {
this.reset();

View File

@ -17,8 +17,8 @@ import { Hero } from './hero';
})
export class OnChangesComponent implements OnChanges {
// #docregion inputs
@Input() hero: Hero;
@Input() power: string;
@Input() hero!: Hero;
@Input() power = '';
// #enddocregion inputs
changeLog: string[] = [];

View File

@ -28,7 +28,7 @@ import { LoggerService } from './logger.service';
export class PeekABooParentComponent {
hasChild = false;
hookLog: string[];
hookLog: string[] = [];
heroName = 'Windstorm';
private logger: LoggerService;

View File

@ -28,7 +28,7 @@ export class PeekABooComponent extends PeekABooDirective implements
AfterContentInit, AfterContentChecked,
AfterViewInit, AfterViewChecked,
OnDestroy {
@Input() name: string;
@Input() name = '';
private verb = 'initialized';

View File

@ -1,7 +1,7 @@
// #docregion
import { Component } from '@angular/core';
import { heroes } from './hero';
import { Hero, heroes } from './hero';
@Component({
selector: 'app-root',
@ -10,7 +10,7 @@ import { heroes } from './hero';
})
export class AppComponent {
heroes = heroes;
hero = this.heroes[0];
hero: Hero | null = this.heroes[0];
heroTraits = ['honest', 'brave', 'considerate'];
// flags for the table

View File

@ -8,7 +8,7 @@ import { Hero } from './hero';
template: `Wow. You like {{hero.name}}. What a happy hero ... just like you.`
})
export class HappyHeroComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
}
@Component({
@ -16,7 +16,7 @@ export class HappyHeroComponent {
template: `You like {{hero.name}}? Such a sad hero. Are you sad too?`
})
export class SadHeroComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
}
@Component({
@ -24,7 +24,7 @@ export class SadHeroComponent {
template: `Are you as confused as {{hero.name}}?`
})
export class ConfusedHeroComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
}
@Component({
@ -32,7 +32,7 @@ export class ConfusedHeroComponent {
template: `{{message}}`
})
export class UnknownHeroComponent {
@Input() hero: Hero;
@Input() hero!: Hero;
get message() {
return this.hero && this.hero.name
? `${this.hero.name} is strange and mysterious.`

Some files were not shown because too many files have changed in this diff Show More