diff --git a/public/docs/_examples/cb-rating-component/e2e-spec.ts b/public/docs/_examples/cb-rating-component/e2e-spec.ts
new file mode 100644
index 0000000000..1e4c28a35d
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/e2e-spec.ts
@@ -0,0 +1,32 @@
+///
+'use strict';
+describe('Rating component', function () {
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ it('should show 5 stars for windstorm', function () {
+ const windstormRating = element.all(by.tagName('my-hero-rating')).get(0);
+ const windstormStars = windstormRating.all(by.css('.glyphicon-star'));
+ expect(windstormStars.count()).toBe(5);
+ });
+
+ it('should show 1 star for bombasto', function() {
+ const bombastoRating = element.all(by.tagName('my-hero-rating')).get(1);
+ const bombastoStars = bombastoRating.all(by.css('.glyphicon-star'));
+ expect(bombastoStars.count()).toBe(1);
+ });
+
+ it('should change the rate on click', function() {
+ const bombastoRating = element.all(by.tagName('my-hero-rating')).get(1);
+ const bombastoFourthStar = bombastoRating.all(by.css('.glyphicon')).get(3);
+ bombastoFourthStar.click().then(function() {
+ const bombastoStars = bombastoRating.all(by.css('.glyphicon-star'));
+ expect(bombastoStars.count()).toBe(4);
+
+ const div = element.all(by.tagName('div')).get(0);
+ expect(div.getText()).toEqual('Bombasto rate is 4');
+ });
+ });
+});
diff --git a/public/docs/_examples/cb-rating-component/ts/.gitignore b/public/docs/_examples/cb-rating-component/ts/.gitignore
new file mode 100644
index 0000000000..cf44e148ba
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/.gitignore
@@ -0,0 +1 @@
+**/*.js
\ No newline at end of file
diff --git a/public/docs/_examples/cb-rating-component/ts/app/app.component.1.ts b/public/docs/_examples/cb-rating-component/ts/app/app.component.1.ts
new file mode 100644
index 0000000000..c90943d04a
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/app.component.1.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+
+
+ `
+})
+export class AppComponent { }
+// #enddocregion
diff --git a/public/docs/_examples/cb-rating-component/ts/app/app.component.ts b/public/docs/_examples/cb-rating-component/ts/app/app.component.ts
new file mode 100644
index 0000000000..31c22ac4e9
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/app.component.ts
@@ -0,0 +1,19 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+
+
+
+
+
+
+
+
+
Bombasto rate is {{bombasto}}
+ `
+})
+export class AppComponent {
+ bombasto = 1;
+}
diff --git a/public/docs/_examples/cb-rating-component/ts/app/app.module.ts b/public/docs/_examples/cb-rating-component/ts/app/app.module.ts
new file mode 100644
index 0000000000..6539ed66e3
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/app.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppComponent } from './app.component';
+import { HeroRatingComponent } from './rating.component';
+
+@NgModule({
+ imports: [ BrowserModule ],
+ declarations: [ AppComponent, HeroRatingComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/cb-rating-component/ts/app/main.ts b/public/docs/_examples/cb-rating-component/ts/app/main.ts
new file mode 100644
index 0000000000..9be7775f4d
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/main.ts
@@ -0,0 +1,5 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/cb-rating-component/ts/app/rating.component.1.ts b/public/docs/_examples/cb-rating-component/ts/app/rating.component.1.ts
new file mode 100644
index 0000000000..a6d446dc0f
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/rating.component.1.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-hero-rating',
+ template: '
Rating
'
+})
+export class HeroRatingComponent { }
+// #enddocregion
diff --git a/public/docs/_examples/cb-rating-component/ts/app/rating.component.2.ts b/public/docs/_examples/cb-rating-component/ts/app/rating.component.2.ts
new file mode 100644
index 0000000000..357bdd5d5f
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/rating.component.2.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-hero-rating',
+ // #docregion template
+ template: `
+
+ (*)
+
+
+ `
+ // #enddocregion template
+})
+export class HeroRatingComponent { }
+// #enddocregion
diff --git a/public/docs/_examples/cb-rating-component/ts/app/rating.component.3.ts b/public/docs/_examples/cb-rating-component/ts/app/rating.component.3.ts
new file mode 100644
index 0000000000..c1ab41bda5
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/rating.component.3.ts
@@ -0,0 +1,23 @@
+// #docregion
+import { Component, Input } from '@angular/core';
+
+@Component({
+ selector: 'my-hero-rating',
+ // #docregion template
+ template: `
+
+ ({{ index < rate ? '*' : ' ' }})
+
+
+ `
+ // #enddocregion template
+})
+export class HeroRatingComponent {
+ @Input() rate: number;
+ // #docregion update-method
+ update(value: number): void {
+ this.rate = value;
+ }
+ // #enddocregion
+}
+// #enddocregion
diff --git a/public/docs/_examples/cb-rating-component/ts/app/rating.component.ts b/public/docs/_examples/cb-rating-component/ts/app/rating.component.ts
new file mode 100644
index 0000000000..cd1ecb3eb2
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/app/rating.component.ts
@@ -0,0 +1,34 @@
+// #docregion
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+ selector: 'my-hero-rating',
+ // #docregion rating-template
+ template: `
+
+ ({{ index < rate ? '*' : ' ' }})
+
+
+ `
+ // #enddocregion rating-template
+})
+export class HeroRatingComponent {
+ // #docregion range-attribute
+ range = new Array(5);
+ // #enddocregion range-attribute
+
+ // #docregion rate-input
+ @Input() rate: number;
+ // #enddocregion rate-input
+ // #docregion rate-output
+ @Output() rateChange = new EventEmitter();
+ // #enddocregion rate-output
+
+ // #docregion update-method
+ update(value: number): void {
+ this.rate = value;
+ this.rateChange.emit(value);
+ }
+ // #enddocregion update-method
+}
+// #enddocregion
diff --git a/public/docs/_examples/cb-rating-component/ts/example-config.json b/public/docs/_examples/cb-rating-component/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/cb-rating-component/ts/index.html b/public/docs/_examples/cb-rating-component/ts/index.html
new file mode 100644
index 0000000000..fd713af1fb
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+ Rating component
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading app...
+
+
+
diff --git a/public/docs/_examples/cb-rating-component/ts/plnkr.json b/public/docs/_examples/cb-rating-component/ts/plnkr.json
new file mode 100644
index 0000000000..1cc9178595
--- /dev/null
+++ b/public/docs/_examples/cb-rating-component/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Rating Component",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1,2,3].*"
+ ],
+ "tags":["cookbook"]
+}
\ No newline at end of file
diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json
index f82d816cef..37afa34272 100644
--- a/public/docs/ts/latest/cookbook/_data.json
+++ b/public/docs/ts/latest/cookbook/_data.json
@@ -43,6 +43,11 @@
"intro": "Render dynamic forms with FormGroup"
},
+ "rating-component": {
+ "title": "Rating Component",
+ "intro": "Learn how to write a rating component"
+ },
+
"rc4-to-rc5": {
"title": "RC4 to RC5 Migration",
"intro": "Migrate your RC4 app to RC5 in minutes."
diff --git a/public/docs/ts/latest/cookbook/rating-component.jade b/public/docs/ts/latest/cookbook/rating-component.jade
new file mode 100644
index 0000000000..1dbb20bfe4
--- /dev/null
+++ b/public/docs/ts/latest/cookbook/rating-component.jade
@@ -0,0 +1,128 @@
+include ../_util-fns
+
+:marked
+ In this cookbook we will show how to build a custom rating directive with two-way databinding.
+
+ ## Table of contents
+
+ [A static rating component](#static-component)
+
+ [Adding inputs](#inputs)
+
+ [Reacting to clicks](#clicks)
+
+ [Adding outputs](#outputs)
+
+:marked
+ **See the [live example](/resources/live-examples/cb-rating-component/ts/plnkr.html)**.
+
+.l-main-section
+
+:marked
+ ## A static rating component
+
+ The first thing we are going to do is to create an static rating component. Let's start with the basic skeleton.
+
++makeExample('cb-rating-component/ts/app/rating.component.1.ts', null, 'rating.component.ts (skeleton)')(format=".")
+
+:marked
+ Our next step would be to print our 5 stars. To do that, we need to output some HTML.
+ 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 accessibility.
+ This means that we need `ngFor` to generate not only one element, but two.
+
+ 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.
+
++makeExample('cb-rating-component/ts/app/rating.component.2.ts', 'template', 'rating.component.ts (template)')(format=".")
+
+.l-sub-section
+ :marked
+ Learn more about this syntax at the [Template Syntax](../guide/template-syntax.html#!#star-template) chapter and [Structural Directives](../guides/structural-directives.html#!#asterisk).
+
+:marked
+ So here we say that we want a `` element (the element itself won't output any markup) with the `ngFor` directive in it.
+ 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.
+ 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.
+
+ We need to create that range collection now.
+
++makeExample('cb-rating-component/ts/app/rating.component.ts', 'range-attribute', 'rating.component.ts (class)')(format=".")
+
+:marked
+ Let's update our app component to see it in action.
+
++makeExample('cb-rating-component/ts/app/app.component.1.ts', null, 'app.component.ts')(format=".")
+
+:marked
+ If we check our browser now, we can see it working.
+
+figure.image-display
+ img(src="/resources/images/cookbooks/rating-component/static-component.png" alt="Static Component")
+
+:marked
+ And also the desired markup.
+
+figure.image-display
+ img(src="/resources/images/cookbooks/rating-component/generated-output.png" alt="Generated Output")
+
+
+:marked
+ ## Adding inputs
+
+ 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.
+
++makeExample('cb-rating-component/ts/app/rating.component.ts', 'rate-input', 'rating.component.ts (class)')(format=".")
+
+:marked
+ With the input we just need to apply a class or another.
+
++makeExample('cb-rating-component/ts/app/rating.component.3.ts', 'template', 'rating.component.ts (template)')(format=".")
+
+:marked
+ Now that we have the input, we can use it with static or dynamic values.
+
+code-example(language="html", format=".")
+ <my-hero-rating rate="3"></my-hero-rating>
+ <my-hero-rating [rate]="rate"></my-hero-rating>
+
+
+:marked
+ ## Reacting to clicks
+
+ Bombasto is not a bad hero, let's make the rating clickable.
+
++makeExample('cb-rating-component/ts/app/rating.component.ts', 'rating-template', 'rating.component.ts (template)')(format=".")
+
+:marked
+ By clicking a star, we update our internal `rate` with the new value.
+
++makeExample('cb-rating-component/ts/app/rating.component.3.ts', 'update-method', 'rating.component.ts (class)')(format=".")
+
+figure.image-display
+ img(src="/resources/images/cookbooks/rating-component/click.gif" alt="Static Click")
+
+
+:marked
+ ## Adding outputs
+
+ When clicking the rating component, we want the updated rate back, so we need an output.
+
++makeExample('cb-rating-component/ts/app/rating.component.ts', 'rate-output', 'rating.component.ts (class)')(format=".")
+
+:marked
+ And we push the new rate in a click.
+
++makeExample('cb-rating-component/ts/app/rating.component.ts', 'update-method', 'rating.component.ts (class)')(format=".")
+
+:marked
+ That allows us to do two-way databinding.
+
+code-example(language="html", format=".")
+ <my-hero-rating [(rate)]="rate"></my-hero-rating>
+
+:marked
+ Here is the complete source for the rating component.
+
++makeExample('cb-rating-component/ts/app/rating.component.ts', null, 'rating.component.ts')(format=".")
+
+:marked
+ [Back to top](#top)
diff --git a/public/resources/images/cookbooks/rating-component/click.gif b/public/resources/images/cookbooks/rating-component/click.gif
new file mode 100644
index 0000000000..7f60ea754f
Binary files /dev/null and b/public/resources/images/cookbooks/rating-component/click.gif differ
diff --git a/public/resources/images/cookbooks/rating-component/generated-output.png b/public/resources/images/cookbooks/rating-component/generated-output.png
new file mode 100644
index 0000000000..0da1138245
Binary files /dev/null and b/public/resources/images/cookbooks/rating-component/generated-output.png differ
diff --git a/public/resources/images/cookbooks/rating-component/static-component.png b/public/resources/images/cookbooks/rating-component/static-component.png
new file mode 100644
index 0000000000..80b629ea29
Binary files /dev/null and b/public/resources/images/cookbooks/rating-component/static-component.png differ