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

Commit 8f017c9

Browse files
bennadelFoxandxss
authored andcommitted
Cookbook for a component item template.
This cookbook demonstrates how to provide a component with an external `TempalteRef` that the component can then use to render aspects of its own internal template. An example of this, as explored in the cookbook, is providing a select-menu component with an item template that the component can use to render the options in its dropdown.
1 parent 5fd6ae3 commit 8f017c9

File tree

11 files changed

+416
-0
lines changed

11 files changed

+416
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/// <reference path='../_protractor/e2e.d.ts' />
2+
'use strict';
3+
// FIRST RUN: gulp run-e2e-tests --filter=cb-item-template
4+
// SUBSEQUENT RUNS: gulp run-e2e-tests --filter=cb-item-template --fast
5+
describe('Item template renderer', function () {
6+
7+
beforeAll(function () {
8+
browser.get('');
9+
});
10+
11+
it('should toggle the menu', function () {
12+
13+
expect( element( by.className( 'select-items' ) ).isPresent() ).toBe( false );
14+
element( by.className( 'select-root' ) ).click();
15+
expect( element( by.className( 'select-items' ) ).isPresent() ).toBe( true );
16+
element( by.className( 'select-root' ) ).click();
17+
expect( element( by.className( 'select-items' ) ).isPresent() ).toBe( false );
18+
19+
});
20+
21+
it('should select last item', function () {
22+
23+
// Make sure all menu roots start out with Black.
24+
element.all( by.className( 'select-root' ) ).each(
25+
function iterator( element ) {
26+
expect( element.getText() ).toContain( 'Black' );
27+
}
28+
);
29+
30+
// Select Magenta.
31+
element( by.className( 'select-root' ) ).click();
32+
element.all( by.css( 'ul.select-items li' ) ).last().click();
33+
34+
// Make sure all menu roots reflect selection (since they are sharing same model).
35+
element.all( by.className( 'select-root' ) ).each(
36+
function iterator( element ) {
37+
expect( element.getText() ).toContain( 'Magenta' );
38+
}
39+
);
40+
41+
});
42+
43+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// #docplaster
2+
// #docregion
3+
import { Component } from '@angular/core';
4+
import { SimpleSelectComponent } from './simple-select.component';
5+
6+
interface Color {
7+
hex: string;
8+
name: string;
9+
}
10+
11+
// #docregion metadata
12+
@Component({
13+
selector: 'my-app',
14+
directives: [ SimpleSelectComponent ],
15+
template:
16+
`
17+
<p>
18+
Selected color: {{ selectedColor?.name || 'None selected' }}.
19+
</p>
20+
21+
<simple-select [items]='colors' [(value)]='selectedColor'>
22+
<template let-item='item'>
23+
<span class='swatch' [style.backgroundColor]='item.hex'></span>
24+
<span class='name'>{{ item.hex }} &mdash; {{ item.name }}</span>
25+
</template>
26+
</simple-select>
27+
28+
<simple-select
29+
[items]='colors'
30+
[(value)]='selectedColor'
31+
[template]='externalTemplate'>
32+
</simple-select>
33+
34+
<template #externalTemplate let-item='item'>
35+
<span class='name' [style.color]='item.hex'>
36+
{{ item.hex }} &mdash; {{ item.name }}
37+
</span>
38+
</template>
39+
`
40+
})
41+
// #enddocregion metadata
42+
export class AppComponent {
43+
44+
public colors: Color[];
45+
public selectedColor: Color;
46+
47+
public constructor() {
48+
this.colors = [
49+
{ hex: '#000000', name: 'Black' },
50+
{ hex: '#FFFFFF', name: 'White' },
51+
{ hex: '#FFD700', name: 'Gold' },
52+
{ hex: '#7FFFD4', name: 'Aquamarine' },
53+
{ hex: '#800080', name: 'Purple' },
54+
{ hex: '#6DC066', name: 'Green' },
55+
{ hex: '#FF00FF', name: 'Magenta' }
56+
];
57+
this.selectedColor = this.colors[ 0 ];
58+
}
59+
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* tslint:disable */
2+
// #docregion
3+
import { bootstrap } from '@angular/platform-browser-dynamic';
4+
import { AppComponent } from './app.component';
5+
6+
bootstrap(AppComponent).then(
7+
() => window.console.info( 'Angular finished bootstrapping your application!' ),
8+
(error) => {
9+
console.warn( 'Angular was not able to bootstrap your application.' );
10+
console.error( error );
11+
}
12+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// #docplaster
2+
// #docregion
3+
import { Component } from '@angular/core';
4+
import { ContentChild } from '@angular/core';
5+
import { EventEmitter } from '@angular/core';
6+
import { Input } from '@angular/core';
7+
import { Output } from '@angular/core';
8+
import { TemplateRef } from '@angular/core';
9+
10+
interface ItemContext {
11+
item: any;
12+
index: number;
13+
}
14+
15+
// #docregion component
16+
// #docregion metadata
17+
@Component({
18+
selector: 'simple-select',
19+
template:
20+
`
21+
<button (click)='toggleItems()' class='select-root'>
22+
23+
<template
24+
[ngTemplateOutlet]='itemTemplateRef'
25+
[ngOutletContext]='{ item: value, index: -1 }'>
26+
</template>
27+
28+
</button>
29+
30+
<ul *ngIf='isShowingItems' class='select-items'>
31+
<li *ngFor='let item of items ; let index = index ;' (click)='selectItem( item )'>
32+
33+
<template
34+
[ngTemplateOutlet]='itemTemplateRef'
35+
[ngOutletContext]='{ item: item, index: index }'>
36+
</template>
37+
38+
</li>
39+
</ul>
40+
`
41+
})
42+
// #enddocregion metadata
43+
export class SimpleSelectComponent {
44+
45+
public isShowingItems: boolean;
46+
@Input() public items: any[];
47+
public itemTemplateRef: TemplateRef<ItemContext>;
48+
@Input() public value: any;
49+
@Output() public valueChange: EventEmitter<any>;
50+
51+
constructor() {
52+
this.isShowingItems = false;
53+
this.items = [];
54+
this.itemTemplateRef = null;
55+
this.value = null;
56+
this.valueChange = new EventEmitter();
57+
}
58+
59+
// #docregion setters
60+
@Input()
61+
set template( newTemplate: TemplateRef<ItemContext> ) {
62+
if ( newTemplate ) {
63+
this.itemTemplateRef = newTemplate;
64+
}
65+
}
66+
67+
@ContentChild( TemplateRef )
68+
set contentChildTemplateRef( newTemplate: TemplateRef<ItemContext> ) {
69+
if ( newTemplate ) {
70+
this.itemTemplateRef = newTemplate;
71+
}
72+
}
73+
// #enddocregion setters
74+
75+
public selectItem( item: any ): void {
76+
this.valueChange.emit( item );
77+
this.toggleItems();
78+
}
79+
80+
public toggleItems(): void {
81+
this.isShowingItems = ! this.isShowingItems;
82+
}
83+
84+
}
85+
// #enddocregion component

public/docs/_examples/cb-item-template/ts/example-config.json

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset='utf-8'>
5+
<meta name='viewport' content='width=device-width, initial-scale=1'>
6+
7+
<title>
8+
Providing An Item Template To A Component
9+
</title>
10+
11+
<link rel='stylesheet' type='text/css' href='styles.css'>
12+
<link rel='stylesheet' type='text/css' href='sample.css'>
13+
14+
<!-- Polyfill(s) for older browsers -->
15+
<script src='node_modules/core-js/client/shim.min.js'></script>
16+
17+
<script src='node_modules/zone.js/dist/zone.js'></script>
18+
<script src='node_modules/reflect-metadata/Reflect.js'></script>
19+
<script src='node_modules/systemjs/dist/system.src.js'></script>
20+
21+
<script src='systemjs.config.js'></script>
22+
<script>
23+
System.import('app')
24+
.then(function() { console.info( 'System.js loaded your application module.' )})
25+
.catch(function(err){ console.error(err); });
26+
</script>
27+
</head>
28+
<body>
29+
30+
<h1>
31+
Providing An Item Template To A Component
32+
</h1>
33+
34+
<my-app>
35+
Loading app...
36+
</my-app>
37+
38+
</body>
39+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"description": "Set The Document Title In Angular 2",
3+
"files": [
4+
"!**/*.d.ts",
5+
"!**/*.js",
6+
"!**/*.[1].*"
7+
],
8+
"tags": [ "cookbook" ]
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
a {
2+
color: #607D8B ;
3+
text-decoration: underline ;
4+
}
5+
6+
simple-select {
7+
cursor: pointer ;
8+
display: 'inline-block' ;
9+
font-size: 16px ;
10+
position: relative ;
11+
user-select: none ;
12+
-moz-user-select: none ;
13+
-webkit-user-select: none ;
14+
}
15+
16+
simple-select button.select-root {
17+
background-color: #FFFFFF ;
18+
border: 1px solid #CCCCCC ;
19+
border-radius: 3px 3px 3px 3px ;
20+
color: #333333 ;
21+
font-size: inherit ;
22+
padding: 7px 10px 7px 10px ;
23+
position: relative ;
24+
}
25+
26+
simple-select ul.select-items {
27+
background-color: #FFFFFF ;
28+
border: 1px solid #CCCCCC ;
29+
border-radius: 3px 3px 3px 3px ;
30+
left: 0px ;
31+
list-style-type: none ;
32+
margin: 0px 0px 0px 0px ;
33+
padding: 0px 0px 0px 0px ;
34+
position: absolute ;
35+
top: 102% ;
36+
white-space: nowrap ;
37+
z-index: 2 ;
38+
}
39+
40+
simple-select ul.select-items li {
41+
border-top: 1px solid #CCCCCC ;
42+
margin: 0px 0px 0px 0px ;
43+
padding: 7px 10px 7px 10px ;
44+
position: relative ;
45+
}
46+
47+
simple-select ul.select-items li:first-child {
48+
border-top-width: 0px ;
49+
}
50+
51+
/* Custom template styles. */
52+
53+
simple-select {
54+
display: table ;
55+
margin: 16px 0px 16px 0px ;
56+
}
57+
58+
simple-select span.swatch {
59+
border: 1px solid #CCCCCC ;
60+
border-radius: 3px 3px 3px 3px ;
61+
display: inline-block ;
62+
height: 13px ;
63+
margin: 2px 5px 0px 0px ;
64+
width: 13px ;
65+
}

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

+5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
"intro": "Migrate your RC4 app to RC5 in minutes."
4949
},
5050

51+
"item-template": {
52+
"title": "Item Template",
53+
"intro": "Providing a component with an item template for dynamic rendering."
54+
},
55+
5156
"set-document-title": {
5257
"title": "Set the Document Title",
5358
"intro": "Setting the document or window title using the Title service."

0 commit comments

Comments
 (0)