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

Commit bd61462

Browse files
committed
docs(cb-rating-component): cookbook about creating a rating component
1 parent 33b5829 commit bd61462

File tree

18 files changed

+328
-0
lines changed

18 files changed

+328
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
describe('Rating compomnent', function () {
2+
3+
beforeAll(function () {
4+
browser.get('');
5+
});
6+
7+
it('should show 5 stars for windstorm', function () {
8+
var windstormRating = element.all(by.tagName('my-hero-rating')).get(0);
9+
var windstormStars = windstormRating.all(by.css('.glyphicon-star'));
10+
expect(windstormStars.count()).toBe(5);
11+
});
12+
13+
it('should show 1 star for bombasto', function() {
14+
var bombastoRating = element.all(by.tagName('my-hero-rating')).get(1);
15+
var bombastoStars = bombastoRating.all(by.css('.glyphicon-star'));
16+
expect(bombastoStars.count()).toBe(1);
17+
});
18+
19+
it('should change the rate on click', function() {
20+
var bombastoRating = element.all(by.tagName('my-hero-rating')).get(1);
21+
var bombastoFourthStar = bombastoRating.all(by.css('.glyphicon')).get(3);
22+
bombastoFourthStar.click().then(function() {
23+
var bombastoStars = bombastoRating.all(by.css('.glyphicon-star'));
24+
expect(bombastoStars.count()).toBe(4);
25+
26+
var div = element.all(by.tagName('div')).get(0);
27+
expect(div.getText()).toEqual('Bombasto rate is 4');
28+
});
29+
});
30+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/*.js
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// #docregion
2+
import { Component } from '@angular/core';
3+
4+
import { HeroRatingComponent } from './rating.component';
5+
6+
@Component({
7+
selector: 'my-app',
8+
template: `
9+
<label>Windstorm: </label>
10+
<my-hero-rating></my-hero-rating>
11+
`,
12+
directives: [HeroRatingComponent]
13+
})
14+
export class AppComponent { }
15+
// #enddocregion
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Component } from '@angular/core';
2+
3+
import { HeroRatingComponent } from './rating.component';
4+
5+
@Component({
6+
selector: 'my-app',
7+
template: `
8+
<p>
9+
<label>Windstorm: </label>
10+
<my-hero-rating rate="5"></my-hero-rating>
11+
</p>
12+
<p>
13+
<label>Bombasto: </label>
14+
<my-hero-rating [(rate)]="bombasto"></my-hero-rating>
15+
</p>
16+
<div>Bombasto rate is {{bombasto}}</div>
17+
`,
18+
directives: [HeroRatingComponent]
19+
})
20+
export class AppComponent {
21+
bombasto: number = 1;
22+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { bootstrap } from '@angular/platform-browser-dynamic';
2+
3+
import { AppComponent } from './app.component';
4+
5+
bootstrap(AppComponent, [])
6+
.catch((err: any) => console.error(err));
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// #docregion
2+
import { Component } from '@angular/core';
3+
4+
@Component({
5+
selector: 'my-hero-rating',
6+
template: '<div>Rating</div>'
7+
})
8+
export class HeroRatingComponent { }
9+
// #enddocregion
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// #docregion
2+
import { Component } from '@angular/core';
3+
4+
@Component({
5+
selector: 'my-hero-rating',
6+
// #docregion template
7+
template: `
8+
<template ngFor [ngForOf]="range" let-index="index">
9+
<span class="sr-only">(*)</span>
10+
<i class="glyphicon glyphicon-star"></i>
11+
</template>
12+
`
13+
// #enddocregion template
14+
})
15+
export class HeroRatingComponent { }
16+
// #enddocregion
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// #docregion
2+
import { Component, Input } from '@angular/core';
3+
4+
@Component({
5+
selector: 'my-hero-rating',
6+
// #docregion template
7+
template: `
8+
<template ngFor [ngForOf]="range" let-index="index">
9+
<span class="sr-only">({{ index < rate ? '*' : ' ' }})</span>
10+
<i class="glyphicon {{index < rate ? 'glyphicon-star' : 'glyphicon-star-empty'}}"></i>
11+
</template>
12+
`
13+
// #enddocregion template
14+
})
15+
export class HeroRatingComponent {
16+
@Input() rate: number;
17+
// #docregion update-method
18+
update(value: number) {
19+
this.rate = value;
20+
}
21+
// #enddocregion
22+
}
23+
// #enddocregion
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// #docregion
2+
import { Component, EventEmitter, Input, Output } from '@angular/core';
3+
4+
@Component({
5+
selector: 'my-hero-rating',
6+
// #docregion rating-template
7+
template: `
8+
<template ngFor [ngForOf]="range" let-index="index">
9+
<span class="sr-only">({{ index < rate ? '*' : ' ' }})</span>
10+
<i class="glyphicon {{index < rate ? 'glyphicon-star' : 'glyphicon-star-empty'}}" (click)="update(index + 1)"></i>
11+
</template>
12+
`
13+
// #enddocregion rating-template
14+
})
15+
export class HeroRatingComponent {
16+
// #docregion range-attribute
17+
range = new Array(5);
18+
// #enddocregion range-attribute
19+
20+
// #docregion rate-input
21+
@Input() rate: number;
22+
// #enddocregion rate-input
23+
// #docregion rate-output
24+
@Output() rateChange: EventEmitter<number> = new EventEmitter<number>();
25+
// #enddocregion rate-output
26+
27+
// #docregion update-method
28+
update(value: number) {
29+
this.rate = value;
30+
this.rateChange.emit(value);
31+
}
32+
// #enddocregion update-method
33+
}
34+
// #enddocregion

public/docs/_examples/cb-rating-component/ts/demo.css

Whitespace-only changes.

public/docs/_examples/cb-rating-component/ts/example-config.json

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<base href="/">
5+
<title>Rating component</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<!-- #docregion style -->
8+
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
9+
<link rel="stylesheet" href="styles.css">
10+
<link rel="stylesheet" href="demo.css">
11+
<!-- #enddocregion style -->
12+
13+
<!-- Polyfill(s) for older browsers -->
14+
<script src="node_modules/core-js/client/shim.min.js"></script>
15+
16+
<script src="node_modules/zone.js/dist/zone.js"></script>
17+
<script src="node_modules/reflect-metadata/Reflect.js"></script>
18+
<script src="node_modules/systemjs/dist/system.src.js"></script>
19+
20+
<script src="systemjs.config.js"></script>
21+
<script>
22+
System.import('app').catch(function(err){ console.error(err); });
23+
</script>
24+
</head>
25+
26+
<body>
27+
<my-app>Loading app...</my-app>
28+
</body>
29+
30+
</html>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"description": "Rating Component",
3+
"files":[
4+
"!**/*.d.ts",
5+
"!**/*.js",
6+
"!**/*.[1,2,3].*"
7+
],
8+
"tags":["cookbook"]
9+
}

public/docs/ts/latest/cookbook/_data.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,10 @@
3939
"ts-to-js": {
4040
"title": "TypeScript to JavaScript",
4141
"intro": "Convert Angular 2 TypeScript examples into ES5 JavaScript"
42+
},
43+
44+
"rating-component": {
45+
"title": "Rating Component",
46+
"intro": "Learn how to write a rating component"
4247
}
4348
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
include ../_util-fns
2+
3+
:marked
4+
In this cookbook we will show how to build a custom rating directive with two-way databinding.
5+
<a id="top"></a>
6+
## Table of contents
7+
8+
[A static rating component](#static-component)
9+
10+
[Adding inputs](#inputs)
11+
12+
[Reacting to clicks](#clicks)
13+
14+
[Adding outputs](#outputs)
15+
16+
:marked
17+
**See the [live example](/resources/live-examples/cb-rating-component/ts/plnkr.html)**.
18+
19+
.l-main-section
20+
<a id="static-component"></a>
21+
:marked
22+
## A static rating component
23+
24+
The first thing we are going to do is to create an static rating component. Let's start with the basic skeleton.
25+
26+
+makeExample('cb-rating-component/ts/app/rating.component.1.ts', null, 'rating.component.ts (skeleton)')(format=".")
27+
28+
:marked
29+
Our next step would be to print our 5 stars. To do that, we need to output some HTML.
30+
For each star, we want to output two elements: We want the star itself which we can see and click, and also an invisible star that we will use for accesibility.
31+
This means that we need `ngFor` to generate not only one element, but two.
32+
33+
What we can do here, is to de-sugar `ngFor` into its complete version. That will allow us to to generate as many elements as we need.
34+
35+
+makeExample('cb-rating-component/ts/app/rating.component.2.ts', 'template', 'rating.component.ts (template)')(format=".")
36+
37+
.l-sub-section
38+
:marked
39+
Learn more about this syntax at the [Template Syntax](../guide/template-syntax.html#!#star-template) chapter and [Structural Directives](../guides/structural-directives.html#!#asterisk).
40+
41+
:marked
42+
So here we say that we want a `<template>` element (the element itself won't output any markup) with the `ngFor` directive in it.
43+
We give to it the collection we want to repeat, `range`, and also that we want a reference to the current index in the collection.
44+
Notice that we aren't getting a reference to the current item on the `range` collection, we don't need it. We just need to generate that template as many times as we need.
45+
46+
We need to create that range collection now.
47+
48+
+makeExample('cb-rating-component/ts/app/rating.component.ts', 'range-attribute', 'rating.component.ts (class)')(format=".")
49+
50+
:marked
51+
Let's update our app component to see it in action.
52+
53+
+makeExample('cb-rating-component/ts/app/app.component.1.ts', null, 'app.component.ts')(format=".")
54+
55+
:marked
56+
If we check our browser now, we can see it working.
57+
58+
figure.image-display
59+
img(src="/resources/images/cookbooks/rating-component/static-component.png" alt="Static Component")
60+
61+
:marked
62+
And also the desired markup.
63+
64+
figure.image-display
65+
img(src="/resources/images/cookbooks/rating-component/generated-output.png" alt="Generated Output")
66+
67+
<a id="inputs"></a>
68+
:marked
69+
## Adding inputs
70+
71+
But not all heroes are as awesome as Windstorm! We should be able to initialize our rating component with a different value. That means that we need an input.
72+
73+
+makeExample('cb-rating-component/ts/app/rating.component.ts', 'rate-input', 'rating.component.ts (class)')(format=".")
74+
75+
:marked
76+
With the input we just need to apply a class or another.
77+
78+
+makeExample('cb-rating-component/ts/app/rating.component.3.ts', 'template', 'rating.component.ts (template)')(format=".")
79+
80+
:marked
81+
Now that we have the input, we can use it with static or dynamic values.
82+
83+
code-example(language="html", format=".")
84+
&lt;my-hero-rating rate="3"&gt;&lt;/my-hero-rating&gt;
85+
&lt;my-hero-rating [rate]="rate"&gt;&lt;/my-hero-rating&gt;
86+
87+
<a id="clicks"></a>
88+
:marked
89+
## Reacting to clicks
90+
91+
Bombasto is not a bad hero, let's make the rating clickable.
92+
93+
+makeExample('cb-rating-component/ts/app/rating.component.ts', 'rating-template', 'rating.component.ts (template)')(format=".")
94+
95+
:marked
96+
By clicking a star, we update our internal `rate` with the new value.
97+
98+
+makeExample('cb-rating-component/ts/app/rating.component.3.ts', 'update-method', 'rating.component.ts (class)')(format=".")
99+
100+
figure.image-display
101+
img(src="/resources/images/cookbooks/rating-component/click.gif" alt="Static Click")
102+
103+
<a id="outputs"></a>
104+
:marked
105+
## Adding outputs
106+
107+
When clicking the rating component, we want the updated rate back, so we need an output.
108+
109+
+makeExample('cb-rating-component/ts/app/rating.component.ts', 'rate-output', 'rating.component.ts (class)')(format=".")
110+
111+
:marked
112+
And we push the new rate in a click.
113+
114+
+makeExample('cb-rating-component/ts/app/rating.component.ts', 'update-method', 'rating.component.ts (class)')(format=".")
115+
116+
:marked
117+
That allows us to do two-way databinding.
118+
119+
code-example(language="html", format=".")
120+
&lt;my-hero-rating [(rate)]="rate"&gt;&lt;/my-hero-rating&gt;
121+
122+
:marked
123+
Here is the complete source for the rating component.
124+
125+
+makeExample('cb-rating-component/ts/app/rating.component.ts', null, 'rating.component.ts')(format=".")
126+
127+
:marked
128+
[Back to top](#top)
Loading
Loading
Loading

0 commit comments

Comments
 (0)