build(docs-infra): implement full template checking for examples (#40930)

This commit turns on full template type checking and fixes the examples
that had errors.

PR Close #40930
This commit is contained in:
Pete Bacon Darwin 2021-02-20 16:30:31 +00:00 committed by Zach Arend
parent 03d92f75e6
commit 4859c0947a
13 changed files with 51 additions and 20 deletions

View File

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

View File

@ -114,6 +114,9 @@ export class AppComponent implements OnInit {
trackById(index: number, item: any): number { return item.id; } trackById(index: number, item: any): number { return item.id; }
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
}
} }

View File

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

View File

@ -11,7 +11,7 @@ export class AppComponent {
currentItem = { name: 'teapot'} ; currentItem = { name: 'teapot'} ;
clickMessage = ''; clickMessage = '';
onSave(event?: KeyboardEvent) { onSave(event?: MouseEvent) {
const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : ''; const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : '';
alert('Saved.' + evtMsg); alert('Saved.' + evtMsg);
if (event) { event.stopPropagation(); } if (event) { event.stopPropagation(); }
@ -21,9 +21,14 @@ export class AppComponent {
alert(`Delete the ${item.name}.`); alert(`Delete the ${item.name}.`);
} }
onClickMe(event?: KeyboardEvent) { onClickMe(event?: MouseEvent) {
const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : ''; const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
alert('Click me.' + evtMsg); alert('Click me.' + evtMsg);
} }
// #docregion getValue
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
}
// #enddocregion getValue
} }

View File

@ -1,14 +1,15 @@
// #docplaster // #docplaster
// #docregion import-http // #docregion import-http
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
// #enddocregion import-http // #enddocregion import-http
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
// #docregion props, methods, inject-http, get-shipping // #docregion props, methods, inject-http, get-shipping
export class CartService { export class CartService {
// #enddocregion get-shipping
items = []; items = [];
// #enddocregion props, methods // #enddocregion props, methods
@ -32,8 +33,10 @@ export class CartService {
} }
// #enddocregion methods // #enddocregion methods
// #docregion get-shipping
getShippingPrices() { getShippingPrices() {
return this.http.get('/assets/shipping.json'); return this.http.get<{type: string, price: number}[]>('/assets/shipping.json');
} }
// #docregion props, methods, inject-http // #docregion props, methods, inject-http
} }
// #enddocregion props, methods, inject-http

View File

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

View File

@ -35,4 +35,9 @@ export class PackageSearchComponent implements OnInit {
toggleRefresh() { this.withRefresh = ! this.withRefresh; } toggleRefresh() { this.withRefresh = ! this.withRefresh; }
// #docregion getValue
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
}
// #enddocregion getValue
} }

View File

@ -3,11 +3,11 @@
// #docregion pure // #docregion pure
import { Pipe, PipeTransform } from '@angular/core'; import { Pipe, PipeTransform } from '@angular/core';
import { Flyer } from './heroes'; import { Hero } from './heroes';
@Pipe({ name: 'flyingHeroes' }) @Pipe({ name: 'flyingHeroes' })
export class FlyingHeroesPipe implements PipeTransform { export class FlyingHeroesPipe implements PipeTransform {
transform(allHeroes: Flyer[]) { transform(allHeroes: Hero[]) {
// #docregion filter // #docregion filter
return allHeroes.filter(hero => hero.canFly); return allHeroes.filter(hero => hero.canFly);
// #enddocregion filter // #enddocregion filter

View File

@ -1,5 +1,5 @@
export interface Flyer { canFly: boolean; } export interface Hero { name: string; canFly: boolean; }
export const HEROES = [ export const HEROES: Hero[] = [
{name: 'Windstorm', canFly: true}, {name: 'Windstorm', canFly: true},
{name: 'Bombasto', canFly: false}, {name: 'Bombasto', canFly: false},
{name: 'Magneto', canFly: false}, {name: 'Magneto', canFly: false},

View File

@ -219,11 +219,9 @@ button</button>
<app-hero-detail [hero]="currentHero"></app-hero-detail> <app-hero-detail [hero]="currentHero"></app-hero-detail>
<img bind-src="heroImageUrl"> <img bind-src="heroImageUrl">
<!-- ERROR: HeroDetailComponent.hero expects a <!-- ERROR: HeroDetailComponent.hero expects a Hero object, not the string "currentHero" -->
Hero object, not the string "currentHero" --> <!-- <app-hero-detail hero="currentHero"></app-hero-detail> -->
<div *ngIf="false">
<app-hero-detail hero="currentHero"></app-hero-detail>
</div>
<app-hero-detail prefix="You are my" [hero]="currentHero"></app-hero-detail> <app-hero-detail prefix="You are my" [hero]="currentHero"></app-hero-detail>
<p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p> <p><img src="{{heroImageUrl}}"> is the <i>interpolated</i> image.</p>

View File

@ -25,7 +25,15 @@ With this example, the following actions occur:
1. The code binds to the `input` event of the `<input>` element, which allows the code to listen for changes. 1. The code binds to the `input` event of the `<input>` element, which allows the code to listen for changes.
1. When the user makes changes, the component raises the `input` event. 1. When the user makes changes, the component raises the `input` event.
1. The binding executes the statement within a context that includes the DOM event object, `$event`. 1. The binding executes the statement within a context that includes the DOM event object, `$event`.
1. Angular retrieves the changed text by following the path `$event.target.value` and updates the `name` property. 1. Angular retrieves the changed text by calling `getValue($event.target)` and updates the `name` property.
If the event belongs to a directive or component, `$event` has the shape that the directive or component produces. If the event belongs to a directive or component, `$event` has the shape that the directive or component produces.
<div class="alert is-helpful">
The type of `$event.target` is only `EventTarget` in the template.
In the `getValue()` method, the target is cast to an `HTMLInputElement` to allow type-safe access to its `value` property.
<code-example path="event-binding/src/app/app.component.ts" region="getValue"></code-example>
</div>

View File

@ -978,6 +978,16 @@ a search request for a package with that name to the npm web API.
</code-example> </code-example>
Here, the `keyup` event binding sends every keystroke to the component's `search()` method. Here, the `keyup` event binding sends every keystroke to the component's `search()` method.
<div class="alert is-helpful">
The type of `$event.target` is only `EventTarget` in the template.
In the `getValue()` method, the target is cast to an `HTMLInputElement` to allow type-safe access to its `value` property.
<code-example path="http/src/app/package-search/package-search.component.ts" region="getValue"></code-example>
</div>
The following snippet implements debouncing for this input using RxJS operators. The following snippet implements debouncing for this input using RxJS operators.
<code-example <code-example

View File

@ -25,7 +25,6 @@
"angularCompilerOptions": { "angularCompilerOptions": {
"strictInjectionParameters": true, "strictInjectionParameters": true,
"strictInputAccessModifiers": true, "strictInputAccessModifiers": true,
// TODO(gkalpak): Fix the code and enable this (i.e. switch from `fullTemplateTypeCheck` to `strictTemplates`). "strictTemplates": true
"fullTemplateTypeCheck": true,// "strictTemplates": true
} }
} }