<!DOCTYPE html>
<html>
<head>
<title>angular2 playground</title>
<link rel="stylesheet" href="style.css" />
<script src="https://code.angularjs.org/2.0.0-beta.17/angular2-polyfills.js"></script>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
</head>
<body>
<my-app>
loading...
</my-app>
</body>
</html>
/* Styles go here */
### Angular2 Starter Plunker - Typescript - RC.0
A simple plunker demonstrating Angular2 usage:
- Uses SystemJS + TypeScript to compile on the fly
- Includes binding, directives, http, pipes, and DI usage.
System.config({
//use typescript for compilation
transpiler: 'typescript',
//typescript compiler options
typescriptOptions: {
emitDecoratorMetadata: true
},
//map tells the System loader where to look for things
map: {
app: "./src",
'@angular': 'https://npmcdn.com/@angular',
'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6'
},
//packages defines our app package
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
'@angular/core': {
main: 'core.umd.js',
defaultExtension: 'js'
},
'@angular/compiler': {
main: 'compiler.umd.js',
defaultExtension: 'js'
},
'@angular/common': {
main: 'common.umd.js',
defaultExtension: 'js'
},
'@angular/platform-browser-dynamic': {
main: 'platform-browser-dynamic.umd.js',
defaultExtension: 'js'
},
'@angular/platform-browser': {
main: 'platform-browser.umd.js',
defaultExtension: 'js'
},
'@angular/router-deprecated': {
main: 'router-deprecated.umd.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
}
}
});
//main entry point
import {bootstrap} from '@angular/platform-browser-dynamic';
import {App} from './app';
import {APP_BASE_HREF} from '@angular/common';
import {provide} from '@angular/core';
import { ROUTER_PROVIDERS } from '@angular/router-deprecated'
bootstrap(App, [ ROUTER_PROVIDERS,provide(APP_BASE_HREF, {useValue : '/' })])
.catch(err => console.error(err));
//our root app component
import {Component} from '@angular/core'
import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated'
import {PageOneComponent} from './PageOneComponent'
import {PageTwoComponent} from './PageTwoComponent'
@Component({
selector: 'my-app',
template: `
<div>
<h2>{{name}}</h2>
<p>Not all components are destroyed properly when routing to a new page.</p>
<p><b>Steps to reproduce:</b></p>
<ol>
<li>Click on Page One to route to PageOne Component</li>
<li>Open your browser console</li>
<li>Click anywhere in the preview to see click event logs</li>
<li>Click on PageTwo link</li>
<li>At this point all 3 child components in pageOne should have been destroyed.</li>
<li>Click anywhere in the preview, u can still see the click event firing. You shouldn't.</li>
</ol>
<p><b>Expected:</b> 3 TestComponents to be destroyed.</p>
<p><b>Result:</b> For some reason only 2 of the 3 child components (TestComponent) in PageOne are destroyed.</p>
<div>
<a [routerLink]="['PageOne']">PageOne</a> | <a [routerLink]="['PageTwo']">PageTwo</a>
</div>
<router-outlet></router-outlet>
</div>
`,
directives: [ROUTER_DIRECTIVES],
providers: [ROUTER_PROVIDERS]
})
@RouteConfig([
{
path: '/pageOne',
name: 'PageOne',
component: PageOneComponent
},
{
path: '/pageTwo',
name: 'PageTwo',
component: PageTwoComponent
}
])
export class App {
constructor() {
this.name = 'Angular 2';
}
}
import {Directive, ElementRef, Input, Output, EventEmitter} from '@angular/core';
@Directive({
selector: '[docClick]',
host: {
// Track mouse events at the global level.
"(document: click)": "handleEvent( $event )",
"(document: mousedown)": "handleEvent( $event )",
"(document: mouseup)": "handleEvent( $event )"
}
})
export class DocClickDirective {
/**
* <div docClick (customClick)="onCustomClick()"></div>
* @type {EventEmitter}
*/
@Output() customClick:EventEmitter<any> = new EventEmitter();
@Output() mousedownOutside:EventEmitter<any> = new EventEmitter();
@Output() mouseupOutside:EventEmitter<any> = new EventEmitter();
/**
* Hold a reference to the outsideElement click element.
*/
private _elementRef:ElementRef;
constructor(el: ElementRef) {
this._elementRef = el;
}
ngOnDestroy() {
//console.log('destroyed docClickDirective');
}
/**
* Global mouse event handler
* @param globalEvent
*/
public handleEvent( globalEvent ) {
// We are only concerned with mouse events that were triggered
// outside of the current host component.
if ( this.eventTriggeredInsideHost( globalEvent ) ) {
return;
}
setTimeout( () => {
console.log('click event still here..');
this["customClick" ].emit( globalEvent );
},0);
}
/**
* I determine if the given event was triggered somewhere within the
* local host component DOM (Document Object Model) tree.
* @param event
* @returns {boolean}
*/
private eventTriggeredInsideHost( event ) {
var current = event.target;
// Reach under the hood to get the actual DOM element that is
// being used to render the component.
var host = this._elementRef.nativeElement;
// Here, we are going to walk up the DOM tree, checking to see
// if we hit the "host" node. If we hit the host node at any
// point, we know that the target must reside within the local
// tree of the host.
do {
// If we hit the host node, we know that the target resides
// within the host component.
if ( current === host ) {
return( true );
}
current = current.parentNode;
} while ( current );
// If we made it this far, we never encountered the host
// component as we walked up the DOM tree. As such, we know that
// the target resided outside of the host component.
return( false );
}
}
//our root app component
import {Component} from '@angular/core'
import {DocClickDirective} from './docClickDirective'
@Component({
selector: 'test-component',
providers: [],
template: `
<div>
<h3>{{name}}</h3>
<div *ngIf="activateDirective"><div docClick (customClick)="clickHandler()">Directive Active</div></div>
</div>
`,
directives: [DocClickDirective]
})
export class TestComponent {
public _activateDirective:boolean = true;
constructor() {
this.name = "Test Component has the docClickDirective";
}
ngOnDestroy(){
console.log('Destroyed Test Component');
}
get activateDirective():boolean{
return this._activateDirective;
}
set activateDirective(val:boolean){
this._activateDirective = val;
}
clickHandler(){
this.activateDirective = true;
}
}
//our root app component
import {Component} from '@angular/core'
@Component({
selector: 'demo-component',
providers: [],
template: `
<div>
<h3>{{name}}</h3>
</div>
`,
directives: []
})
export class DemoComponent {
constructor() {
this.name = "Demo Component";
}
}
//our root app component
import {Component} from '@angular/core'
import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated'
import {PageOneComponent} from './PageOneComponent'
import {PageTwoComponent} from './PageTwoComponent'
@Component({
selector: 'nested-routing-component',
template: `
<div>
<h3>{{name}}</h3>
<div>
<a [routerLink]="['PageOne']">PageOne</a> | <a [routerLink]="['PageTwo']">PageTwo</a>
</div>
<router-outlet></router-outlet>
</div>
`,
directives: [ROUTER_DIRECTIVES],
})
@RouteConfig([
{
path:'/pageOne',
name: 'PageOne',
component: PageOneComponent,
useAsDefault: true
},
{
path: '/pageTwo',
name: 'PageTwo',
component: PageTwoComponent
}
])
export class NestedRoutingComponent {
constructor() {
this.name = 'Nested Routing Component';
}
}
//our root app component
import {Component} from '@angular/core'
import {TestComponent} from './TestComponent'
@Component({
selector: 'page-one-component',
providers: [],
template: `
<div>
<h4>{{name}}</h4>
<test-component *ngFor="let item of items;"></test-component>
</div>
`,
directives: [TestComponent]
})
export class PageOneComponent {
constructor() {
this.name = "Page One Component has 3 components with a custom click event directive.";
this.items = [
{id:1},
{id:2},
{id:3}
];
}
}
//our root app component
import {Component} from '@angular/core'
@Component({
selector: 'page-two-component',
providers: [],
template: `
<div>
<h4>{{name}}</h4>
</div>
`,
directives: []
})
export class PageTwoComponent {
constructor() {
this.name = "Page Two Component";
}
}