diff --git a/aio/content/examples/template-syntax/e2e/src/app.e2e-spec.ts b/aio/content/examples/template-syntax/e2e/src/app.e2e-spec.ts index 71f1c581650..6483774f103 100644 --- a/aio/content/examples/template-syntax/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/template-syntax/e2e/src/app.e2e-spec.ts @@ -2,42 +2,49 @@ import { browser, element, by } from 'protractor'; -// Not yet complete -describe('Template Syntax', function () { +// TODO Not yet complete +describe('Template Syntax', () => { - beforeAll(function () { + beforeAll(() => { browser.get(''); }); - it('should be able to use interpolation with a hero', function () { - let heroInterEle = element.all(by.css('h2+p')).get(0); + it('should be able to use interpolation with a hero', () => { + const heroInterEle = element.all(by.css('h2+p')).get(0); expect(heroInterEle.getText()).toEqual('My current hero is Hercules'); }); - it('should be able to use interpolation with a calculation', function () { - let theSumEles = element.all(by.cssContainingText('h3~p', 'The sum of')); + it('should be able to use interpolation with a calculation', () => { + const theSumEles = element.all(by.cssContainingText('h3~p', 'The sum of')); expect(theSumEles.count()).toBe(2); expect(theSumEles.get(0).getText()).toEqual('The sum of 1 + 1 is 2'); expect(theSumEles.get(1).getText()).toEqual('The sum of 1 + 1 is not 4'); }); - it('should be able to use class binding syntax', function () { - let specialEle = element(by.cssContainingText('div', 'Special')); + it('should be able to use class binding syntax', () => { + const specialEle = element(by.cssContainingText('div', 'Special')); expect(specialEle.getAttribute('class')).toMatch('special'); }); - it('should be able to use style binding syntax', function () { - let specialButtonEle = element(by.cssContainingText('div.special~button', 'button')); + it('should be able to use style binding syntax', () => { + const specialButtonEle = element(by.cssContainingText('div.special~button', 'button')); expect(specialButtonEle.getAttribute('style')).toMatch('color: red'); }); it('should two-way bind to sizer', async () => { - let div = element(by.css('div#two-way-1')); - let incButton = div.element(by.buttonText('+')); - let input = div.element(by.css('input')); - let initSize = await input.getAttribute('value'); + const div = element(by.css('div#two-way-1')); + const incButton = div.element(by.buttonText('+')); + const input = div.element(by.css('input')); + const initSize = await input.getAttribute('value'); incButton.click(); expect(input.getAttribute('value')).toEqual((+initSize + 1).toString()); }); -}); + it('should change SVG rectangle\'s fill color on click', async () => { + const div = element(by.css('app-svg')); + const colorSquare = div.element(by.css('rect')); + const initialColor = await colorSquare.getAttribute('fill'); + colorSquare.click(); + expect(colorSquare.getAttribute('fill')).not.toEqual(initialColor); + }); +}); diff --git a/aio/content/examples/template-syntax/src/app/app.component.html b/aio/content/examples/template-syntax/src/app/app.component.html index fc5b36d35a3..ca61c008968 100644 --- a/aio/content/examples/template-syntax/src/app/app.component.html +++ b/aio/content/examples/template-syntax/src/app/app.component.html @@ -38,6 +38,7 @@ Safe navigation operator ?.
Non-null assertion operator !.
Enums
+SVG Templates

Interpolation

@@ -442,7 +443,7 @@ button + (input)="updateCurrentHeroName($event)"> without NgModel
@@ -752,7 +753,7 @@ bindon-ngModel
- {{product.price | currency:'USD':true}} + {{product.price | currency:'USD':'symbol'}}
top @@ -857,3 +858,9 @@ The null hero's name is {{nullHero && nullHero.name}}

top + +

SVG Templates

+ + + +top diff --git a/aio/content/examples/template-syntax/src/app/app.component.ts b/aio/content/examples/template-syntax/src/app/app.component.ts index 0758ba65166..d39e2408954 100644 --- a/aio/content/examples/template-syntax/src/app/app.component.ts +++ b/aio/content/examples/template-syntax/src/app/app.component.ts @@ -5,7 +5,7 @@ import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } import { Hero } from './hero'; -export enum Color {Red, Green, Blue}; +export enum Color {Red, Green, Blue} /** * Giant grab bag of stuff to drive the chapter @@ -29,7 +29,7 @@ export class AppComponent implements AfterViewInit, OnInit { trackChanges(this.heroesWithTrackBy, () => this.heroesWithTrackByCount++); } - @ViewChildren('noTrackBy') heroesNoTrackBy: QueryList; + @ViewChildren('noTrackBy') heroesNoTrackBy: QueryList; @ViewChildren('withTrackBy') heroesWithTrackBy: QueryList; actionName = 'Go for it'; @@ -66,6 +66,10 @@ export class AppComponent implements AfterViewInit, OnInit { currentHero: Hero; + updateCurrentHeroName(event: Event) { + this.currentHero.name = (event.target as any).value; + } + deleteHero(hero?: Hero) { this.alert(`Delete ${hero ? hero.name : 'the hero'}.`); } @@ -105,13 +109,13 @@ export class AppComponent implements AfterViewInit, OnInit { get nullHero(): Hero { return null; } - onClickMe(event?: KeyboardEvent) { - let evtMsg = event ? ' Event target class is ' + (event.target).className : ''; + onClickMe(event?: MouseEvent) { + const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : ''; this.alert('Click me.' + evtMsg); } - onSave(event?: KeyboardEvent) { - let evtMsg = event ? ' Event target is ' + (event.target).textContent : ''; + onSave(event?: MouseEvent) { + const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : ''; this.alert('Saved.' + evtMsg); if (event) { event.stopPropagation(); } } @@ -140,9 +144,9 @@ export class AppComponent implements AfterViewInit, OnInit { setCurrentClasses() { // CSS classes: added/removed per current state of component properties this.currentClasses = { - 'saveable': this.canSave, - 'modified': !this.isUnchanged, - 'special': this.isSpecial + saveable: this.canSave, + modified: !this.isUnchanged, + special: this.isSpecial }; } // #enddocregion setClasses @@ -164,7 +168,7 @@ export class AppComponent implements AfterViewInit, OnInit { // #enddocregion trackByHeroes // #docregion trackById - trackById(index: number, item: any): number { return item['id']; } + trackById(index: number, item: any): number { return item.id; } // #enddocregion trackById } diff --git a/aio/content/examples/template-syntax/src/app/app.module.ts b/aio/content/examples/template-syntax/src/app/app.module.ts index 55e359ccd2e..ca7d5dc68aa 100644 --- a/aio/content/examples/template-syntax/src/app/app.module.ts +++ b/aio/content/examples/template-syntax/src/app/app.module.ts @@ -1,14 +1,14 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { BigHeroDetailComponent, HeroDetailComponent } from './hero-detail.component'; import { ClickDirective, ClickDirective2 } from './click.directive'; -import { HeroFormComponent } from './hero-form.component'; -import { heroSwitchComponents } from './hero-switch.components'; -import { SizerComponent } from './sizer.component'; -import { SvgComponent } from './svg.component'; +import { HeroFormComponent } from './hero-form.component'; +import { heroSwitchComponents } from './hero-switch.components'; +import { SizerComponent } from './sizer.component'; +import { SvgComponent } from './svg.component'; @NgModule({ imports: [ diff --git a/aio/content/examples/template-syntax/src/app/click.directive.ts b/aio/content/examples/template-syntax/src/app/click.directive.ts index ef9d68f0328..e2eeeee1056 100644 --- a/aio/content/examples/template-syntax/src/app/click.directive.ts +++ b/aio/content/examples/template-syntax/src/app/click.directive.ts @@ -1,4 +1,4 @@ -/* tslint:disable use-output-property-decorator directive-class-suffix */ +/* tslint:disable directive-selector directive-class-suffix */ // #docplaster import { Directive, ElementRef, EventEmitter, Output } from '@angular/core'; diff --git a/aio/content/examples/template-syntax/src/app/hero-form.component.ts b/aio/content/examples/template-syntax/src/app/hero-form.component.ts index 173cc137de5..2b1a48666f3 100644 --- a/aio/content/examples/template-syntax/src/app/hero-form.component.ts +++ b/aio/content/examples/template-syntax/src/app/hero-form.component.ts @@ -1,5 +1,5 @@ import { Component, Input, ViewChild } from '@angular/core'; -import { NgForm } from '@angular/forms'; +import { NgForm } from '@angular/forms'; import { Hero } from './hero'; @@ -15,10 +15,11 @@ export class HeroFormComponent { @Input() hero: Hero; @ViewChild('heroForm', {static: false}) form: NgForm; + // tslint:disable-next-line:variable-name private _submitMessage = ''; get submitMessage() { - if (!this.form.valid) { + if (this.form && !this.form.valid) { this._submitMessage = ''; } return this._submitMessage; diff --git a/aio/content/examples/template-syntax/src/app/svg.component.svg b/aio/content/examples/template-syntax/src/app/svg.component.svg index 62431475a02..634b66df8ca 100644 --- a/aio/content/examples/template-syntax/src/app/svg.component.svg +++ b/aio/content/examples/template-syntax/src/app/svg.component.svg @@ -1,6 +1,6 @@ - + click the rectangle to change the fill color diff --git a/aio/content/examples/template-syntax/src/app/svg.component.ts b/aio/content/examples/template-syntax/src/app/svg.component.ts index d3f80e143ac..7be251a8efa 100644 --- a/aio/content/examples/template-syntax/src/app/svg.component.ts +++ b/aio/content/examples/template-syntax/src/app/svg.component.ts @@ -6,12 +6,12 @@ import { Component } from '@angular/core'; styleUrls: ['./svg.component.css'] }) export class SvgComponent { - fill = 'rgb(255, 0, 0)'; + fillColor = 'rgb(255, 0, 0)'; changeColor() { const r = Math.floor(Math.random() * 256); const g = Math.floor(Math.random() * 256); const b = Math.floor(Math.random() * 256); - this.fill = `rgb(${r}, ${g}, ${b})`; + this.fillColor = `rgb(${r}, ${g}, ${b})`; } } diff --git a/aio/content/guide/template-syntax.md b/aio/content/guide/template-syntax.md index bf11a8e97a7..2bc7bc2752e 100644 --- a/aio/content/guide/template-syntax.md +++ b/aio/content/guide/template-syntax.md @@ -2276,12 +2276,14 @@ The `$any()` cast function works anywhere in a binding expression where a method ## SVG in templates -It is possible to use SVG as valid templates in Angular. All of the template syntax below is applicable to both SVG and HTML. -Learn more in the SVG [1.1](https://www.w3.org/TR/SVG11/) and [2.0](https://www.w3.org/TR/SVG2/) specifications. +It is possible to use SVG as valid templates in Angular. All of the template syntax below is +applicable to both SVG and HTML. Learn more in the SVG [1.1](https://www.w3.org/TR/SVG11/) and +[2.0](https://www.w3.org/TR/SVG2/) specifications. Why would you use SVG as template, instead of simply adding it as image to your application? -When you use an SVG as the template, you are able to use directives and bindings just like with HTML templates. This means that you will be able to dynamically generate interactive graphics. +When you use an SVG as the template, you are able to use directives and bindings just like with HTML +templates. This means that you will be able to dynamically generate interactive graphics. Refer to the sample code snippet below for a syntax example: @@ -2293,4 +2295,5 @@ Add the below code to your `svg.component.svg` file: -Here you can see the use of a `click()` event binding and the property binding syntax (`[attr.fill]="fill"`). +Here you can see the use of a `click()` event binding and the property binding syntax +(`[attr.fill]="fillColor"`).