Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

example(template-syntax): follow style-guide and other updates #2750

Merged
merged 4 commits into from
Nov 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions public/docs/_examples/template-syntax/e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,13 @@ describe('Template Syntax', function () {
expect(specialButtonEle.getAttribute('style')).toMatch('color: red');
});

it('should two-way bind to sizer', function () {
let buttons = element.all(by.css('div#two-way-1 my-sizer button'));
let input = element(by.css('input#fontsize'));

input.getAttribute('value').then(size => {
buttons.get(1).click();
browser.waitForAngular();
expect(input.getAttribute('value')).toEqual((+size + 1).toString());
});
it('should two-way bind to sizer', async () => {
let div = element(by.css('div#two-way-1'));
let incButton = div.element(by.buttonText('+'));
let input = div.element(by.css('input'));
let initSize = await input.getAttribute('value');
incButton.click();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that we don't have to wait for the browser to settle after clicking the button. That line had a purpose at one time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, e2e tests run fine:

Suites passed:
  public/docs/_examples/template-syntax/ts
All tests passed

expect(input.getAttribute('value')).toEqual((+initSize + 1).toString());
});
});

27 changes: 20 additions & 7 deletions public/docs/_examples/template-syntax/ts/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,10 @@ <h3>

<div>
<!-- #docregion event-binding-3 -->
<!-- `myClick` is an event on the custom `MyClickDirective` -->
<!-- #docregion my-click -->
<!-- `myClick` is an event on the custom `ClickDirective` -->
<!-- #docregion myClick -->
<div (myClick)="clickMessage=$event">click with myClick</div>
<!-- #enddocregion my-click -->
<!-- #enddocregion myClick -->
<!-- #enddocregion event-binding-3 -->
{{clickMessage}}
</div>
Expand Down Expand Up @@ -351,21 +351,22 @@ <h3>
<hr><h2 id="two-way">Two-way Binding</h2>
<div id="two-way-1">
<!-- #docregion two-way-1 -->
<my-sizer [(size)]="fontSize"></my-sizer>
<div [style.font-size.px]="fontSize">Resizable Text</div>
<my-sizer [(size)]="fontSizePx"></my-sizer>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems picayune. I feel it distracts from the point and starts a conversation that isn't helpful. I can understand that you feel that being inspecific about the units in the property name provokes a distracting conversation too. I don't actually feel strongly about this so I'd say "whatever, OK" .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is mainly to avoid a conflict with a string property named fontSize whose initial value is "large".

It seemed to me that since the property is being assigned to [style.font-size.px] (see the old line 355 above) it could naturally be named fontSizePx (which is the camel-case equivalent of the style attribute it is being assigned to).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm cool with it

<div [style.font-size.px]="fontSizePx">Resizable Text</div>
<!-- #enddocregion two-way-1 -->
<label>FontSize: <input id="fontsize" [(ngModel)]="fontSize"></label>
<label>FontSize (px): <input [(ngModel)]="fontSizePx"></label>
</div>
<br>
<div id="two-way-2">
<h3>De-sugared two-way binding</h3>
<!-- #docregion two-way-2 -->
<my-sizer [size]="fontSize" (sizeChange)="fontSize=$event"></my-sizer>
<my-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></my-sizer>
<!-- #enddocregion two-way-2 -->
</div>
<br><br>

<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>
Expand Down Expand Up @@ -428,6 +429,18 @@ <h3>Result: {{currentHero.firstName}}</h3>
<!-- NgStyle binding -->
<hr><h2 id="ngStyle">NgStyle Binding</h2>

<!-- #docregion NgStyle -->
<div>
<p [ngStyle]="setStyle()" #styleP>Change style of this text!</p>

<label>Italic: <input type="checkbox" [(ngModel)]="isItalic"></label> |
<label>Bold: <input type="checkbox" [(ngModel)]="isBold"></label> |
<label>Size: <input type="text" [(ngModel)]="fontSize"></label>

<p>Style set to: <code>'{{styleP.style.cssText}}'</code></p>
</div>
<!-- #enddocregion NgStyle -->

<!-- #docregion NgStyle-1 -->
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
This div is x-large.
Expand Down
17 changes: 15 additions & 2 deletions public/docs/_examples/template-syntax/ts/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ export class AppComponent implements AfterViewInit, OnInit {
this.alert('Deleted hero: ' + (hero && hero.firstName));
}

fontSize = 10;

// #docregion evil-title
evilTitle = 'Template <script>alert("evil never sleeps")</script>Syntax';
// #enddocregion evil-title
Expand Down Expand Up @@ -180,6 +178,21 @@ export class AppComponent implements AfterViewInit, OnInit {
}
// #enddocregion setStyles

// #docregion NgStyle
isItalic = false;
isBold = false;
fontSize: string = 'large';
fontSizePx: number | string = 14;

setStyle() {
return {
'font-style': this.isItalic ? 'italic' : 'normal',
'font-weight': this.isBold ? 'bold' : 'normal',
'font-size': this.fontSize
};
}
// #enddocregion NgStyle

toeChoice = '';
toeChooser(picker: HTMLFieldSetElement) {
let choices = picker.children;
Expand Down
6 changes: 3 additions & 3 deletions public/docs/_examples/template-syntax/ts/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { BigHeroDetailComponent, HeroDetailComponent } from './hero-detail.component';
import { MyClickDirective, MyClickDirective2 } from './my-click.directive';
import { ClickDirective, ClickDirective2 } from './click.directive';
import { SizerComponent } from './sizer.component';

@NgModule({
Expand All @@ -16,8 +16,8 @@ import { SizerComponent } from './sizer.component';
AppComponent,
BigHeroDetailComponent,
HeroDetailComponent,
MyClickDirective,
MyClickDirective2,
ClickDirective,
ClickDirective2,
SizerComponent
],
bootstrap: [ AppComponent ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';

@Directive({selector: '[myClick]'})
export class MyClickDirective {
// #docregion my-click-output-1
export class ClickDirective {
// #docregion output-myClick
@Output('myClick') clicks = new EventEmitter<string>(); // @Output(alias) propertyName = ...
// #enddocregion my-click-output-1
// #enddocregion output-myClick

toggle = false;

Expand All @@ -19,15 +19,15 @@ export class MyClickDirective {
}
}

// #docregion my-click-output-2
// #docregion output-myClick2
@Directive({
// #enddocregion my-click-output-2
// #enddocregion output-myClick2
selector: '[myClick2]',
// #docregion my-click-output-2
// #docregion output-myClick2
outputs: ['clicks:myClick'] // propertyName:alias
})
// #enddocregion my-click-output-2
export class MyClickDirective2 {
// #enddocregion output-myClick2
export class ClickDirective2 {
clicks = new EventEmitter<string>();
toggle = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
</div>`
})
export class SizerComponent {
@Input() size: number;
@Input() size: number | string;
@Output() sizeChange = new EventEmitter<number>();

dec() { this.resize(-1); }
inc() { this.resize(+1); }

resize(delta: number) {
const size = +this.size + delta;
this.size = Math.min(40, Math.max(8, size));
this.size = Math.min(40, Math.max(8, +this.size + delta));
this.sizeChange.emit(this.size);
}
}
55 changes: 30 additions & 25 deletions public/docs/ts/latest/guide/template-syntax.jade
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,8 @@ table
If we must read a target element property or call one of its methods,
we'll need a different technique.
See the API reference for
[viewChild](../api/core/index/ViewChild-decorator.html) and
[contentChild](../api/core/index/ContentChild-decorator.html).
[ViewChild](../api/core/index/ViewChild-decorator.html) and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

[ContentChild](../api/core/index/ContentChild-decorator.html).

:marked
### Binding target
Expand Down Expand Up @@ -581,7 +581,7 @@ a(id="one-time-initialization")


:marked
#### Content Security
#### Content security
Imagine the following *malicious content*.
+makeExample('template-syntax/ts/app/app.component.ts', 'evil-title')(format=".")
:marked
Expand All @@ -599,10 +599,10 @@ figure.image-display
.l-main-section
:marked
<a id="other-bindings"></a>
## Attribute, Class, and Style Bindings
## Attribute, class, and style bindings
The template syntax provides specialized one-way bindings for scenarios less well suited to property binding.

### Attribute Binding
### Attribute binding
We can set the value of an attribute directly with an **attribute binding**.
.l-sub-section
:marked
Expand Down Expand Up @@ -652,7 +652,7 @@ code-example(format="nocode").
is to set ARIA attributes, as in this example:
+makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-aria')(format=".")
:marked
### Class Binding
### Class binding

We can add and remove CSS class names from an element’s `class` attribute with
a **class binding**.
Expand All @@ -668,9 +668,6 @@ code-example(format="nocode").
We can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding.
+makeExample('template-syntax/ts/app/app.component.html', 'class-binding-2')(format=".")

block dart-class-binding-bug
//- N/A

:marked
Finally, we can bind to a specific class name.
Angular adds the class when the template expression evaluates to #{_truthy}.
Expand All @@ -683,7 +680,7 @@ block dart-class-binding-bug
we generally prefer the [NgClass directive](#ngClass) for managing multiple class names at the same time.

:marked
### Style Binding
### Style binding

We can set inline styles with a **style binding**.

Expand Down Expand Up @@ -747,7 +744,7 @@ block style-property-name-dart-diff
on [aliasing input/output properties](#aliasing-io).

:marked
If the name fails to match element event or output property of a known directive,
If the name fails to match an element event or an output property of a known directive,
Angular reports an “unknown directive” error.

### *$event* and event handling statements
Expand Down Expand Up @@ -778,7 +775,7 @@ block style-property-name-dart-diff

<a id="eventemitter"></a>
<a id="custom-event"></a>
### Custom Events with EventEmitter
### Custom events with *EventEmitter*

Directives typically raise custom events with an Angular [EventEmitter](../api/core/index/EventEmitter-class.html).
The directive creates an `EventEmitter` and exposes it as a property.
Expand Down Expand Up @@ -853,36 +850,44 @@ block style-property-name-dart-diff

Angular offers a special _two-way data binding_ syntax for this purpose, **`[(x)]`**.
The `[(x)]` syntax combines the brackets
of _Property Binding_, `[x]`, with the parentheses of _Event Binding_, `(x)`.
of _property binding_, `[x]`, with the parentheses of _event binding_, `(x)`.

.callout.is-important
header [( )] = banana in a box
:marked
Visualize a *banana in a box* to remember that the parentheses go _inside_ the brackets.

:marked
The `[(x)]` syntax is easy to demonstrate when the element has a settable property called `x`
and a corresponding event named `xChange`.
Here's a `SizerComponent` that fits the pattern.
It has a `size` value property and a companion `sizeChange` event:
+makeExample('template-syntax/ts/app/sizer.component.ts', null, 'app/sizer.component.ts')

+makeExample('app/sizer.component.ts')

:marked
The initial `size` is an input value from a property binding.
Clicking the buttons increases or decreases the `size`, within min/max values constraints,
and then raises (_emits_) the `sizeChange` event with the adjusted size.

Here's an example in which the `AppComponent.fontSize` is two-way bound to the `SizerComponent`:
+makeExample('template-syntax/ts/app/app.component.html', 'two-way-1')(format=".")
Here's an example in which the `AppComponent.fontSizePx` is two-way bound to the `SizerComponent`:

+makeExcerpt('app/app.component.html', 'two-way-1', '')

:marked
The `AppComponent.fontSize` establishes the initial `SizerComponent.size` value.
Clicking the buttons updates the `AppComponent.fontSize` via the two-way binding.
The revised `AppComponent.fontSize` value flows through to the _style_ binding, making the displayed text bigger or smaller.
Try it in the <live-example>live example</live-example>.
The `AppComponent.fontSizePx` establishes the initial `SizerComponent.size` value.
Clicking the buttons updates the `AppComponent.fontSizePx` via the two-way binding.
The revised `AppComponent.fontSizePx` value flows through to the _style_ binding, making the displayed text bigger or smaller.
Try it in the <live-example></live-example>.

The two-way binding syntax is really just syntactic sugar for a _property_ binding and an _event_ binding.
Angular _desugars_ the `SizerComponent` binding into this:
+makeExample('template-syntax/ts/app/app.component.html', 'two-way-2')(format=".")

+makeExcerpt('app/app.component.html', 'two-way-2', '')

:marked
The `$event` variable contains the payload of the `SizerComponent.sizeChange` event.
Angular assigns the `$event` value to the `AppComponent.fontSize` when the user clicks the buttons.
Angular assigns the `$event` value to the `AppComponent.fontSizePx` when the user clicks the buttons.

Clearly the two-way binding syntax is a great convenience compared to separate property and event bindings.

Expand Down Expand Up @@ -1418,7 +1423,7 @@ h3#aliasing-io Aliasing input/output properties
Directive consumers expect to bind to the name of the directive.
For example, when we apply a directive with a `myClick` selector to a `<div>` tag,
we expect to bind to an event property that is also called `myClick`.
+makeExample('template-syntax/ts/app/app.component.html', 'my-click')(format=".")
+makeExample('template-syntax/ts/app/app.component.html', 'myClick')(format=".")
:marked
However, the directive name is often a poor choice for the name of a property within the directive class.
The directive name rarely describes what the property does.
Expand All @@ -1431,14 +1436,14 @@ h3#aliasing-io Aliasing input/output properties

We can specify the alias for the property name by passing it into the input/output decorator like this:

+makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-1')(format=".")
+makeExample('template-syntax/ts/app/click.directive.ts', 'output-myClick')(format=".")

.l-sub-section
:marked
We can also alias property names in the `inputs` and `outputs` #{_array}s.
We write a colon-delimited (`:`) string with
the directive property name on the *left* and the public alias on the *right*:
+makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-2')(format=".")
+makeExample('template-syntax/ts/app/click.directive.ts', 'output-myClick2')(format=".")

<a id="expression-operators"></a>
.l-main-section
Expand Down