<!DOCTYPE html>
<html>
<head>
<base href="." />
<title>angular2 playground</title>
<script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js/dist/zone.js"></script>
<script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
<script src="https://unpkg.com/systemjs@0.19.31/dist/system.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>
### Angular Starter Plunker - Typescript
var ngVer = '@2.3.1';
System.config({
//use typescript for compilation
transpiler: 'typescript',
//typescript compiler options
typescriptOptions: {
emitDecoratorMetadata: true
},
paths: {
'npm:': 'https://unpkg.com/'
},
//map tells the System loader where to look for thingsd
map: {
'app': './src',
'@angular/core': 'npm:@angular/core' + ngVer + '/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common' + ngVer + '/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler' + ngVer + '/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser' + ngVer + '/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic' + ngVer + '/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http' + ngVer + '/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router' + '@3.3.1' + '/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms' + ngVer + '/bundles/forms.umd.js',
'@angular/core/testing': 'npm:@angular/core' + ngVer + '/bundles/core-testing.umd.js',
'@angular/common/testing': 'npm:@angular/common' + ngVer + '/bundles/common-testing.umd.js',
'@angular/compiler/testing': 'npm:@angular/compiler' + ngVer + '/bundles/compiler-testing.umd.js',
'@angular/platform-browser/testing': 'npm:@angular/platform-browser' + ngVer + '/bundles/platform-browser-testing.umd.js',
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic' + ngVer + '/bundles/platform-browser-dynamic-testing.umd.js',
'@angular/http/testing': 'npm:@angular/http' + ngVer + '/bundles/http-testing.umd.js',
'@angular/router/testing': 'npm:@angular/router' + ngVer + '/bundles/router-testing.umd.js',
'@angular/flex-layout': 'npm:@angular/flex-layout@2.0.0-beta.4/bundles/flex-layout.umd.js',
'rxjs': 'npm:rxjs',
'typescript': 'npm:typescript@2.0.2/lib/typescript.js',
'lodash': 'npm:lodash@4.17.4/lodash.min.js'
},
//packages defines our app package
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
rxjs: {
defaultExtension: 'js'
}
}
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app';
platformBrowserDynamic().bootstrapModule(AppModule);
//our root app component
import {HostBinding, AfterViewInit, CanDeactivate, Injectable, Component, ChangeDetectorRef, NgModule, animate, style, state, transition, trigger} from '@angular/core'
import {RoutesRecognized, RouterModule, ActivatedRoute, Routes, Router} from '@angular/router';
import {BrowserModule} from '@angular/platform-browser'
import { FlexLayoutModule } from "@angular/flex-layout";
import * as _ from 'lodash';
import {Subject} from 'rxjs/Subject';
// horizontal slide animation
const statesSlidedIn = [
state('fromLeft' , style({})),
state('fromRight' , style({}))
];
const styleSlidedLeft = style({transform: 'translateX(-100%)', display: 'none'});
const styleSlidedRight = style({transform: 'translateX(100%)', display: 'none'});
const stateSlidedLeft = state('left', styleSlidedLeft);
const stateSlidedRight = state('right', styleSlidedRight);
const transitionsSlideLeft = [
transition('fromLeft => void', animate('.3s ease-out', styleSlidedRight)),
transition('void => fromLeft', [styleSlidedLeft, animate('.3s ease-out')])
];
const transitionsSlideRight = [
transition('fromRight => void', animate('.3s ease-out', styleSlidedLeft)),
transition('void => fromRight', [styleSlidedRight, animate('.3s ease-out')])
];
const slideHorizontal = trigger('slideHorizontal', [
...statesSlidedIn,
stateSlidedLeft,
stateSlidedRight,
...transitionsSlideLeft,
...transitionsSlideRight
]);
// a shared service providing the current slide direction
@Injectable()
class RouteSlideDirectionService {
direction: string;
setDirection(direction: string) {
this.direction = direction;
}
getDirection(): string {
return this.direction;
}
}
declare abstract class WaitForChangeDetection {
abstract waitForChangeDetection(): Promise<boolean>;
}
@Injectable()
class CanDeactivateAfterChangeDetectionGuard implements CanDeactivate<WaitForChangeDetection> {
canDeactivate(component: WaitForChangeDetection): Promise<boolean> {
return component.waitForChangeDetection();
}
}
@Component({})
class WaitForChangeDetectionImpl implements AfterViewChecked, WaitForChangeDetection {
constructor(protected cdRef: ChangeDetectorRef){
this.viewChecked$ = new Subject<void>();
}
viewChecked$: Subject<void>;
waitForChangeDetection(): Promise<boolean>{
this.cdRef.detectChanges();
return new Promise((resolve) => this.viewChecked$.subscribe(() => resolve(true)));
}
ngAfterViewChecked(){
this.viewChecked$.next();
}
}
@Component({})
class Slidable extends WaitForChangeDetectionImpl {
constructor(protected cdRef: ChangeDetectorRef, private routeSlideDirectionService: RouteSlideDirectionService){
super(cdRef);
}
@HostBinding('@slideHorizontal')
get slideHorizontal(){
let slideDirection = this.routeSlideDirectionService.getDirection();
if(slideDirection){
return slideDirection === 'right' ? 'fromRight' : 'fromLeft';
}
return null;
}
}
// 3 components extending Slidable component
@Component({
template: `
<div style="text-align: center;">A</div>
`,
animations: [slideHorizontal],
styles: [`
:host {
position: absolute;
display: block;
top: 0; left: 0; right: 0;
min-height: 100%;
background: rgba(0,255,255,.2);
}
`]
})
class ComponentA extends Slidable {}
@Component({
template: `
<div style="text-align: center;">B</div>
`,
animations: [slideHorizontal],
styles: [`
:host {
position: absolute;
display: block;
top: 0; left: 0; right: 0;
min-height: 100%;
background: rgba(255,0,255,.2);
}
`]
})
class ComponentB extends Slidable {}
@Component({
template: `
<div style="text-align: center;">C</div>
`,
animations: [slideHorizontal],
styles: [`
:host {
position: absolute;
display: block;
top: 0; left: 0; right: 0;
min-height: 100%;
background: rgba(255,255,0,.2);
}
`]
})
class ComponentC extends Slidable {}
const AppRoutes: Routes = [{
path: 'a',
component: ComponentA,
data: {
slideIndex: 1
},
canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
}, {
path: 'b',
component: ComponentB,
data: {
slideIndex: 2
},
canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
}, {
path: 'c',
component: ComponentC,
data: {
slideIndex: 3
},
canDeactivate: [CanDeactivateAfterChangeDetectionGuard]
}, {
path: '**',
redirectTo: 'a'
}];
@Component({
selector: 'menu-bar',
template: `
<div fxLayout="row" style="position: relative; height: 60px; background: #5e35b1; color: #fff;">
<div fxFlex fxLayout="column" fxLayoutAlign="center center" routerLink="/a" [routerLinkActive]="['is-active']" class="link">
<div>A</div>
</div>
<div fxFlex fxLayout="column" fxLayoutAlign="center center" routerLink="/b" [routerLinkActive]="['is-active']" class="link">
<div>B</div>
</div>
<div fxFlex fxLayout="column" fxLayoutAlign="center center" routerLink="/c" [routerLinkActive]="['is-active']" class="link">
<div>C</div>
</div>
</div>
`,
styles: [`
.link {
cursor: pointer;
border-radius: 4px;
}
.is-active {
background-color: rgba(255,255,255,.1);
font-weight: bold;
}
`]
})
class MenuBarComponent {}
@Component({
selector: 'my-app',
template: `
<menu-bar></menu-bar>
<div style="position: absolute; top: 60px; left: 0; right: 0; bottom: 0;">
<router-outlet></router-outlet>
</div>
`,
styles: [`
:host {
display: block;
position: fixed;
top: 0; bottom: 0; left: 0; right: 0;
}
`]
})
class App {
constructor(private router: Router, private route: ActivatedRoute, private routeSlideDirectionService: RouteSlideDirectionService){
this.router.events.subscribe((event) => {
if (event instanceof RoutesRecognized) {
let leavingSlideIndex = _.get(event, 'state.root.firstChild.data.slideIndex');
let enteringSlideIndex = _.get(this.route, 'snapshot.firstChild.data.slideIndex');
if(leavingSlideIndex && enteringSlideIndex){
this.routeSlideDirectionService.setDirection(leavingSlideIndex > enteringSlideIndex ? 'right' : 'left')
} else {
this.routeSlideDirectionService.setDirection(null);
}
}
});
}
}
@NgModule({
imports: [ BrowserModule, FlexLayoutModule.forRoot(), RouterModule.forRoot(AppRoutes) ],
declarations: [ App, ComponentA, ComponentB, ComponentC, MenuBarComponent],
bootstrap: [ App ],
providers: [RouteSlideDirectionService, CanDeactivateAfterChangeDetectionGuard]
})
export class AppModule {}