docs(core): in template syntax guide, make SVG example more clear (#31356)
add e2e test for SVG template example fix template syntax example app - linting errors - runtime exceptions - template type errors - deprecated type casting - deprecated currency pipe example Relates to #30559 PR Close #31356
This commit is contained in:
parent
95a9d67599
commit
09970d52e8
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
|
||||
<a href="#non-null-assertion-operator">Non-null assertion operator <i>!.</i></a><br>
|
||||
<a href="#enums">Enums</a><br>
|
||||
<a href="#svg-templates">SVG Templates</a><br>
|
||||
|
||||
<!-- Interpolation and expressions -->
|
||||
<hr><h2 id="interpolation">Interpolation</h2>
|
||||
|
@ -442,7 +443,7 @@ button</button>
|
|||
|
||||
<!-- #docregion without-NgModel -->
|
||||
<input [value]="currentHero.name"
|
||||
(input)="currentHero.name=$event.target.value" >
|
||||
(input)="updateCurrentHeroName($event)">
|
||||
<!-- #enddocregion without-NgModel -->
|
||||
without NgModel
|
||||
<br>
|
||||
|
@ -752,7 +753,7 @@ bindon-ngModel
|
|||
|
||||
<div>
|
||||
<!-- pipe price to USD and display the $ symbol -->
|
||||
<label>Price: </label>{{product.price | currency:'USD':true}}
|
||||
<label>Price: </label>{{product.price | currency:'USD':'symbol'}}
|
||||
</div>
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
@ -857,3 +858,9 @@ The null hero's name is {{nullHero && nullHero.name}}
|
|||
</p>
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
||||
<hr><h2 id="svg-templates">SVG Templates</h2>
|
||||
<!-- #docregion svg-templates -->
|
||||
<app-svg></app-svg>
|
||||
<!-- #enddocregion svg-templates -->
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
|
|
@ -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<ElementRef>;
|
||||
@ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;
|
||||
@ViewChildren('withTrackBy') heroesWithTrackBy: QueryList<ElementRef>;
|
||||
|
||||
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 ' + (<HTMLElement>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 ' + (<HTMLElement>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
|
||||
}
|
||||
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<svg>
|
||||
<g>
|
||||
<rect x="0" y="0" width="100" height="100" [attr.fill]="fill" (click)="changeColor()" />
|
||||
<rect x="0" y="0" width="100" height="100" [attr.fill]="fillColor" (click)="changeColor()" />
|
||||
<text x="120" y="50">click the rectangle to change the fill color</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 201 B |
|
@ -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})`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
|||
<code-example path="template-syntax/src/app/svg.component.svg" header="src/app/svg.component.svg">
|
||||
</code-example>
|
||||
|
||||
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"`).
|
||||
|
|
Loading…
Reference in New Issue