1683 lines
89 KiB
HTML
1683 lines
89 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.html" target="_self"><input type="hidden" name="files[src/app/app.component.ts]" value="/* tslint:disable:forin member-ordering */
|
||
|
|
||
|
import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||
|
|
||
|
import { Hero } from './hero';
|
||
|
|
||
|
export enum Color {Red, Green, Blue}
|
||
|
|
||
|
/**
|
||
|
* Giant grab bag of stuff to drive the chapter
|
||
|
*/
|
||
|
@Component({
|
||
|
selector: 'app-root',
|
||
|
templateUrl: './app.component.html',
|
||
|
styleUrls: [ './app.component.css' ]
|
||
|
})
|
||
|
export class AppComponent implements AfterViewInit, OnInit {
|
||
|
|
||
|
ngOnInit() {
|
||
|
this.resetHeroes();
|
||
|
this.setCurrentClasses();
|
||
|
this.setCurrentStyles();
|
||
|
}
|
||
|
|
||
|
ngAfterViewInit() {
|
||
|
// Detect effects of NgForTrackBy
|
||
|
trackChanges(this.heroesNoTrackBy, () => this.heroesNoTrackByCount++);
|
||
|
trackChanges(this.heroesWithTrackBy, () => this.heroesWithTrackByCount++);
|
||
|
}
|
||
|
|
||
|
@ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;
|
||
|
@ViewChildren('withTrackBy') heroesWithTrackBy: QueryList<ElementRef>;
|
||
|
|
||
|
actionName = 'Go for it';
|
||
|
badCurly = 'bad curly';
|
||
|
classes = 'special';
|
||
|
help = '';
|
||
|
|
||
|
alert(msg?: string) { window.alert(msg); }
|
||
|
callFax(value: string) { this.alert(`Faxing ${value} ...`); }
|
||
|
callPhone(value: string) { this.alert(`Calling ${value} ...`); }
|
||
|
canSave = true;
|
||
|
|
||
|
changeIds() {
|
||
|
this.resetHeroes();
|
||
|
this.heroes.forEach(h => h.id += 10 * this.heroIdIncrement++);
|
||
|
this.heroesWithTrackByCountReset = -1;
|
||
|
}
|
||
|
|
||
|
clearTrackByCounts() {
|
||
|
const trackByCountReset = this.heroesWithTrackByCountReset;
|
||
|
this.resetHeroes();
|
||
|
this.heroesNoTrackByCount = -1;
|
||
|
this.heroesWithTrackByCount = trackByCountReset;
|
||
|
this.heroIdIncrement = 1;
|
||
|
}
|
||
|
|
||
|
clicked = '';
|
||
|
clickMessage = '';
|
||
|
clickMessage2 = '';
|
||
|
|
||
|
Color = Color;
|
||
|
color = Color.Red;
|
||
|
colorToggle() {this.color = (this.color === Color.Red) ? Color.Blue : Color.Red; }
|
||
|
|
||
|
currentHero: Hero;
|
||
|
|
||
|
updateCurrentHeroName(event: Event) {
|
||
|
this.currentHero.name = (event.target as any).value;
|
||
|
}
|
||
|
|
||
|
deleteHero(hero?: Hero) {
|
||
|
this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
|
||
|
}
|
||
|
|
||
|
evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
|
||
|
|
||
|
fontSizePx = 16;
|
||
|
|
||
|
title = 'Template Syntax';
|
||
|
|
||
|
getVal(): number { return 2; }
|
||
|
|
||
|
name: string = Hero.heroes[0].name;
|
||
|
hero: Hero; // defined to demonstrate template context precedence
|
||
|
heroes: Hero[];
|
||
|
|
||
|
// trackBy change counting
|
||
|
heroesNoTrackByCount = 0;
|
||
|
heroesWithTrackByCount = 0;
|
||
|
heroesWithTrackByCountReset = 0;
|
||
|
|
||
|
heroIdIncrement = 1;
|
||
|
|
||
|
// heroImageUrl = 'https://wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
|
||
|
// Public Domain terms of use: https://wpclipart.com/terms.html
|
||
|
heroImageUrl = 'assets/images/hero.png';
|
||
|
// villainImageUrl = 'https://www.clker.com/cliparts/u/s/y/L/x/9/villain-man-hi.png'
|
||
|
// Public Domain terms of use https://www.clker.com/disclaimer.html
|
||
|
villainImageUrl = 'assets/images/villain.png';
|
||
|
|
||
|
iconUrl = 'assets/images/ng-logo.png';
|
||
|
isActive = false;
|
||
|
isSpecial = true;
|
||
|
isUnchanged = true;
|
||
|
|
||
|
get nullHero(): Hero { return null; }
|
||
|
|
||
|
onClickMe(event?: MouseEvent) {
|
||
|
const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
|
||
|
this.alert('Click me.' + evtMsg);
|
||
|
}
|
||
|
|
||
|
onSave(event?: MouseEvent) {
|
||
|
const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : '';
|
||
|
this.alert('Saved.' + evtMsg);
|
||
|
if (event) { event.stopPropagation(); }
|
||
|
}
|
||
|
|
||
|
onSubmit(data: any) {/* referenced but not used */}
|
||
|
|
||
|
product = {
|
||
|
name: 'frimfram',
|
||
|
price: 42
|
||
|
};
|
||
|
|
||
|
// updates with fresh set of cloned heroes
|
||
|
resetHeroes() {
|
||
|
this.heroes = Hero.heroes.map(hero => hero.clone());
|
||
|
this.currentHero = this.heroes[0];
|
||
|
this.hero = this.currentHero;
|
||
|
this.heroesWithTrackByCountReset = 0;
|
||
|
}
|
||
|
|
||
|
setUppercaseName(name: string) {
|
||
|
this.currentHero.name = name.toUpperCase();
|
||
|
}
|
||
|
|
||
|
currentClasses: {};
|
||
|
setCurrentClasses() {
|
||
|
// CSS classes: added/removed per current state of component properties
|
||
|
this.currentClasses = {
|
||
|
saveable: this.canSave,
|
||
|
modified: !this.isUnchanged,
|
||
|
special: this.isSpecial
|
||
|
};
|
||
|
}
|
||
|
|
||
|
currentStyles: {};
|
||
|
setCurrentStyles() {
|
||
|
// CSS styles: set per current state of component properties
|
||
|
this.currentStyles = {
|
||
|
'font-style': this.canSave ? 'italic' : 'normal',
|
||
|
'font-weight': !this.isUnchanged ? 'bold' : 'normal',
|
||
|
'font-size': this.isSpecial ? '24px' : '12px'
|
||
|
};
|
||
|
}
|
||
|
|
||
|
trackByHeroes(index: number, hero: Hero): number { return hero.id; }
|
||
|
|
||
|
trackById(index: number, item: any): number { return item.id; }
|
||
|
}
|
||
|
|
||
|
// helper to track changes to viewChildren
|
||
|
function trackChanges(views: QueryList<ElementRef>, changed: () => void) {
|
||
|
let oldRefs = views.toArray();
|
||
|
views.changes.subscribe((changes: QueryList<ElementRef>) => {
|
||
|
const changedRefs = changes.toArray();
|
||
|
// Check if every changed Element is the same as old and in the same position
|
||
|
const isSame = oldRefs.every((v, i) => v.nativeElement === changedRefs[i].nativeElement);
|
||
|
if (!isSame) {
|
||
|
oldRefs = changedRefs;
|
||
|
// wait a tick because called after views are constructed
|
||
|
setTimeout(changed, 0);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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.1.ts]" value="import { NgModule } from '@angular/core';
|
||
|
import { BrowserModule } from '@angular/platform-browser';
|
||
|
import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
|
||
|
|
||
|
/* Other imports */
|
||
|
|
||
|
@NgModule({
|
||
|
imports: [
|
||
|
BrowserModule,
|
||
|
FormsModule // <--- import into the NgModule
|
||
|
],
|
||
|
/* Other module metadata */
|
||
|
})
|
||
|
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/app.module.ts]" value="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';
|
||
|
|
||
|
@NgModule({
|
||
|
imports: [
|
||
|
BrowserModule,
|
||
|
FormsModule
|
||
|
],
|
||
|
declarations: [
|
||
|
AppComponent,
|
||
|
BigHeroDetailComponent,
|
||
|
HeroDetailComponent,
|
||
|
HeroFormComponent,
|
||
|
heroSwitchComponents,
|
||
|
ClickDirective,
|
||
|
ClickDirective2,
|
||
|
SizerComponent,
|
||
|
SvgComponent
|
||
|
],
|
||
|
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/click.directive.ts]" value="/* tslint:disable directive-selector directive-class-suffix */
|
||
|
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
|
||
|
|
||
|
@Directive({selector: '[myClick]'})
|
||
|
export class ClickDirective {
|
||
|
@Output('myClick') clicks = new EventEmitter<string>(); // @Output(alias) propertyName = ...
|
||
|
|
||
|
toggle = false;
|
||
|
|
||
|
constructor(el: ElementRef) {
|
||
|
el.nativeElement
|
||
|
.addEventListener('click', (event: Event) => {
|
||
|
this.toggle = !this.toggle;
|
||
|
this.clicks.emit(this.toggle ? 'Click!' : '');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Directive({
|
||
|
selector: '[myClick2]',
|
||
|
// tslint:disable: no-outputs-metadata-property
|
||
|
outputs: ['clicks:myClick'] // propertyName:alias
|
||
|
})
|
||
|
// tslint:enable: no-outputs-metadata-property
|
||
|
export class ClickDirective2 {
|
||
|
clicks = new EventEmitter<string>();
|
||
|
toggle = false;
|
||
|
|
||
|
constructor(el: ElementRef) {
|
||
|
el.nativeElement
|
||
|
.addEventListener('click', (event: Event) => {
|
||
|
this.toggle = !this.toggle;
|
||
|
this.clicks.emit(this.toggle ? 'Click2!' : '');
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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/hero-detail.component.ts]" value="/* tslint:disable use-input-property-decorator use-output-property-decorator */
|
||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||
|
|
||
|
import { Hero } from './hero';
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-hero-detail',
|
||
|
// tslint:disable: no-inputs-metadata-property no-outputs-metadata-property
|
||
|
inputs: ['hero'],
|
||
|
outputs: ['deleteRequest'],
|
||
|
// tslint:enable: no-inputs-metadata-property no-outputs-metadata-property
|
||
|
styles: ['button {margin-left: 8px} div {margin: 8px 0} img {height:24px}'],
|
||
|
template: `
|
||
|
<div>
|
||
|
<img src="{{heroImageUrl}}">
|
||
|
<span [style.text-decoration]="lineThrough">
|
||
|
{{prefix}} {{hero?.name}}
|
||
|
</span>
|
||
|
<button (click)="delete()">Delete</button>
|
||
|
</div>`
|
||
|
})
|
||
|
export class HeroDetailComponent {
|
||
|
hero: Hero = new Hero(-1, '', 'Zzzzzzzz'); // default sleeping hero
|
||
|
// heroImageUrl = 'https://wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
|
||
|
// Public Domain terms of use: https://wpclipart.com/terms.html
|
||
|
heroImageUrl = 'assets/images/hero.png';
|
||
|
lineThrough = '';
|
||
|
@Input() prefix = '';
|
||
|
|
||
|
// This component makes a request but it can't actually delete a hero.
|
||
|
deleteRequest = new EventEmitter<Hero>();
|
||
|
|
||
|
delete() {
|
||
|
this.deleteRequest.emit(this.hero);
|
||
|
this.lineThrough = this.lineThrough ? '' : 'line-through';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-big-hero-detail',
|
||
|
template: `
|
||
|
<div class="detail">
|
||
|
<img src="{{heroImageUrl}}">
|
||
|
<div><b>{{hero?.name}}</b></div>
|
||
|
<div>Name: {{hero?.name}}</div>
|
||
|
<div>Emotion: {{hero?.emotion}}</div>
|
||
|
<div>Birthdate: {{hero?.birthdate | date:'longDate'}}</div>
|
||
|
<div>Web: <a href="{{hero?.url}}" target="_blank">{{hero?.url}}</a></div>
|
||
|
<div>Rate/hr: {{hero?.rate | currency:'EUR'}}</div>
|
||
|
<br clear="all">
|
||
|
<button (click)="delete()">Delete</button>
|
||
|
</div>
|
||
|
`,
|
||
|
styles: [`
|
||
|
.detail { border: 1px solid black; padding: 4px; max-width: 450px; }
|
||
|
img { float: left; margin-right: 8px; height: 100px; }
|
||
|
`]
|
||
|
})
|
||
|
export class BigHeroDetailComponent extends HeroDetailComponent {
|
||
|
|
||
|
@Input() hero: Hero;
|
||
|
@Output() deleteRequest = new EventEmitter<Hero>();
|
||
|
|
||
|
delete() {
|
||
|
this.deleteRequest.emit(this.hero);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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/hero-form.component.ts]" value="import { Component, Input, ViewChild } from '@angular/core';
|
||
|
import { NgForm } from '@angular/forms';
|
||
|
|
||
|
import { Hero } from './hero';
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-hero-form',
|
||
|
templateUrl: './hero-form.component.html',
|
||
|
styles: [`
|
||
|
button { margin: 6px 0; }
|
||
|
#heroForm { border: 1px solid black; margin: 20px 0; padding: 8px; max-width: 350px; }
|
||
|
`]
|
||
|
})
|
||
|
export class HeroFormComponent {
|
||
|
@Input() hero: Hero;
|
||
|
@ViewChild('heroForm') form: NgForm;
|
||
|
|
||
|
// tslint:disable-next-line:variable-name
|
||
|
private _submitMessage = '';
|
||
|
|
||
|
get submitMessage() {
|
||
|
if (this.form && !this.form.valid) {
|
||
|
this._submitMessage = '';
|
||
|
}
|
||
|
return this._submitMessage;
|
||
|
}
|
||
|
|
||
|
onSubmit(form: NgForm) {
|
||
|
this._submitMessage = 'Submitted. form value is ' + JSON.stringify(form.value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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/hero-switch.components.ts]" value="import { Component, Input } from '@angular/core';
|
||
|
import { Hero } from './hero';
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-happy-hero',
|
||
|
template: `Wow. You like {{hero.name}}. What a happy hero ... just like you.`
|
||
|
})
|
||
|
export class HappyHeroComponent {
|
||
|
@Input() hero: Hero;
|
||
|
}
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-sad-hero',
|
||
|
template: `You like {{hero.name}}? Such a sad hero. Are you sad too?`
|
||
|
})
|
||
|
export class SadHeroComponent {
|
||
|
@Input() hero: Hero;
|
||
|
}
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-confused-hero',
|
||
|
template: `Are you as confused as {{hero.name}}?`
|
||
|
})
|
||
|
export class ConfusedHeroComponent {
|
||
|
@Input() hero: Hero;
|
||
|
}
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-unknown-hero',
|
||
|
template: `{{message}}`
|
||
|
})
|
||
|
export class UnknownHeroComponent {
|
||
|
@Input() hero: Hero;
|
||
|
get message() {
|
||
|
return this.hero && this.hero.name ?
|
||
|
`${this.hero.name} is strange and mysterious.` :
|
||
|
'Are you feeling indecisive?';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const heroSwitchComponents =
|
||
|
[ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
|
||
|
|
||
|
|
||
|
/*
|
||
|
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/hero.ts]" value="export class Hero {
|
||
|
static nextId = 0;
|
||
|
|
||
|
static heroes: Hero[] = [
|
||
|
new Hero(
|
||
|
null,
|
||
|
'Hercules',
|
||
|
'happy',
|
||
|
new Date(1970, 1, 25),
|
||
|
'https://www.imdb.com/title/tt0065832/',
|
||
|
325
|
||
|
),
|
||
|
new Hero(1, 'Dr Nice', 'happy'),
|
||
|
new Hero(2, 'Narco', 'sad' ),
|
||
|
new Hero(3, 'Windstorm', 'confused' ),
|
||
|
new Hero(4, 'Magneta')
|
||
|
];
|
||
|
|
||
|
|
||
|
constructor(
|
||
|
public id?: number,
|
||
|
public name?: string,
|
||
|
public emotion?: string,
|
||
|
public birthdate?: Date,
|
||
|
public url?: string,
|
||
|
public rate = 100,
|
||
|
) {
|
||
|
this.id = id ? id : Hero.nextId++;
|
||
|
}
|
||
|
|
||
|
clone(): Hero {
|
||
|
return Object.assign(new Hero(), this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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/sizer.component.ts]" value="import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-sizer',
|
||
|
template: `
|
||
|
<div>
|
||
|
<button (click)="dec()" title="smaller">-</button>
|
||
|
<button (click)="inc()" title="bigger">+</button>
|
||
|
<label [style.font-size.px]="size">FontSize: {{size}}px</label>
|
||
|
</div>`
|
||
|
})
|
||
|
export class SizerComponent {
|
||
|
@Input() size: number | string;
|
||
|
@Output() sizeChange = new EventEmitter<number>();
|
||
|
|
||
|
dec() { this.resize(-1); }
|
||
|
inc() { this.resize(+1); }
|
||
|
|
||
|
resize(delta: number) {
|
||
|
this.size = Math.min(40, Math.max(8, +this.size + delta));
|
||
|
this.sizeChange.emit(this.size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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/svg.component.ts]" value="import { Component } from '@angular/core';
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-svg',
|
||
|
templateUrl: './svg.component.svg',
|
||
|
styleUrls: ['./svg.component.css']
|
||
|
})
|
||
|
export class SvgComponent {
|
||
|
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.fillColor = `rgb(${r}, ${g}, ${b})`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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 "evergreen" 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/app/app.component.css]" value="a.to-toc { margin: 30px 0; }
|
||
|
button { font-size: 100%; margin: 0 2px; }
|
||
|
div[clickable] {cursor: pointer; max-width: 200px; margin: 16px 0}
|
||
|
#noTrackByCnt, #withTrackByCnt {color: darkred; max-width: 450px; margin: 4px;}
|
||
|
img {height: 100px;}
|
||
|
.box {border: 1px solid black; padding: 6px; max-width: 450px;}
|
||
|
.child-div {margin-left: 1em; font-weight: normal}
|
||
|
.context {margin-left: 1em;}
|
||
|
.hidden {display: none}
|
||
|
.parent-div {margin-top: 1em; font-weight: bold}
|
||
|
.special {font-weight:bold; font-size: x-large}
|
||
|
.bad {color: red;}
|
||
|
.saveable {color: limegreen;}
|
||
|
.curly, .modified {font-family: "Brush Script MT", cursive}
|
||
|
.toe {margin-left: 1em; font-style: italic;}
|
||
|
little-hero {color:blue; font-size: smaller; background-color: Turquoise }
|
||
|
.to-toc {margin-top: 10px; display: block}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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/svg.component.css]" value="svg {
|
||
|
display: block;
|
||
|
width: 100%;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
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="text"] {
|
||
|
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/app/app.component.html]" value="<a id="toc"></a>
|
||
|
<h1>Template Syntax</h1>
|
||
|
<a href="#interpolation">Interpolation</a><br>
|
||
|
<a href="#expression-context">Expression context</a><br>
|
||
|
<a href="#statement-context">Statement context</a><br>
|
||
|
<a href="#mental-model">Mental Model</a><br>
|
||
|
<a href="#buttons">Buttons</a><br>
|
||
|
<a href="#prop-vs-attrib">Properties vs. Attributes</a><br>
|
||
|
<br>
|
||
|
<a href="#property-binding">Property Binding</a><br>
|
||
|
<div style="margin-left:8px">
|
||
|
<a href="#attribute-binding">Attribute Binding</a><br>
|
||
|
<a href="#class-binding">Class Binding</a><br>
|
||
|
<a href="#style-binding">Style Binding</a><br>
|
||
|
</div>
|
||
|
<br>
|
||
|
<a href="#event-binding">Event Binding</a><br>
|
||
|
<a href="#two-way">Two-way Binding</a><br>
|
||
|
<br>
|
||
|
<div>Directives</div>
|
||
|
<div style="margin-left:8px">
|
||
|
<a href="#ngModel">NgModel (two-way) Binding</a><br>
|
||
|
<a href="#ngClass">NgClass Binding</a><br>
|
||
|
<a href="#ngStyle">NgStyle Binding</a><br>
|
||
|
<a href="#ngIf">NgIf</a><br>
|
||
|
<a href="#ngFor">NgFor</a><br>
|
||
|
<div style="margin-left:8px">
|
||
|
<a href="#ngFor-index">NgFor with index</a><br>
|
||
|
<a href="#ngFor-trackBy">NgFor with trackBy</a><br>
|
||
|
</div>
|
||
|
<a href="#ngSwitch">NgSwitch</a><br>
|
||
|
</div>
|
||
|
<br>
|
||
|
<a href="#ref-vars">Template reference variables</a><br>
|
||
|
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
|
||
|
<a href="#pipes">Pipes</a><br>
|
||
|
<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>
|
||
|
|
||
|
<p>My current hero is {{currentHero.name}}</p>
|
||
|
|
||
|
<h3>
|
||
|
{{title}}
|
||
|
<img src="{{heroImageUrl}}" style="height:30px">
|
||
|
</h3>
|
||
|
|
||
|
<!-- "The sum of 1 + 1 is 2" -->
|
||
|
<p>The sum of 1 + 1 is {{1 + 1}}</p>
|
||
|
|
||
|
<!-- "The sum of 1 + 1 is not 4" -->
|
||
|
<p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<hr><h2 id="expression-context">Expression context</h2>
|
||
|
|
||
|
<p>Component expression context (&#123;&#123;title&#125;&#125;, [hidden]="isUnchanged")</p>
|
||
|
<div class="context">
|
||
|
{{title}}
|
||
|
<span [hidden]="isUnchanged">changed</span>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<p>Template input variable expression context (let hero)</p>
|
||
|
<!-- template hides the following; plenty of examples later -->
|
||
|
<ng-template>
|
||
|
<div *ngFor="let hero of heroes">{{hero.name}}</div>
|
||
|
</ng-template>
|
||
|
|
||
|
<p>Template reference variable expression context (#heroInput)</p>
|
||
|
<div (keyup)="0" class="context">
|
||
|
Type something:
|
||
|
<input #heroInput> {{heroInput.value}}
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<hr><h2 id="statement-context">Statement context</h2>
|
||
|
|
||
|
<p>Component statement context ( (click)="onSave() )
|
||
|
<div class="context">
|
||
|
<button (click)="deleteHero()">Delete hero</button>
|
||
|
</div>
|
||
|
|
||
|
<p>Template $event statement context</p>
|
||
|
<div class="context">
|
||
|
<button (click)="onSave($event)">Save</button>
|
||
|
</div>
|
||
|
|
||
|
<p>Template input variable statement context (let hero)</p>
|
||
|
<!-- template hides the following; plenty of examples later -->
|
||
|
<div class="context">
|
||
|
<button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button>
|
||
|
</div>
|
||
|
|
||
|
<p>Template reference variable statement context (#heroForm)</p>
|
||
|
<div class="context">
|
||
|
<form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- New Mental Model -->
|
||
|
<hr><h2 id="mental-model">New Mental Model</h2>
|
||
|
|
||
|
<!--<img src="https://wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png">-->
|
||
|
<!-- Public Domain terms of use: https://wpclipart.com/terms.html -->
|
||
|
<div class="special">Mental Model</div>
|
||
|
<img src="assets/images/hero.png">
|
||
|
<button disabled>Save</button>
|
||
|
<br><br>
|
||
|
|
||
|
<div>
|
||
|
<!-- Normal HTML -->
|
||
|
<div class="special">Mental Model</div>
|
||
|
<!-- Wow! A new element! -->
|
||
|
<app-hero-detail></app-hero-detail>
|
||
|
</div>
|
||
|
<br><br>
|
||
|
|
||
|
<div>
|
||
|
<!-- Bind button disabled state to `isUnchanged` property -->
|
||
|
<button [disabled]="isUnchanged">Save</button>
|
||
|
</div>
|
||
|
<br><br>
|
||
|
|
||
|
<div>
|
||
|
<img [src]="heroImageUrl">
|
||
|
<app-hero-detail [hero]="currentHero"></app-hero-detail>
|
||
|
<div [ngClass]="{'special': isSpecial}"></div>
|
||
|
</div>
|
||
|
<br><br>
|
||
|
|
||
|
<button (click)="onSave()">Save</button>
|
||
|
<app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail>
|
||
|
<div (myClick)="clicked=$event" clickable>click me</div>
|
||
|
{{clicked}}
|
||
|
<br><br>
|
||
|
|
||
|
<div>
|
||
|
Hero Name:
|
||
|
<input [(ngModel)]="name">
|
||
|
{{name}}
|
||
|
</div>
|
||
|
<br><br>
|
||
|
|
||
|
<button [attr.aria-label]="help">help</button>
|
||
|
<br><br>
|
||
|
|
||
|
<div [class.special]="isSpecial">Special</div>
|
||
|
<br><br>
|
||
|
|
||
|
<button [style.color]="isSpecial ? 'red' : 'green'">
|
||
|
button</button>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- property vs. attribute -->
|
||
|
<hr><h2 id="prop-vs-attrib">Property vs. Attribute (img examples)</h2>
|
||
|
<!-- examine the following <img> tag in the browser tools -->
|
||
|
<img src="images/ng-logo.png"
|
||
|
[src]="heroImageUrl">
|
||
|
|
||
|
<br><br>
|
||
|
|
||
|
<img [src]="iconUrl"/>
|
||
|
<img bind-src="heroImageUrl"/>
|
||
|
<img [attr.src]="villainImageUrl"/>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- buttons -->
|
||
|
<hr><h2 id="buttons">Buttons</h2>
|
||
|
|
||
|
<button>Enabled (but does nothing)</button>
|
||
|
<button disabled>Disabled</button>
|
||
|
<button disabled=false>Still disabled</button>
|
||
|
<br><br>
|
||
|
<button disabled>disabled by attribute</button>
|
||
|
<button [disabled]="isUnchanged">disabled by property binding</button>
|
||
|
<br><br>
|
||
|
<button bind-disabled="isUnchanged" on-click="onSave($event)">Disabled Cancel</button>
|
||
|
<button [disabled]="!canSave" (click)="onSave($event)">Enabled Save</button>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- property binding -->
|
||
|
<hr><h2 id="property-binding">Property Binding</h2>
|
||
|
|
||
|
<img [src]="heroImageUrl">
|
||
|
<button [disabled]="isUnchanged">Cancel is disabled</button>
|
||
|
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
|
||
|
<app-hero-detail [hero]="currentHero"></app-hero-detail>
|
||
|
<img bind-src="heroImageUrl">
|
||
|
|
||
|
<!-- ERROR: HeroDetailComponent.hero expects a Hero object, not the string "currentHero" -->
|
||
|
<!-- <app-hero-detail 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>property bound</i> image.</p>
|
||
|
|
||
|
<p><span>"{{title}}" is the <i>interpolated</i> title.</span></p>
|
||
|
<p>"<span [innerHTML]="title"></span>" is the <i>property bound</i> title.</p>
|
||
|
|
||
|
<!--
|
||
|
Angular generates warnings for these two lines as it sanitizes them
|
||
|
WARNING: sanitizing HTML stripped some content (see https://g.co/ng/security#xss).
|
||
|
-->
|
||
|
<p><span>"{{evilTitle}}" is the <i>interpolated</i> evil title.</span></p>
|
||
|
<p>"<span [innerHTML]="evilTitle"></span>" is the <i>property bound</i> evil title.</p>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- attribute binding -->
|
||
|
<hr><h2 id="attribute-binding">Attribute Binding</h2>
|
||
|
|
||
|
<!-- create and set a colspan attribute -->
|
||
|
<table border=1>
|
||
|
<!-- expression calculates colspan=2 -->
|
||
|
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
|
||
|
|
||
|
<!-- ERROR: There is no `colspan` property to set!
|
||
|
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
|
||
|
-->
|
||
|
|
||
|
<tr><td>Five</td><td>Six</td></tr>
|
||
|
</table>
|
||
|
|
||
|
<br>
|
||
|
<!-- create and set an aria attribute for assistive technology -->
|
||
|
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>
|
||
|
<br><br>
|
||
|
|
||
|
<!-- The following effects are not discussed in the chapter -->
|
||
|
<div>
|
||
|
<!-- any use of [attr.disabled] creates the disabled attribute -->
|
||
|
<button [attr.disabled]="isUnchanged">Disabled</button>
|
||
|
|
||
|
<button [attr.disabled]="!isUnchanged">Disabled as well</button>
|
||
|
|
||
|
<!-- we'd have to remove it with property binding -->
|
||
|
<button disabled [disabled]="false">Enabled (but inert)</button>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- class binding -->
|
||
|
<hr><h2 id="class-binding">Class Binding</h2>
|
||
|
|
||
|
<!-- standard class attribute setting -->
|
||
|
<div class="bad curly special">Bad curly special</div>
|
||
|
|
||
|
<!-- reset/override all class names with a binding -->
|
||
|
<div class="bad curly special"
|
||
|
[class]="badCurly">Bad curly</div>
|
||
|
|
||
|
<!-- toggle the "special" class on/off with a property -->
|
||
|
<div [class.special]="isSpecial">The class binding is special</div>
|
||
|
|
||
|
<!-- binding to `class.special` trumps the class attribute -->
|
||
|
<div class="special"
|
||
|
[class.special]="!isSpecial">This one is not so special</div>
|
||
|
|
||
|
<div bind-class.special="isSpecial">This class binding is special too</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!--style binding -->
|
||
|
<hr><h2 id="style-binding">Style Binding</h2>
|
||
|
|
||
|
<button [style.color]="isSpecial ? 'red': 'green'">Red</button>
|
||
|
<button [style.background-color]="canSave ? 'cyan': 'grey'" >Save</button>
|
||
|
|
||
|
<button [style.font-size.em]="isSpecial ? 3 : 1" >Big</button>
|
||
|
<button [style.font-size.%]="!isSpecial ? 150 : 50" >Small</button>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- event binding -->
|
||
|
<hr><h2 id="event-binding">Event Binding</h2>
|
||
|
|
||
|
<button (click)="onSave()">Save</button>
|
||
|
|
||
|
<button on-click="onSave()">On Save</button>
|
||
|
|
||
|
<div>
|
||
|
<!-- `myClick` is an event on the custom `ClickDirective` -->
|
||
|
<div (myClick)="clickMessage=$event" clickable>click with myClick</div>
|
||
|
{{clickMessage}}
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<!-- binding to a nested component -->
|
||
|
<app-hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></app-hero-detail>
|
||
|
<br>
|
||
|
|
||
|
<app-big-hero-detail
|
||
|
(deleteRequest)="deleteHero($event)"
|
||
|
[hero]="currentHero">
|
||
|
</app-big-hero-detail>
|
||
|
|
||
|
<div class="parent-div" (click)="onClickMe($event)" clickable>Click me
|
||
|
<div class="child-div">Click me too!</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- Will save only once -->
|
||
|
<div (click)="onSave()" clickable>
|
||
|
<button (click)="onSave($event)">Save, no propagation</button>
|
||
|
</div>
|
||
|
|
||
|
<!-- Will save twice -->
|
||
|
<div (click)="onSave()" clickable>
|
||
|
<button (click)="onSave()">Save w/ propagation</button>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<hr><h2 id="two-way">Two-way Binding</h2>
|
||
|
<div id="two-way-1">
|
||
|
<app-sizer [(size)]="fontSizePx"></app-sizer>
|
||
|
<div [style.font-size.px]="fontSizePx">Resizable Text</div>
|
||
|
<label>FontSize (px): <input [(ngModel)]="fontSizePx"></label>
|
||
|
</div>
|
||
|
<br>
|
||
|
<div id="two-way-2">
|
||
|
<h3>De-sugared two-way binding</h3>
|
||
|
<app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- Two way data binding unwound;
|
||
|
passing the changed display value to the event handler via `$event` -->
|
||
|
<hr><h2 id="ngModel">NgModel (two-way) Binding</h2>
|
||
|
|
||
|
<h3>Result: {{currentHero.name}}</h3>
|
||
|
|
||
|
<input [value]="currentHero.name"
|
||
|
(input)="updateCurrentHeroName($event)">
|
||
|
without NgModel
|
||
|
<br>
|
||
|
<input [(ngModel)]="currentHero.name">
|
||
|
[(ngModel)]
|
||
|
<br>
|
||
|
<input bindon-ngModel="currentHero.name">
|
||
|
bindon-ngModel
|
||
|
<br>
|
||
|
<input
|
||
|
[ngModel]="currentHero.name"
|
||
|
(ngModelChange)="currentHero.name=$event">
|
||
|
(ngModelChange)="...name=$event"
|
||
|
<br>
|
||
|
<input
|
||
|
[ngModel]="currentHero.name"
|
||
|
(ngModelChange)="setUppercaseName($event)">
|
||
|
(ngModelChange)="setUppercaseName($event)"
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- NgClass binding -->
|
||
|
<hr><h2 id="ngClass">NgClass Binding</h2>
|
||
|
|
||
|
<p>currentClasses is {{currentClasses | json}}</p>
|
||
|
<div [ngClass]="currentClasses">This div is initially saveable, unchanged, and special</div>
|
||
|
|
||
|
<!-- not used in chapter -->
|
||
|
<br>
|
||
|
<label>saveable <input type="checkbox" [(ngModel)]="canSave"></label> |
|
||
|
<label>modified: <input type="checkbox" [value]="!isUnchanged" (change)="isUnchanged=!isUnchanged"></label> |
|
||
|
<label>special: <input type="checkbox" [(ngModel)]="isSpecial"></label>
|
||
|
<button (click)="setCurrentClasses()">Refresh currentClasses</button>
|
||
|
<br><br>
|
||
|
<div [ngClass]="currentClasses">
|
||
|
This div should be {{ canSave ? "": "not"}} saveable,
|
||
|
{{ isUnchanged ? "unchanged" : "modified" }} and,
|
||
|
{{ isSpecial ? "": "not"}} special after clicking "Refresh".</div>
|
||
|
<br><br>
|
||
|
|
||
|
<div [ngClass]="isSpecial ? 'special' : ''">This div is special</div>
|
||
|
|
||
|
<div class="bad curly special">Bad curly special</div>
|
||
|
<div [ngClass]="{'bad':false, 'curly':true, 'special':true}">Curly special</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- NgStyle binding -->
|
||
|
<hr><h2 id="ngStyle">NgStyle Binding</h2>
|
||
|
|
||
|
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
|
||
|
This div is x-large or smaller.
|
||
|
</div>
|
||
|
|
||
|
<h3>[ngStyle] binding to currentStyles - CSS property names</h3>
|
||
|
<p>currentStyles is {{currentStyles | json}}</p>
|
||
|
<div [ngStyle]="currentStyles">
|
||
|
This div is initially italic, normal weight, and extra large (24px).
|
||
|
</div>
|
||
|
|
||
|
<!-- not used in chapter -->
|
||
|
<br>
|
||
|
<label>italic: <input type="checkbox" [(ngModel)]="canSave"></label> |
|
||
|
<label>normal: <input type="checkbox" [(ngModel)]="isUnchanged"></label> |
|
||
|
<label>xlarge: <input type="checkbox" [(ngModel)]="isSpecial"></label>
|
||
|
<button (click)="setCurrentStyles()">Refresh currentStyles</button>
|
||
|
<br><br>
|
||
|
<div [ngStyle]="currentStyles">
|
||
|
This div should be {{ canSave ? "italic": "plain"}},
|
||
|
{{ isUnchanged ? "normal weight" : "bold" }} and,
|
||
|
{{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- NgIf binding -->
|
||
|
<hr><h2 id="ngIf">NgIf Binding</h2>
|
||
|
|
||
|
<app-hero-detail *ngIf="isActive"></app-hero-detail>
|
||
|
|
||
|
<div *ngIf="currentHero">Hello, {{currentHero.name}}</div>
|
||
|
<div *ngIf="nullHero">Hello, {{nullHero.name}}</div>
|
||
|
|
||
|
<!-- NgIf binding with template (no *) -->
|
||
|
|
||
|
<ng-template [ngIf]="currentHero">Add {{currentHero.name}} with template</ng-template>
|
||
|
|
||
|
<!-- Does not show because isActive is false! -->
|
||
|
<div>Hero Detail removed from DOM (via template) because isActive is false</div>
|
||
|
<ng-template [ngIf]="isActive">
|
||
|
<app-hero-detail></app-hero-detail>
|
||
|
</ng-template>
|
||
|
|
||
|
<!-- isSpecial is true -->
|
||
|
<div [class.hidden]="!isSpecial">Show with class</div>
|
||
|
<div [class.hidden]="isSpecial">Hide with class</div>
|
||
|
|
||
|
<!-- HeroDetail is in the DOM but hidden -->
|
||
|
<app-hero-detail [class.hidden]="isSpecial"></app-hero-detail>
|
||
|
|
||
|
<div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div>
|
||
|
<div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- NgFor binding -->
|
||
|
<hr><h2 id="ngFor">NgFor Binding</h2>
|
||
|
|
||
|
<div class="box">
|
||
|
<div *ngFor="let hero of heroes">{{hero.name}}</div>
|
||
|
</div>
|
||
|
<br>
|
||
|
|
||
|
<div class="box">
|
||
|
<!-- *ngFor w/ hero-detail Component -->
|
||
|
<app-hero-detail *ngFor="let hero of heroes" [hero]="hero"></app-hero-detail>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<h4 id="ngFor-index">*ngFor with index</h4>
|
||
|
<p>with <i>semi-colon</i> separator</p>
|
||
|
<div class="box">
|
||
|
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.name}}</div>
|
||
|
</div>
|
||
|
|
||
|
<p>with <i>comma</i> separator</p>
|
||
|
<div class="box">
|
||
|
<!-- Ex: "1 - Hercules" -->
|
||
|
<div *ngFor="let hero of heroes, let i=index">{{i + 1}} - {{hero.name}}</div>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<h4 id="ngFor-trackBy">*ngFor trackBy</h4>
|
||
|
<button (click)="resetHeroes()">Reset heroes</button>
|
||
|
<button (click)="changeIds()">Change ids</button>
|
||
|
<button (click)="clearTrackByCounts()">Clear counts</button>
|
||
|
|
||
|
<p><i>without</i> trackBy</p>
|
||
|
<div class="box">
|
||
|
<div #noTrackBy *ngFor="let hero of heroes">({{hero.id}}) {{hero.name}}</div>
|
||
|
|
||
|
<div id="noTrackByCnt" *ngIf="heroesNoTrackByCount" >
|
||
|
Hero DOM elements change #{{heroesNoTrackByCount}} without trackBy
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<p>with trackBy</p>
|
||
|
<div class="box">
|
||
|
<div #withTrackBy *ngFor="let hero of heroes; trackBy: trackByHeroes">({{hero.id}}) {{hero.name}}</div>
|
||
|
|
||
|
<div id="withTrackByCnt" *ngIf="heroesWithTrackByCount">
|
||
|
Hero DOM elements change #{{heroesWithTrackByCount}} with trackBy
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<br><br><br>
|
||
|
|
||
|
<p>with trackBy and <i>semi-colon</i> separator</p>
|
||
|
<div class="box">
|
||
|
<div *ngFor="let hero of heroes; trackBy: trackByHeroes">
|
||
|
({{hero.id}}) {{hero.name}}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<p>with trackBy and <i>comma</i> separator</p>
|
||
|
<div class="box">
|
||
|
<div *ngFor="let hero of heroes, trackBy: trackByHeroes">({{hero.id}}) {{hero.name}}</div>
|
||
|
</div>
|
||
|
|
||
|
<p>with trackBy and <i>space</i> separator</p>
|
||
|
<div class="box">
|
||
|
<div *ngFor="let hero of heroes trackBy: trackByHeroes">({{hero.id}}) {{hero.name}}</div>
|
||
|
</div>
|
||
|
|
||
|
<p>with <i>generic</i> trackById function</p>
|
||
|
<div class="box">
|
||
|
<div *ngFor="let hero of heroes, trackBy: trackById">({{hero.id}}) {{hero.name}}</div>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- NgSwitch binding -->
|
||
|
<hr><h2 id="ngSwitch">NgSwitch Binding</h2>
|
||
|
|
||
|
<p>Pick your favorite hero</p>
|
||
|
<div>
|
||
|
<label *ngFor="let h of heroes">
|
||
|
<input type="radio" name="heroes" [(ngModel)]="currentHero" [value]="h">{{h.name}}
|
||
|
</label>
|
||
|
</div>
|
||
|
|
||
|
<div [ngSwitch]="currentHero.emotion">
|
||
|
<app-happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></app-happy-hero>
|
||
|
<app-sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></app-sad-hero>
|
||
|
<app-confused-hero *ngSwitchCase="'confused'" [hero]="currentHero"></app-confused-hero>
|
||
|
<div *ngSwitchCase="'confused'">Are you as confused as {{currentHero.name}}?</div>
|
||
|
<app-unknown-hero *ngSwitchDefault [hero]="currentHero"></app-unknown-hero>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- template reference variable -->
|
||
|
<hr><h2 id="ref-vars">Template reference variables</h2>
|
||
|
|
||
|
<input #phone placeholder="phone number">
|
||
|
|
||
|
<!-- lots of other elements -->
|
||
|
|
||
|
<!-- phone refers to the input element; pass its `value` to an event handler -->
|
||
|
<button (click)="callPhone(phone.value)">Call</button>
|
||
|
|
||
|
<input ref-fax placeholder="fax number">
|
||
|
<button (click)="callFax(fax.value)">Fax</button>
|
||
|
|
||
|
<!-- btn refers to the button element; show its disabled state -->
|
||
|
<button #btn disabled [innerHTML]="'disabled by attribute: '+btn.disabled"></button>
|
||
|
|
||
|
<h4>Example Form</h4>
|
||
|
<app-hero-form [hero]="currentHero"></app-hero-form>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- inputs and output -->
|
||
|
<hr><h2 id="inputs-and-outputs">Inputs and Outputs</h2>
|
||
|
|
||
|
<img [src]="iconUrl"/>
|
||
|
<button (click)="onSave()">Save</button>
|
||
|
|
||
|
<app-hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
|
||
|
</app-hero-detail>
|
||
|
|
||
|
<div (myClick)="clickMessage2=$event" clickable>myClick2</div>
|
||
|
{{clickMessage2}}
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- Pipes -->
|
||
|
<hr><h2 id="pipes">Pipes</h2>
|
||
|
|
||
|
<div>Title through uppercase pipe: {{title | uppercase}}</div>
|
||
|
|
||
|
<!-- Pipe chaining: convert title to uppercase, then to lowercase -->
|
||
|
<div>
|
||
|
Title through a pipe chain:
|
||
|
{{title | uppercase | lowercase}}
|
||
|
</div>
|
||
|
|
||
|
<!-- pipe with configuration argument => "February 25, 1970" -->
|
||
|
<div>Birthdate: {{currentHero?.birthdate | date:'longDate'}}</div>
|
||
|
|
||
|
<div>{{currentHero | json}}</div>
|
||
|
|
||
|
<div>Birthdate: {{(currentHero?.birthdate | date:'longDate') | uppercase}}</div>
|
||
|
|
||
|
<div>
|
||
|
<!-- pipe price to USD and display the $ symbol -->
|
||
|
<label>Price: </label>{{product.price | currency:'USD':'symbol'}}
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- Null values and the safe navigation operator -->
|
||
|
<hr><h2 id="safe-navigation-operator">Safe navigation operator <i>?.</i></h2>
|
||
|
|
||
|
<div>
|
||
|
The title is {{title}}
|
||
|
</div>
|
||
|
|
||
|
<div>
|
||
|
The current hero's name is {{currentHero?.name}}
|
||
|
</div>
|
||
|
|
||
|
<div>
|
||
|
The current hero's name is {{currentHero.name}}
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<!--
|
||
|
The null hero's name is {{nullHero.name}}
|
||
|
|
||
|
See console log:
|
||
|
TypeError: Cannot read property 'name' of null in [null]
|
||
|
-->
|
||
|
|
||
|
<!--No hero, div not displayed, no error -->
|
||
|
<div *ngIf="nullHero">The null hero's name is {{nullHero.name}}</div>
|
||
|
|
||
|
<div>
|
||
|
The null hero's name is {{nullHero && nullHero.name}}
|
||
|
</div>
|
||
|
|
||
|
<div>
|
||
|
<!-- No hero, no problem! -->
|
||
|
The null hero's name is {{nullHero?.name}}
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- non-null assertion operator -->
|
||
|
<hr><h2 id="non-null-assertion-operator">Non-null assertion operator <i>!.</i></h2>
|
||
|
|
||
|
<div>
|
||
|
<!--No hero, no text -->
|
||
|
<div *ngIf="hero">
|
||
|
The hero's name is {{hero!.name}}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- non-null assertion operator -->
|
||
|
<hr><h2 id="any-type-cast-function">$any type cast function <i>$any( )</i>.</h2>
|
||
|
|
||
|
<div>
|
||
|
<!-- Accessing an undeclared member -->
|
||
|
<div>
|
||
|
The hero's marker is {{$any(hero).marker}}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div>
|
||
|
<!-- Accessing an undeclared member -->
|
||
|
<div>
|
||
|
Undeclared members is {{$any(this).member}}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<!-- TODO: discuss this in the Style binding section -->
|
||
|
<!-- enums in bindings -->
|
||
|
<hr><h2 id="enums">Enums in binding</h2>
|
||
|
|
||
|
<p>
|
||
|
The name of the Color.Red enum is {{Color[Color.Red]}}.<br>
|
||
|
The current color is {{Color[color]}} and its number is {{color}}.<br>
|
||
|
<button [style.color]="Color[color]" (click)="colorToggle()">Enum Toggle</button>
|
||
|
</p>
|
||
|
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
<hr><h2 id="svg-templates">SVG Templates</h2>
|
||
|
<app-svg></app-svg>
|
||
|
<a class="to-toc" href="#toc">top</a>
|
||
|
|
||
|
|
||
|
<!--
|
||
|
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/hero-form.component.html]" value="<div id="heroForm">
|
||
|
<form (ngSubmit)="onSubmit(heroForm)" #heroForm="ngForm">
|
||
|
<div class="form-group">
|
||
|
<label for="name">Name
|
||
|
<input class="form-control" name="name" required [(ngModel)]="hero.name">
|
||
|
</label>
|
||
|
</div>
|
||
|
<button type="submit" [disabled]="!heroForm.form.valid">Submit</button>
|
||
|
</form>
|
||
|
<div [hidden]="!heroForm.form.valid">
|
||
|
{{submitMessage}}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
|
||
|
<!--
|
||
|
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="en">
|
||
|
<head>
|
||
|
<title>Template Syntax</title>
|
||
|
<base href="/">
|
||
|
<meta charset="UTF-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
</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="{
|
||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||
|
"version": 1,
|
||
|
"newProjectRoot": "projects",
|
||
|
"projects": {
|
||
|
"angular.io-example": {
|
||
|
"projectType": "application",
|
||
|
"schematics": {
|
||
|
"@schematics/angular:application": {
|
||
|
"strict": true
|
||
|
}
|
||
|
},
|
||
|
"root": "",
|
||
|
"sourceRoot": "src",
|
||
|
"prefix": "app",
|
||
|
"architect": {
|
||
|
"build": {
|
||
|
"builder": "@angular-devkit/build-angular:browser",
|
||
|
"options": {
|
||
|
"outputPath": "dist",
|
||
|
"index": "src/index.html",
|
||
|
"main": "src/main.ts",
|
||
|
"polyfills": "src/polyfills.ts",
|
||
|
"tsConfig": "tsconfig.app.json",
|
||
|
"aot": true,
|
||
|
"assets": [
|
||
|
"src/favicon.ico",
|
||
|
"src/assets"
|
||
|
],
|
||
|
"styles": [
|
||
|
"src/styles.css"
|
||
|
],
|
||
|
"scripts": []
|
||
|
},
|
||
|
"configurations": {
|
||
|
"production": {
|
||
|
"fileReplacements": [
|
||
|
{
|
||
|
"replace": "src/environments/environment.ts",
|
||
|
"with": "src/environments/environment.prod.ts"
|
||
|
}
|
||
|
],
|
||
|
"optimization": true,
|
||
|
"outputHashing": "all",
|
||
|
"sourceMap": false,
|
||
|
"namedChunks": false,
|
||
|
"extractLicenses": true,
|
||
|
"vendorChunk": false,
|
||
|
"buildOptimizer": true,
|
||
|
"budgets": [
|
||
|
{
|
||
|
"type": "initial",
|
||
|
"maximumWarning": "500kb",
|
||
|
"maximumError": "1mb"
|
||
|
},
|
||
|
{
|
||
|
"type": "anyComponentStyle",
|
||
|
"maximumWarning": "2kb",
|
||
|
"maximumError": "4kb"
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
"serve": {
|
||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||
|
"options": {
|
||
|
"browserTarget": "angular.io-example:build"
|
||
|
},
|
||
|
"configurations": {
|
||
|
"production": {
|
||
|
"browserTarget": "angular.io-example:build:production"
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
"extract-i18n": {
|
||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||
|
"options": {
|
||
|
"browserTarget": "angular.io-example:build"
|
||
|
}
|
||
|
},
|
||
|
"test": {
|
||
|
"builder": "@angular-devkit/build-angular:karma",
|
||
|
"options": {
|
||
|
"main": "src/test.ts",
|
||
|
"polyfills": "src/polyfills.ts",
|
||
|
"tsConfig": "tsconfig.spec.json",
|
||
|
"karmaConfig": "karma.conf.js",
|
||
|
"assets": [
|
||
|
"src/favicon.ico",
|
||
|
"src/assets"
|
||
|
],
|
||
|
"styles": [
|
||
|
"src/styles.css"
|
||
|
],
|
||
|
"scripts": []
|
||
|
}
|
||
|
},
|
||
|
"lint": {
|
||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||
|
"options": {
|
||
|
"tsConfig": [
|
||
|
"tsconfig.app.json",
|
||
|
"tsconfig.spec.json",
|
||
|
"e2e/tsconfig.json"
|
||
|
],
|
||
|
"exclude": [
|
||
|
"**/node_modules/**"
|
||
|
]
|
||
|
}
|
||
|
},
|
||
|
"e2e": {
|
||
|
"builder": "@angular-devkit/build-angular:protractor",
|
||
|
"options": {
|
||
|
"protractorConfig": "e2e/protractor.conf.js",
|
||
|
"devServerTarget": "angular.io-example:serve"
|
||
|
},
|
||
|
"configurations": {
|
||
|
"production": {
|
||
|
"devServerTarget": "angular.io-example:serve:production"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
"defaultProject": "angular.io-example"
|
||
|
}
|
||
|
"><input type="hidden" name="files[tsconfig.json]" value="{
|
||
|
"compileOnSave": false,
|
||
|
"compilerOptions": {
|
||
|
"baseUrl": "./",
|
||
|
"outDir": "./dist/out-tsc",
|
||
|
"forceConsistentCasingInFileNames": true,
|
||
|
"noImplicitReturns": true,
|
||
|
"noFallthroughCasesInSwitch": true,
|
||
|
"sourceMap": true,
|
||
|
"declaration": false,
|
||
|
"downlevelIteration": true,
|
||
|
"experimentalDecorators": true,
|
||
|
"moduleResolution": "node",
|
||
|
"importHelpers": true,
|
||
|
"target": "es2015",
|
||
|
"module": "es2020",
|
||
|
"lib": [
|
||
|
"es2018",
|
||
|
"dom"
|
||
|
]
|
||
|
},
|
||
|
"angularCompilerOptions": {
|
||
|
"strictInjectionParameters": true,
|
||
|
"strictInputAccessModifiers": true,
|
||
|
"strictTemplates": true,
|
||
|
"enableIvy": true
|
||
|
}
|
||
|
}"><input type="hidden" name="files[src/assets/images/hero.base64.png]" value="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
|
||
|
<g>
|
||
|
<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>
|
||
|
"><input type="hidden" name="tags[0]" value="angular"><input type="hidden" name="tags[1]" value="example"><input type="hidden" name="tags[2]" value="template"><input type="hidden" name="description" value="Angular Example - Template Syntax Collection"><input type="hidden" name="dependencies" value="{"@angular/animations":"~11.0.1","@angular/common":"~11.0.1","@angular/compiler":"~11.0.1","@angular/core":"~11.0.1","@angular/forms":"~11.0.1","@angular/platform-browser":"~11.0.1","@angular/platform-browser-dynamic":"~11.0.1","@angular/router":"~11.0.1","angular-in-memory-web-api":"~0.11.0","rxjs":"~6.6.0","tslib":"^2.0.0","zone.js":"~0.11.4","jasmine-core":"~3.6.0","jasmine-marbles":"~0.6.0"}"></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>
|