docs(template syntax): improvements based on review feedback Feb 16
closes #851
This commit is contained in:
parent
f07ca644a6
commit
157d6d48cc
|
@ -28,6 +28,7 @@ class AppComponent {
|
||||||
// String badCurly = 'bad curly'; // XXX: This isn't working.
|
// String badCurly = 'bad curly'; // XXX: This isn't working.
|
||||||
String badCurly = 'bad'; // XXX: This isn't working.
|
String badCurly = 'bad'; // XXX: This isn't working.
|
||||||
// List<String> badCurly = ['bad', 'curly']; // XXX: This isn't working.
|
// List<String> badCurly = ['bad', 'curly']; // XXX: This isn't working.
|
||||||
|
String classes = 'special';
|
||||||
bool canSave = true;
|
bool canSave = true;
|
||||||
bool isActive = false;
|
bool isActive = false;
|
||||||
bool isSpecial = true;
|
bool isSpecial = true;
|
||||||
|
@ -96,6 +97,8 @@ class AppComponent {
|
||||||
alerter('Click me. $evtMsg');
|
alerter('Click me. $evtMsg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deleteHero([Hero hero]) => alerter('Deleted hero: ${hero?.firstName}');
|
||||||
|
|
||||||
bool onSave([MouseEvent event = null]) {
|
bool onSave([MouseEvent event = null]) {
|
||||||
var evtMsg =
|
var evtMsg =
|
||||||
event != null ? ' Event target is ${event.target.innerHtml}.' : '';
|
event != null ? ' Event target is ${event.target.innerHtml}.' : '';
|
||||||
|
@ -103,8 +106,6 @@ class AppComponent {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onHeroDeleted([Hero hero]) => alerter('Deleted hero: ${hero?.firstName}');
|
|
||||||
|
|
||||||
void onSubmit(NgForm form) {
|
void onSubmit(NgForm form) {
|
||||||
var evtMsg = form.valid
|
var evtMsg = form.valid
|
||||||
? ' Form value is ${JSON.encode(form.value)}'
|
? ' Form value is ${JSON.encode(form.value)}'
|
||||||
|
|
|
@ -70,7 +70,6 @@
|
||||||
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
|
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
|
||||||
<!-- #docregion img+button -->
|
<!-- #docregion img+button -->
|
||||||
<div class="special">Mental Model</div>
|
<div class="special">Mental Model</div>
|
||||||
<div><b>{{currentHero.fullName}}</b></div>
|
|
||||||
<img src="assets/images/hero.png">
|
<img src="assets/images/hero.png">
|
||||||
<button disabled>Save</button>
|
<button disabled>Save</button>
|
||||||
<!-- #enddocregion img+button -->
|
<!-- #enddocregion img+button -->
|
||||||
|
@ -78,7 +77,9 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion hero-detail-1 -->
|
<!-- #docregion hero-detail-1 -->
|
||||||
|
<!-- Normal HTML -->
|
||||||
<div class="special">Mental Model</div>
|
<div class="special">Mental Model</div>
|
||||||
|
<!-- Wow! A new element! -->
|
||||||
<hero-detail></hero-detail>
|
<hero-detail></hero-detail>
|
||||||
<!-- #enddocregion hero-detail-1 -->
|
<!-- #enddocregion hero-detail-1 -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,7 +104,7 @@
|
||||||
|
|
||||||
<!-- #docregion event-binding-syntax-1 -->
|
<!-- #docregion event-binding-syntax-1 -->
|
||||||
<button (click) = "onSave()">Save</button>
|
<button (click) = "onSave()">Save</button>
|
||||||
<hero-detail (deleted)="onHeroDeleted()"></hero-detail>
|
<hero-detail (deleteRequest)="deleteHero()"></hero-detail>
|
||||||
<div (myClick)="clickity=$event">click me</div>
|
<div (myClick)="clickity=$event">click me</div>
|
||||||
<!-- #enddocregion event-binding-syntax-1 -->
|
<!-- #enddocregion event-binding-syntax-1 -->
|
||||||
{{clickity}}
|
{{clickity}}
|
||||||
|
@ -170,31 +171,38 @@ button</button>
|
||||||
<img [src]="heroImageUrl">
|
<img [src]="heroImageUrl">
|
||||||
<!-- #enddocregion property-binding-1 -->
|
<!-- #enddocregion property-binding-1 -->
|
||||||
<!-- #docregion property-binding-2 -->
|
<!-- #docregion property-binding-2 -->
|
||||||
<button [disabled]="isUnchanged">Cancel</button>
|
<button [disabled]="isUnchanged">Cancel is disabled</button>
|
||||||
<!-- #enddocregion property-binding-2 -->
|
<!-- #enddocregion property-binding-2 -->
|
||||||
<!-- #docregion property-binding-3 -->
|
<!-- #docregion property-binding-3 -->
|
||||||
<div [ngClass]="'special'">NgClass is special</div>
|
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
|
||||||
<!-- #enddocregion property-binding-3 -->
|
<!-- #enddocregion property-binding-3 -->
|
||||||
<!-- #docregion property-binding-4 -->
|
<!-- #docregion property-binding-4 -->
|
||||||
<hero-detail [hero]="selectedHero"></hero-detail>
|
<hero-detail [hero]="currentHero"></hero-detail>
|
||||||
<!-- #enddocregion property-binding-4 -->
|
<!-- #enddocregion property-binding-4 -->
|
||||||
<!-- #docregion property-binding-5 -->
|
<!-- #docregion property-binding-5 -->
|
||||||
<img bind-src="heroImageUrl">
|
<img bind-src="heroImageUrl">
|
||||||
<!-- #enddocregion property-binding-5 -->
|
<!-- #enddocregion property-binding-5 -->
|
||||||
|
|
||||||
<!-- #docregion property-binding-6 -->
|
<!-- #docregion property-binding-6 -->
|
||||||
<!-- Doesn't work! HeroDetailComponent expects a Hero, not a string -->
|
<!--
|
||||||
<!-- <hero-detail hero="…what do we do here??? …"></hero-detail> -->
|
BAD!
|
||||||
|
HeroDetailComponent.hero expects a Hero object,
|
||||||
|
not the string "currentHero"
|
||||||
|
-->
|
||||||
|
<hero-detail hero="currentHero"></hero-detail>
|
||||||
<!-- #enddocregion property-binding-6 -->
|
<!-- #enddocregion property-binding-6 -->
|
||||||
<!-- In checked mode, uncommenting the hero-detail above causes this:
|
<!-- In checked mode, uncommenting the hero-detail above causes this:
|
||||||
EXCEPTION: type 'String' is not a subtype of type 'Hero' of 'value'. -->
|
EXCEPTION: type 'String' is not a subtype of type 'Hero' of 'value'. -->
|
||||||
|
<!-- #docregion property-binding-7 -->
|
||||||
|
<hero-detail prefix="You are my" [hero]="currentHero"></hero-detail>
|
||||||
|
<!-- #enddocregion property-binding-7 -->
|
||||||
|
|
||||||
<!-- #docregion property-binding-vs-interpolation -->
|
<!-- #docregion property-binding-vs-interpolation -->
|
||||||
<img src="{{heroImageUrl}}">
|
Interpolated: <img src="{{heroImageUrl}}"><br>
|
||||||
<img [src]="heroImageUrl">
|
Property bound: <img [src]="heroImageUrl">
|
||||||
|
|
||||||
<div>The title is {{title}}</div>
|
<div>The interpolated title is {{title}}</div>
|
||||||
<div [textContent]="'The title is ' + title"></div>
|
<div [textContent]="'The [textContent] title is '+title"></div>
|
||||||
<!-- #enddocregion property-binding-vs-interpolation -->
|
<!-- #enddocregion property-binding-vs-interpolation -->
|
||||||
|
|
||||||
<a class="to-toc" href="#toc">top</a>
|
<a class="to-toc" href="#toc">top</a>
|
||||||
|
@ -298,7 +306,6 @@ button</button>
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion event-binding-3 -->
|
<!-- #docregion event-binding-3 -->
|
||||||
<!-- `myClick` is an event on the custom `MyClickDirective` -->
|
<!-- `myClick` is an event on the custom `MyClickDirective` -->
|
||||||
|
|
||||||
<!-- #docregion my-click -->
|
<!-- #docregion my-click -->
|
||||||
<div (myClick)="clickMessage=$event">click with myClick</div>
|
<div (myClick)="clickMessage=$event">click with myClick</div>
|
||||||
<!-- #enddocregion my-click -->
|
<!-- #enddocregion my-click -->
|
||||||
|
@ -309,13 +316,12 @@ button</button>
|
||||||
|
|
||||||
<!-- binding to a nested component -->
|
<!-- binding to a nested component -->
|
||||||
<!-- #docregion event-binding-to-component -->
|
<!-- #docregion event-binding-to-component -->
|
||||||
<hero-detail (deleted)="onHeroDeleted($event)" [hero]="currentHero">
|
<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>
|
||||||
</hero-detail>
|
|
||||||
<!-- #enddocregion event-binding-to-component -->
|
<!-- #enddocregion event-binding-to-component -->
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<big-hero-detail
|
<big-hero-detail
|
||||||
(deleted)="onHeroDeleted($event)"
|
(deleteRequest)="deleteHero($event)"
|
||||||
[hero]="currentHero">
|
[hero]="currentHero">
|
||||||
</big-hero-detail>
|
</big-hero-detail>
|
||||||
|
|
||||||
|
@ -689,7 +695,7 @@ bindon-ngModel
|
||||||
<!-- #enddocregion io-1 -->
|
<!-- #enddocregion io-1 -->
|
||||||
|
|
||||||
<!-- #docregion io-2 -->
|
<!-- #docregion io-2 -->
|
||||||
<hero-detail [hero]="currentHero" (deleted)="onHeroDeleted($event)">
|
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
|
||||||
</hero-detail>
|
</hero-detail>
|
||||||
<!-- #enddocregion io-2 -->
|
<!-- #enddocregion io-2 -->
|
||||||
|
|
||||||
|
|
|
@ -13,15 +13,20 @@ var nextHeroDetailId = 1;
|
||||||
// #docregion input-output-2
|
// #docregion input-output-2
|
||||||
// ...
|
// ...
|
||||||
inputs: const ['hero'],
|
inputs: const ['hero'],
|
||||||
outputs: const ['deleted'],
|
outputs: const ['deleteRequest'],
|
||||||
// #enddocregion input-output-2
|
// #enddocregion input-output-2
|
||||||
|
styles:['button { margin-left: 8px} div {margin: 8px 0} img {height:24px}'],
|
||||||
|
// #docregion template-1
|
||||||
template: '''
|
template: '''
|
||||||
<div>
|
<div>
|
||||||
<span [style.text-decoration]="lineThrough" >{{hero?.fullName}}</span>
|
<img src="{{heroImageUrl}}">
|
||||||
<img src="{{heroImageUrl}}" style="height:24px">
|
<span [style.text-decoration]="lineThrough">
|
||||||
<a (click)="onDelete()">delete</a>
|
{{prefix}} {{hero?.fullName}}
|
||||||
</div>
|
</span>
|
||||||
|
<button (click)="delete()">Delete</button>
|
||||||
|
</div>
|
||||||
'''
|
'''
|
||||||
|
// #enddocregion template-1
|
||||||
// #docregion input-output-2
|
// #docregion input-output-2
|
||||||
)
|
)
|
||||||
// #enddocregion input-output-2
|
// #enddocregion input-output-2
|
||||||
|
@ -29,29 +34,26 @@ class HeroDetailComponent {
|
||||||
Hero hero = new Hero('Zzzzzzzz'); // default sleeping hero
|
Hero hero = new Hero('Zzzzzzzz'); // default sleeping hero
|
||||||
String heroImageUrl = 'assets/images/hero.png';
|
String heroImageUrl = 'assets/images/hero.png';
|
||||||
String lineThrough = ''; // PENDING: use null instead?
|
String lineThrough = ''; // PENDING: use null instead?
|
||||||
|
@Input() String prefix = '';
|
||||||
|
|
||||||
// #docregion deleted
|
// #docregion deleteRequest
|
||||||
final EventEmitter deleted = new EventEmitter<Hero>();
|
// This component make a request but it can't actually delete a hero.
|
||||||
// #enddocregion deleted
|
final EventEmitter deleteRequest = new EventEmitter<Hero>();
|
||||||
|
|
||||||
HeroDetailComponent() {
|
delete() {
|
||||||
deleted.listen((Hero _) {
|
deleteRequest.emit(hero);
|
||||||
lineThrough = (lineThrough == '') ? 'line-through' : '';
|
// #enddocregion deleteRequest
|
||||||
});
|
lineThrough = (lineThrough == '') ? 'line-through' : '';
|
||||||
|
// #docregion deleteRequest
|
||||||
}
|
}
|
||||||
|
// #enddocregion deleteRequest
|
||||||
// #docregion deleted
|
|
||||||
onDelete() {
|
|
||||||
deleted.emit(hero);
|
|
||||||
}
|
|
||||||
// #enddocregion deleted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
selector: 'big-hero-detail',
|
selector: 'big-hero-detail',
|
||||||
/*
|
/*
|
||||||
inputs: ['hero'],
|
inputs: ['hero'],
|
||||||
outputs: ['deleted'],
|
outputs: ['deleteRequest'],
|
||||||
*/
|
*/
|
||||||
template: '''
|
template: '''
|
||||||
<div style="border: 1px solid black; padding:3px">
|
<div style="border: 1px solid black; padding:3px">
|
||||||
|
@ -63,18 +65,18 @@ class HeroDetailComponent {
|
||||||
<div>Web: <a href="{{hero?.url}}" target="_blank">{{hero?.url}}</a></div>
|
<div>Web: <a href="{{hero?.url}}" target="_blank">{{hero?.url}}</a></div>
|
||||||
<div>Rate/hr: {{hero?.rate | currency:'EUR'}}</div>
|
<div>Rate/hr: {{hero?.rate | currency:'EUR'}}</div>
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
<button (click)="onDelete()">Delete</button>
|
<button (click)="delete()">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
''')
|
''')
|
||||||
class BigHeroDetailComponent extends HeroDetailComponent {
|
class BigHeroDetailComponent extends HeroDetailComponent {
|
||||||
// #docregion input-output-1
|
// #docregion input-output-1
|
||||||
@Input() Hero hero;
|
@Input() Hero hero;
|
||||||
@Output() final EventEmitter deleted = new EventEmitter<Hero>();
|
@Output() final EventEmitter deleteRequest = new EventEmitter<Hero>();
|
||||||
// #enddocregion input-output-1
|
// #enddocregion input-output-1
|
||||||
|
|
||||||
String heroImageUrl = 'assets/images/hero.png';
|
String heroImageUrl = 'assets/images/hero.png';
|
||||||
|
|
||||||
onDelete() {
|
delete() {
|
||||||
deleted.emit(hero);
|
deleteRequest.emit(hero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,6 @@
|
||||||
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
|
<!-- Public Domain terms of use: http://www.wpclipart.com/terms.html -->
|
||||||
<!-- #docregion img+button -->
|
<!-- #docregion img+button -->
|
||||||
<div class="special">Mental Model</div>
|
<div class="special">Mental Model</div>
|
||||||
<div><b>{{currentHero.fullName}}</b></div>
|
|
||||||
<img src="images/hero.png">
|
<img src="images/hero.png">
|
||||||
<button disabled>Save</button>
|
<button disabled>Save</button>
|
||||||
<!-- #enddocregion img+button -->
|
<!-- #enddocregion img+button -->
|
||||||
|
@ -78,7 +77,9 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion hero-detail-1 -->
|
<!-- #docregion hero-detail-1 -->
|
||||||
|
<!-- Normal HTML -->
|
||||||
<div class="special">Mental Model</div>
|
<div class="special">Mental Model</div>
|
||||||
|
<!-- Wow! A new element! -->
|
||||||
<hero-detail></hero-detail>
|
<hero-detail></hero-detail>
|
||||||
<!-- #enddocregion hero-detail-1 -->
|
<!-- #enddocregion hero-detail-1 -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,7 +104,7 @@
|
||||||
|
|
||||||
<!-- #docregion event-binding-syntax-1 -->
|
<!-- #docregion event-binding-syntax-1 -->
|
||||||
<button (click) = "onSave()">Save</button>
|
<button (click) = "onSave()">Save</button>
|
||||||
<hero-detail (deleted)="onHeroDeleted()"></hero-detail>
|
<hero-detail (deleteRequest)="deleteHero()"></hero-detail>
|
||||||
<div (myClick)="clicked=$event">click me</div>
|
<div (myClick)="clicked=$event">click me</div>
|
||||||
<!-- #enddocregion event-binding-syntax-1 -->
|
<!-- #enddocregion event-binding-syntax-1 -->
|
||||||
{{clicked}}
|
{{clicked}}
|
||||||
|
@ -170,29 +171,36 @@ button</button>
|
||||||
<img [src]="heroImageUrl">
|
<img [src]="heroImageUrl">
|
||||||
<!-- #enddocregion property-binding-1 -->
|
<!-- #enddocregion property-binding-1 -->
|
||||||
<!-- #docregion property-binding-2 -->
|
<!-- #docregion property-binding-2 -->
|
||||||
<button [disabled]="isUnchanged">Cancel</button>
|
<button [disabled]="isUnchanged">Cancel is disabled</button>
|
||||||
<!-- #enddocregion property-binding-2 -->
|
<!-- #enddocregion property-binding-2 -->
|
||||||
<!-- #docregion property-binding-3 -->
|
<!-- #docregion property-binding-3 -->
|
||||||
<div [ngClass]="'special'">NgClass is special</div>
|
<div [ngClass]="classes">[ngClass] binding to the classes property</div>
|
||||||
<!-- #enddocregion property-binding-3 -->
|
<!-- #enddocregion property-binding-3 -->
|
||||||
<!-- #docregion property-binding-4 -->
|
<!-- #docregion property-binding-4 -->
|
||||||
<hero-detail [hero]="selectedHero"></hero-detail>
|
<hero-detail [hero]="currentHero"></hero-detail>
|
||||||
<!-- #enddocregion property-binding-4 -->
|
<!-- #enddocregion property-binding-4 -->
|
||||||
<!-- #docregion property-binding-5 -->
|
<!-- #docregion property-binding-5 -->
|
||||||
<img bind-src="heroImageUrl">
|
<img bind-src="heroImageUrl">
|
||||||
<!-- #enddocregion property-binding-5 -->
|
<!-- #enddocregion property-binding-5 -->
|
||||||
|
|
||||||
<!-- #docregion property-binding-6 -->
|
<!-- #docregion property-binding-6 -->
|
||||||
<!-- Doesn't work! HeroDetailComponent expects a Hero, not a string -->
|
<!--
|
||||||
<hero-detail hero="…what do we do here??? …"></hero-detail>
|
BAD!
|
||||||
|
HeroDetailComponent.hero expects a Hero object,
|
||||||
|
not the string "currentHero"
|
||||||
|
-->
|
||||||
|
<hero-detail hero="currentHero"></hero-detail>
|
||||||
<!-- #enddocregion property-binding-6 -->
|
<!-- #enddocregion property-binding-6 -->
|
||||||
|
<!-- #docregion property-binding-7 -->
|
||||||
|
<hero-detail prefix="You are my" [hero]="currentHero"></hero-detail>
|
||||||
|
<!-- #enddocregion property-binding-7 -->
|
||||||
|
|
||||||
<!-- #docregion property-binding-vs-interpolation -->
|
<!-- #docregion property-binding-vs-interpolation -->
|
||||||
<img src="{{heroImageUrl}}">
|
Interpolated: <img src="{{heroImageUrl}}"><br>
|
||||||
<img [src]="'' + heroImageUrl">
|
Property bound: <img [src]="heroImageUrl">
|
||||||
|
|
||||||
<div>The title is {{title}}</div>
|
<div>The interpolated title is {{title}}</div>
|
||||||
<div [textContent]="'The title is '+title"></div>
|
<div [textContent]="'The [textContent] title is '+title"></div>
|
||||||
<!-- #enddocregion property-binding-vs-interpolation -->
|
<!-- #enddocregion property-binding-vs-interpolation -->
|
||||||
|
|
||||||
<a class="to-toc" href="#toc">top</a>
|
<a class="to-toc" href="#toc">top</a>
|
||||||
|
@ -295,7 +303,6 @@ button</button>
|
||||||
<div>
|
<div>
|
||||||
<!-- #docregion event-binding-3 -->
|
<!-- #docregion event-binding-3 -->
|
||||||
<!-- `myClick` is an event on the custom `MyClickDirective` -->
|
<!-- `myClick` is an event on the custom `MyClickDirective` -->
|
||||||
|
|
||||||
<!-- #docregion my-click -->
|
<!-- #docregion my-click -->
|
||||||
<div (myClick)="clickMessage=$event">click with myClick</div>
|
<div (myClick)="clickMessage=$event">click with myClick</div>
|
||||||
<!-- #enddocregion my-click -->
|
<!-- #enddocregion my-click -->
|
||||||
|
@ -306,13 +313,12 @@ button</button>
|
||||||
|
|
||||||
<!-- binding to a nested component -->
|
<!-- binding to a nested component -->
|
||||||
<!-- #docregion event-binding-to-component -->
|
<!-- #docregion event-binding-to-component -->
|
||||||
<hero-detail (deleted)="onHeroDeleted($event)" [hero]="currentHero">
|
<hero-detail (deleteRequest)="deleteHero($event)" [hero]="currentHero"></hero-detail>
|
||||||
</hero-detail>
|
|
||||||
<!-- #enddocregion event-binding-to-component -->
|
<!-- #enddocregion event-binding-to-component -->
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<big-hero-detail
|
<big-hero-detail
|
||||||
(deleted)="onHeroDeleted($event)"
|
(deleteRequest)="deleteHero($event)"
|
||||||
[hero]="currentHero">
|
[hero]="currentHero">
|
||||||
</big-hero-detail>
|
</big-hero-detail>
|
||||||
|
|
||||||
|
@ -685,7 +691,7 @@ After setClasses(), the classes are "{{classDiv.className}}"
|
||||||
<!-- #enddocregion io-1 -->
|
<!-- #enddocregion io-1 -->
|
||||||
|
|
||||||
<!-- #docregion io-2 -->
|
<!-- #docregion io-2 -->
|
||||||
<hero-detail [hero]="currentHero" (deleted)="onHeroDeleted($event)">
|
<hero-detail [hero]="currentHero" (deleteRequest)="deleteHero($event)">
|
||||||
</hero-detail>
|
</hero-detail>
|
||||||
<!-- #enddocregion io-2 -->
|
<!-- #enddocregion io-2 -->
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||||
actionName = 'Go for it';
|
actionName = 'Go for it';
|
||||||
alert = alerter;
|
alert = alerter;
|
||||||
badCurly = 'bad curly';
|
badCurly = 'bad curly';
|
||||||
|
classes = 'special';
|
||||||
|
|
||||||
callFax(value:string) {this.alert(`Faxing ${value} ...`)}
|
callFax(value:string) {this.alert(`Faxing ${value} ...`)}
|
||||||
callPhone(value:string) {this.alert(`Calling ${value} ...`)}
|
callPhone(value:string) {this.alert(`Calling ${value} ...`)}
|
||||||
canSave = true;
|
canSave = true;
|
||||||
|
@ -48,6 +50,10 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||||
|
|
||||||
currentHero = Hero.MockHeroes[0];
|
currentHero = Hero.MockHeroes[0];
|
||||||
|
|
||||||
|
deleteHero(hero:Hero){
|
||||||
|
this.alert('Deleted hero: '+ (hero && hero.firstName))
|
||||||
|
}
|
||||||
|
|
||||||
// DevMode memoization fields
|
// DevMode memoization fields
|
||||||
private _priorClasses:{};
|
private _priorClasses:{};
|
||||||
private _priorStyles:{};
|
private _priorStyles:{};
|
||||||
|
@ -88,10 +94,6 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||||
this.alert('Click me.'+evtMsg)
|
this.alert('Click me.'+evtMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
onHeroDeleted(hero:Hero){
|
|
||||||
this.alert('Deleted hero: '+ (hero && hero.firstName))
|
|
||||||
}
|
|
||||||
|
|
||||||
onSave(event:KeyboardEvent){
|
onSave(event:KeyboardEvent){
|
||||||
let evtMsg = event ? ' Event target is '+ (<HTMLElement>event.target).innerText : '';
|
let evtMsg = event ? ' Event target is '+ (<HTMLElement>event.target).innerText : '';
|
||||||
this.alert('Saved.'+evtMsg)
|
this.alert('Saved.'+evtMsg)
|
||||||
|
|
|
@ -11,41 +11,42 @@ let nextHeroDetailId = 1;
|
||||||
selector: 'hero-detail',
|
selector: 'hero-detail',
|
||||||
// #docregion input-output-2
|
// #docregion input-output-2
|
||||||
inputs: ['hero'],
|
inputs: ['hero'],
|
||||||
outputs: ['deleted'],
|
outputs: ['deleteRequest'],
|
||||||
// #enddocregion input-output-2
|
// #enddocregion input-output-2
|
||||||
|
styles:['button { margin-left: 8px} div {margin: 8px 0} img {height:24px}'],
|
||||||
|
// #docregion template-1
|
||||||
template: `
|
template: `
|
||||||
<div>
|
<div>
|
||||||
<span [style.text-decoration]="lineThrough" >{{hero?.fullName}}</span>
|
<img src="{{heroImageUrl}}">
|
||||||
<img src="{{heroImageUrl}}" style="height:24px">
|
<span [style.text-decoration]="lineThrough">
|
||||||
<a (click)="onDelete()">delete</a>
|
{{prefix}} {{hero?.fullName}}
|
||||||
</div>`,
|
</span>
|
||||||
styles:['a { cursor: pointer; cursor: hand; }']
|
<button (click)="delete()">Delete</button>
|
||||||
|
</div>`
|
||||||
|
// #enddocregion template-1
|
||||||
// #docregion input-output-2
|
// #docregion input-output-2
|
||||||
})
|
})
|
||||||
// #enddocregion input-output-2
|
// #enddocregion input-output-2
|
||||||
export class HeroDetailComponent {
|
export class HeroDetailComponent {
|
||||||
constructor() {
|
|
||||||
// Toggle the line-through style so we see something happen
|
// #docregion deleteRequest
|
||||||
// even if no one attaches to the `deleted` event.
|
// This component make a request but it can't actually delete a hero.
|
||||||
// Subscribing in ctor rather than the more obvious thing of doing it in
|
deleteRequest = new EventEmitter<Hero>();
|
||||||
// OnDelete because don't want this mess to distract the chapter reader.
|
|
||||||
this.deleted.subscribe(() => {
|
delete() {
|
||||||
this.lineThrough = this.lineThrough ? '' : 'line-through';
|
this.deleteRequest.emit(this.hero);
|
||||||
})
|
// #enddocregion deleteRequest
|
||||||
|
this.lineThrough = this.lineThrough ? '' : 'line-through';
|
||||||
|
// #docregion deleteRequest
|
||||||
}
|
}
|
||||||
|
// #enddocregion deleteRequest
|
||||||
// #docregion deleted
|
|
||||||
deleted = new EventEmitter<Hero>();
|
|
||||||
onDelete() {
|
|
||||||
this.deleted.emit(this.hero);
|
|
||||||
}
|
|
||||||
// #enddocregion
|
|
||||||
|
|
||||||
hero: Hero = new Hero('','Zzzzzzzz'); // default sleeping hero
|
hero: Hero = new Hero('','Zzzzzzzz'); // default sleeping hero
|
||||||
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
|
// heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
|
||||||
// Public Domain terms of use: http://www.wpclipart.com/terms.html
|
// Public Domain terms of use: http://www.wpclipart.com/terms.html
|
||||||
heroImageUrl = 'images/hero.png';
|
heroImageUrl = 'images/hero.png';
|
||||||
lineThrough = '';
|
lineThrough = '';
|
||||||
|
@Input() prefix = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -60,7 +61,7 @@ export class HeroDetailComponent {
|
||||||
<div>Web: <a href="{{hero?.url}}" target="_blank">{{hero?.url}}</a></div>
|
<div>Web: <a href="{{hero?.url}}" target="_blank">{{hero?.url}}</a></div>
|
||||||
<div>Rate/hr: {{hero?.rate | currency:'EUR'}}</div>
|
<div>Rate/hr: {{hero?.rate | currency:'EUR'}}</div>
|
||||||
<br clear="all">
|
<br clear="all">
|
||||||
<button (click)="onDelete()">Delete</button>
|
<button (click)="delete()">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
|
@ -68,10 +69,10 @@ export class BigHeroDetailComponent extends HeroDetailComponent {
|
||||||
|
|
||||||
// #docregion input-output-1
|
// #docregion input-output-1
|
||||||
@Input() hero: Hero;
|
@Input() hero: Hero;
|
||||||
@Output() deleted = new EventEmitter<Hero>();
|
@Output() deleteRequest = new EventEmitter<Hero>();
|
||||||
// #enddocregion input-output-1
|
// #enddocregion input-output-1
|
||||||
|
|
||||||
onDelete() {
|
delete() {
|
||||||
this.deleted.emit(this.hero);
|
this.deleteRequest.emit(this.hero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,8 +163,10 @@ table
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'property-binding-11')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'property-binding-11')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'property-binding-6')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'property-binding-6')(format=".")
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'property-binding-12')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'property-binding-12')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'property-binding-vs-interpolation')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'property-binding-7')(format=".")
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'property-binding-13')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'property-binding-13')
|
||||||
|
+makeExample('template-syntax/dart/lib/app_component.html', 'property-binding-vs-interpolation')(format=".")
|
||||||
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'property-binding-14')
|
||||||
|
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'other-bindings-1')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'other-bindings-1')
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'other-bindings-2')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'other-bindings-2')
|
||||||
|
@ -200,7 +202,10 @@ table
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-5')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-5')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'without-NgModel')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'without-NgModel')(format=".")
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-6')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-6')
|
||||||
+makeExample('template-syntax/dart/lib/hero_detail_component.dart', 'deleted', 'lib/hero_detail_component.dart (excerpt)')(format=".")
|
+makeExample('template-syntax/dart/lib/hero_detail_component.dart',
|
||||||
|
'template-1', 'HeroDetailComponent.ts (template)')(format=".")
|
||||||
|
+makeExample('template-syntax/dart/lib/hero_detail_component.dart',
|
||||||
|
'deleteRequest', 'HeroDetailComponent.ts (delete logic)')(format=".")
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-7')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-7')
|
||||||
+makeExample('template-syntax/dart/lib/app_component.html', 'event-binding-to-component')(format=".")
|
+makeExample('template-syntax/dart/lib/app_component.html', 'event-binding-to-component')(format=".")
|
||||||
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-8')
|
+includeShared('../../../ts/latest/guide/template-syntax.jade', 'event-binding-8')
|
||||||
|
|
|
@ -172,8 +172,10 @@ include ../../../../_includes/_util-fns
|
||||||
The view should be stable throughout a single rendering pass.
|
The view should be stable throughout a single rendering pass.
|
||||||
|
|
||||||
#### Quick execution
|
#### Quick execution
|
||||||
Angular executes template expressions more often than we might think.
|
Angular executes template expressions more often than we think.
|
||||||
|
They can be called after every keypress or mouse move.
|
||||||
Expressions should finish quickly or the user experience may drag, especially on slower devices.
|
Expressions should finish quickly or the user experience may drag, especially on slower devices.
|
||||||
|
Consider caching values computed from other values when the computation is expensive.
|
||||||
|
|
||||||
#### Simplicity
|
#### Simplicity
|
||||||
Although it's possible to write quite complex template expressions, we really shouldn't.
|
Although it's possible to write quite complex template expressions, we really shouldn't.
|
||||||
|
@ -565,40 +567,57 @@ table
|
||||||
:marked
|
:marked
|
||||||
If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error.
|
If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error.
|
||||||
|
|
||||||
### Template expressions in property binding
|
### Avoid side effects
|
||||||
As we've already discussed, evaluation of a template expression should have no visible side effects. The expression language itself does its part to keep us safe. We can’t assign a value to anything in a property binding expression nor use the increment and decorator operators.
|
As we've already discussed, evaluation of a template expression should have no visible side effects. The expression language itself does its part to keep us safe. We can’t assign a value to anything in a property binding expression nor use the increment and decorator operators.
|
||||||
|
|
||||||
Of course, our expression might invoke a property or method that has side effects. Angular has no way of knowing that or stopping us.
|
Of course, our expression might invoke a property or method that has side effects. Angular has no way of knowing that or stopping us.
|
||||||
|
|
||||||
The expression could call something like `getFoo()`. Only we know what `getFoo()` does.
|
The expression could call something like `getFoo()`. Only we know what `getFoo()` does.
|
||||||
If `getFoo()` changes something and we happen to be binding to that something, we risk an unpleasant experience. Angular may or may not display the changed value. Angular may detect the change and throw a warning error. Our general advice: stick to data properties and to methods that return values and do no more.
|
If `getFoo()` changes something and we happen to be binding to that something, we risk an unpleasant experience. Angular may or may not display the changed value. Angular may detect the change and throw a warning error. Our general advice: stick to data properties and to methods that return values and do no more.
|
||||||
|
|
||||||
|
### Return the proper type
|
||||||
|
The template expression should evaluate to the type of value expected by the target property.
|
||||||
|
Return a string if the target property expects a string.
|
||||||
|
Return a number if the target property expects a number.
|
||||||
|
Return an object if the target property expects an object.
|
||||||
|
|
||||||
The template expression should evaluate to a value of the type expected by the target property. Most native element properties expect a string. For example, the image `src` should be set to a string that's an URL for the resource providing the image. On the other hand, the `disabled` property of a button expects a Boolean value, so an expression that's assigned to `disabled` should evaluate to `true` or `false`.
|
The `hero` property of the `HeroDetail` component expects a `Hero` object, which is exactly what we’re sending in the property binding:
|
||||||
|
|
||||||
The `hero` property of the `HeroDetail` component expects a `Hero` object, which is exactly what we’re sending in the property binding:
|
|
||||||
// #enddocregion property-binding-10
|
// #enddocregion property-binding-10
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".")
|
||||||
// #docregion property-binding-11
|
// #docregion property-binding-11
|
||||||
:marked
|
:marked
|
||||||
This is good news.
|
### Remember the brackets
|
||||||
If `hero` were an attribute, we could not set it to a `Hero` object.
|
The brackets tell Angular to evaluate the template expression.
|
||||||
|
If we forget the brackets, Angular treats the string as a constant and *initializes the target property* with that string.
|
||||||
|
It does *not* evaluate the string!
|
||||||
|
|
||||||
|
Don't make the following mistake:
|
||||||
// #enddocregion property-binding-11
|
// #enddocregion property-binding-11
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-6')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-6')(format=".")
|
||||||
// #docregion property-binding-12
|
// #docregion property-binding-12
|
||||||
|
a(id="one-time-initialization")
|
||||||
:marked
|
:marked
|
||||||
We can't set an attribute to an object. We can only set it to a string.
|
### One-time string initialization
|
||||||
Internally, the attribute may be able to convert that string to an object before setting the like-named element property.
|
We *should omit the brackets* when
|
||||||
That’s good to know but not helpful to us when we're trying to pass a significant data object
|
* the target property accepts a string value
|
||||||
from one component element to another.
|
* the string is a fixed value that we can bake into the template
|
||||||
The power of property binding is its ability to bypass the attribute and
|
* this initial value never changes
|
||||||
set the element property directly with a value of the appropriate type.
|
|
||||||
|
We routinely initialize attributes this way in standard HTML and it works
|
||||||
|
just as well for directive and component property initialization.
|
||||||
|
In the following example, we initialize the `prefix` property of the `HeroDetailComponent` to a fixed string,
|
||||||
|
not a template expression. Angular sets it and forgets it.
|
||||||
|
// #enddocregion property-binding-12
|
||||||
|
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-7')(format=".")
|
||||||
|
// #docregion property-binding-13
|
||||||
|
:marked
|
||||||
|
The `[hero]` binding, on the other hand, remains a live binding to the component's `currentHero` property.
|
||||||
|
|
||||||
### Property binding or interpolation?
|
### Property binding or interpolation?
|
||||||
We often have a choice between interpolation and property binding. The following binding pairs do the same thing:
|
We often have a choice between interpolation and property binding. The following binding pairs do the same thing:
|
||||||
// #enddocregion property-binding-12
|
// #enddocregion property-binding-13
|
||||||
|
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".")
|
||||||
// #docregion property-binding-13
|
// #docregion property-binding-14
|
||||||
:marked
|
:marked
|
||||||
Interpolation is a convenient alternative for property binding in many cases.
|
Interpolation is a convenient alternative for property binding in many cases.
|
||||||
In fact, Angular translates those interpolations into the corresponding property bindings
|
In fact, Angular translates those interpolations into the corresponding property bindings
|
||||||
|
@ -608,7 +627,7 @@ table
|
||||||
We lean toward readability, which tends to favor interpolation.
|
We lean toward readability, which tends to favor interpolation.
|
||||||
We suggest establishing coding style rules for the organization and choosing the form that
|
We suggest establishing coding style rules for the organization and choosing the form that
|
||||||
both conforms to the rules and feels most natural for the task at hand.
|
both conforms to the rules and feels most natural for the task at hand.
|
||||||
// #enddocregion property-binding-13
|
// #enddocregion property-binding-14
|
||||||
|
|
||||||
// #docregion other-bindings-1
|
// #docregion other-bindings-1
|
||||||
.l-main-section
|
.l-main-section
|
||||||
|
@ -754,7 +773,7 @@ code-example(format="", language="html").
|
||||||
// #docregion event-binding-2
|
// #docregion event-binding-2
|
||||||
:marked
|
:marked
|
||||||
### Target event
|
### Target event
|
||||||
A **name between enclosing parentheses** — for example, `(keyup)` —
|
A **name between enclosing parentheses** — for example, `(click)` —
|
||||||
identifies the target event. In the following example, the target is the button’s click event.
|
identifies the target event. In the following example, the target is the button’s click event.
|
||||||
// #enddocregion event-binding-2
|
// #enddocregion event-binding-2
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".")
|
||||||
|
@ -812,29 +831,36 @@ code-example(format="", language="html").
|
||||||
The directive calls `EventEmitter.emit(payload)` to fire an event, passing in a message payload that can be anything.
|
The directive calls `EventEmitter.emit(payload)` to fire an event, passing in a message payload that can be anything.
|
||||||
Parent directives listen for the event by binding to this property and accessing the payload through the `$event` object.
|
Parent directives listen for the event by binding to this property and accessing the payload through the `$event` object.
|
||||||
|
|
||||||
Consider a `HeroDetailComponent` that produces `deleted` events with an `EventEmitter`.
|
Consider a `HeroDetailComponent` that presents hero information and responds to user actions.
|
||||||
|
Although the `HeroDetailComponent` has a delete button it doesn't know how to delete the hero itself.
|
||||||
|
The best it can do is raise an event reporting the user's delete request.
|
||||||
|
|
||||||
|
Here are the pertinent excerpts from that `HeroDetailComponent`:
|
||||||
// #enddocregion event-binding-6
|
// #enddocregion event-binding-6
|
||||||
+makeExample('template-syntax/ts/app/hero-detail.component.ts',
|
+makeExample('template-syntax/ts/app/hero-detail.component.ts',
|
||||||
'deleted', 'HeroDetailComponent.ts (excerpt)')(format=".")
|
'template-1', 'HeroDetailComponent.ts (template)')(format=".")
|
||||||
|
+makeExample('template-syntax/ts/app/hero-detail.component.ts',
|
||||||
|
'deleteRequest', 'HeroDetailComponent.ts (delete logic)')(format=".")
|
||||||
|
|
||||||
// #docregion event-binding-7
|
// #docregion event-binding-7
|
||||||
:marked
|
:marked
|
||||||
When the user clicks a button, the component invokes the `onDelete()` method, which emits a `Hero` object.
|
The component defines a `deleteRequest` property that returns an `EventEmitter`.
|
||||||
The `HeroDetailComponent` doesn't know how to delete a hero. Its job is to present information and
|
When the user clicks *delete*, the component invokes the `delete()` method
|
||||||
respond to user actions.
|
which tells the `EventEmitter` to emit a `Hero` object.
|
||||||
|
|
||||||
Now imagine a parent component that binds to the `HeroDetailComponent`'s `deleted` event.
|
Now imagine a hosting parent component that binds to the `HeroDetailComponent`'s `deleteRequest` event.
|
||||||
// #enddocregion event-binding-7
|
// #enddocregion event-binding-7
|
||||||
|
|
||||||
+makeExample('template-syntax/ts/app/app.component.html',
|
+makeExample('template-syntax/ts/app/app.component.html',
|
||||||
'event-binding-to-component')(format=".")
|
'event-binding-to-component')(format=".")
|
||||||
// #docregion event-binding-8
|
// #docregion event-binding-8
|
||||||
:marked
|
:marked
|
||||||
When the `deleted` event fires, Angular calls the parent component's `onHeroDeleted` method,
|
When the `deleteRequest` event fires, Angular calls the parent component's `deleteHero` method,
|
||||||
passing the *hero-to-delete* (emitted by `HeroDetail`) in the `$event` variable.
|
passing the *hero-to-delete* (emitted by `HeroDetail`) in the `$event` variable.
|
||||||
|
|
||||||
The `onHeroDeleted` method has a side effect: It deletes a hero.
|
### Template statements have side effects
|
||||||
Side effects are not just OK, they are expected.
|
The `deleteHero` method has a side effect: It deletes a hero.
|
||||||
|
Template statement side effects are not just OK, they are expected.
|
||||||
|
|
||||||
Deleting the hero updates the model, perhaps triggering other changes
|
Deleting the hero updates the model, perhaps triggering other changes
|
||||||
including queries and saves to a remote server.
|
including queries and saves to a remote server.
|
||||||
|
@ -1452,9 +1478,9 @@ figure.image-display
|
||||||
+makeExample('template-syntax/ts/app/app.component.html', 'io-2')(format=".")
|
+makeExample('template-syntax/ts/app/app.component.html', 'io-2')(format=".")
|
||||||
// #docregion inputs-outputs-3
|
// #docregion inputs-outputs-3
|
||||||
:marked
|
:marked
|
||||||
Both `HeroDetailComponent.hero` and `HeroDetailComponent.deleted` are on the **left side** of binding declarations.
|
Both `HeroDetailComponent.hero` and `HeroDetailComponent.deleteRequest` are on the **left side** of binding declarations.
|
||||||
`HeroDetailComponent.hero` is inside brackets; it is the target of a property binding.
|
`HeroDetailComponent.hero` is inside brackets; it is the target of a property binding.
|
||||||
`HeroDetailComponent.deleted` is inside parentheses; it is the target of an event binding.
|
`HeroDetailComponent.deleteRequest` is inside parentheses; it is the target of an event binding.
|
||||||
|
|
||||||
### Declaring input and output properties
|
### Declaring input and output properties
|
||||||
Target properties must be explicitly marked as inputs or outputs.
|
Target properties must be explicitly marked as inputs or outputs.
|
||||||
|
@ -1488,7 +1514,7 @@ figure.image-display
|
||||||
`HeroDetailComponent.hero` is an **input** property from the perspective of `HeroDetailComponent`
|
`HeroDetailComponent.hero` is an **input** property from the perspective of `HeroDetailComponent`
|
||||||
because data flows *into* that property from a template binding expression.
|
because data flows *into* that property from a template binding expression.
|
||||||
|
|
||||||
`HeroDetailComponent.deleted` is an **output** property from the perspective of `HeroDetailComponent`
|
`HeroDetailComponent.deleteRequest` is an **output** property from the perspective of `HeroDetailComponent`
|
||||||
because events stream *out* of that property and toward the handler in a template binding statement.
|
because events stream *out* of that property and toward the handler in a template binding statement.
|
||||||
// #enddocregion inputs-outputs-4
|
// #enddocregion inputs-outputs-4
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Loading…
Reference in New Issue