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

docs(cb-rating-component): cookbook about creating a rating component #960

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
32 changes: 32 additions & 0 deletions public/docs/_examples/cb-rating-component/e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// <reference path='../_protractor/e2e.d.ts' />
'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');
});
});
});
1 change: 1 addition & 0 deletions public/docs/_examples/cb-rating-component/ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/*.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// #docregion
import { Component } from '@angular/core';

@Component({
selector: 'my-app',
template: `
<label>Windstorm: </label>
<my-hero-rating></my-hero-rating>
`
})
export class AppComponent { }
// #enddocregion
19 changes: 19 additions & 0 deletions public/docs/_examples/cb-rating-component/ts/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component } from '@angular/core';

@Component({
selector: 'my-app',
template: `
<p>
<label>Windstorm: </label>
<my-hero-rating rate="5"></my-hero-rating>
</p>
<p>
<label>Bombasto: </label>
<my-hero-rating [(rate)]="bombasto"></my-hero-rating>
</p>
<div>Bombasto rate is {{bombasto}}</div>
`
})
export class AppComponent {
bombasto = 1;
}
12 changes: 12 additions & 0 deletions public/docs/_examples/cb-rating-component/ts/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -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 {}
5 changes: 5 additions & 0 deletions public/docs/_examples/cb-rating-component/ts/app/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// #docregion
import { Component } from '@angular/core';

@Component({
selector: 'my-hero-rating',
template: '<div>Rating</div>'
})
export class HeroRatingComponent { }
// #enddocregion
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// #docregion
import { Component } from '@angular/core';

@Component({
selector: 'my-hero-rating',
// #docregion template
template: `
<template ngFor [ngForOf]="range" let-index="index">
<span class="sr-only">(*)</span>
<i class="glyphicon glyphicon-star"></i>
</template>
`
// #enddocregion template
})
export class HeroRatingComponent { }
// #enddocregion
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// #docregion
import { Component, Input } from '@angular/core';

@Component({
selector: 'my-hero-rating',
// #docregion template
template: `
<template ngFor [ngForOf]="range" let-index="index">
<span class="sr-only">({{ index < rate ? '*' : ' ' }})</span>
<i class="glyphicon {{index < rate ? 'glyphicon-star' : 'glyphicon-star-empty'}}"></i>
</template>
`
// #enddocregion template
})
export class HeroRatingComponent {
@Input() rate: number;
// #docregion update-method
update(value: number): void {
this.rate = value;
}
// #enddocregion
}
// #enddocregion
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// #docregion
import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
selector: 'my-hero-rating',
// #docregion rating-template
template: `
<template ngFor [ngForOf]="range" let-index="index">
<span class="sr-only">({{ index < rate ? '*' : ' ' }})</span>
<i class="glyphicon {{index < rate ? 'glyphicon-star' : 'glyphicon-star-empty'}}" (click)="update(index + 1)"></i>
</template>
`
// #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<number>();
// #enddocregion rate-output

// #docregion update-method
update(value: number): void {
this.rate = value;
this.rateChange.emit(value);
}
// #enddocregion update-method
}
// #enddocregion
29 changes: 29 additions & 0 deletions public/docs/_examples/cb-rating-component/ts/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<base href="/">
<title>Rating component</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- #docregion style -->
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="styles.css">
<!-- #enddocregion style -->

<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>

<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>

<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>

<body>
<my-app>Loading app...</my-app>
</body>

</html>
9 changes: 9 additions & 0 deletions public/docs/_examples/cb-rating-component/ts/plnkr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"description": "Rating Component",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1,2,3].*"
],
"tags":["cookbook"]
}
5 changes: 5 additions & 0 deletions public/docs/ts/latest/cookbook/_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
128 changes: 128 additions & 0 deletions public/docs/ts/latest/cookbook/rating-component.jade
Original file line number Diff line number Diff line change
@@ -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.
<a id="top"></a>
## 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
<a id="static-component"></a>
: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 `<template>` 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")

<a id="inputs"></a>
: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=".")
&lt;my-hero-rating rate="3"&gt;&lt;/my-hero-rating&gt;
&lt;my-hero-rating [rate]="rate"&gt;&lt;/my-hero-rating&gt;

<a id="clicks"></a>
: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")

<a id="outputs"></a>
: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=".")
&lt;my-hero-rating [(rate)]="rate"&gt;&lt;/my-hero-rating&gt;

: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)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.