<!DOCTYPE html>
<html>
<head>
<base href="." />
<title>angular 5 + flex layout</title>
<style>
my-app {
font-size: 1.2em;
}
input[type=checkbox] {
width: 20px;
height: 20px;
cursor: pointer;
}
</style>
<!-- polyfill(s) for older browsers -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.8.16?main=browser"></script>
<!--<script src="https://unpkg.com/reflect-metadata@0.1.10"></script>-->
<script src="https://unpkg.com/systemjs@0.20.18/dist/system.src.js"></script>
<script src="https://unpkg.com/web-animations-js@2.3.1"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function (err) { console.error(err); });
</script>
</head>
<body>
<my-app>
loading...
</my-app>
</body>
</html>
//our root app component
import { Component, NgModule, VERSION} from '@angular/core'
import { BrowserModule} from '@angular/platform-browser'
@Component({
selector: 'my-app',
template: `
<p>Enter readonly mode <input type=checkbox [(ngModel)]="isReadOnly"></p>
<div class="editor" [appDisabled]="isReadOnly">
<div class="toolBar">
<app-button><span style="font-weight: bold;">B</span></app-button>
<app-button><span style="text-style: italic;">I</span></app-button>
<app-button><span style="text-decoration: underline;">U</span></app-button>
<app-button [appDisabled] [disabledStopPropagation]="true">Fullscreen</app-button>
</div>
<app-edit-area></app-edit-area>
</div>
`,
styles: [`
.editor {
width: 300px;
}
app-edit-area {
width: 100%;
height: 200px;
margin-top: 5px;
}
`]
})
export class AppComponent {
public isReadOnly: boolean = false;
}
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
import { ButtonComponent } from './button.component';
import { DisabledDirective } from './disabled.directive';
import { EditAreaComponent } from './editarea.component';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
],
declarations: [
AppComponent,
ButtonComponent,
DisabledDirective,
EditAreaComponent
],
providers: [
],
bootstrap: [AppComponent]
})
export class AppModule { }
import {
Component,
NgModule,
VERSION,
Optional,
ChangeDetectorRef
} from '@angular/core'
import {DisabledDirective, resolve} from './disabled.directive';
@Component({
selector: 'app-button',
template: `
<button [disabled]="disabled"><ng-content></ng-content></button>
`,
styles: [`
button {
min-width: 40px;
height: 40px;
}
`]
})
export class ButtonComponent {
private disabledDirective: DisabledDirective;
constructor(changeDetector: ChangeDetectorRef, @Optional() optDisabled: DisabledDirective) {
this.disabledDirective = resolve(optDisabled);
this.disabledDirective.onChange(this, (newValue) => {
changeDetector.markForCheck();
});
}
get disabled(): boolean {
return this.disabledDirective.disabled;
}
}
import {
ChangeDetectorRef,
Directive,
EventEmitter,
ElementRef,
Input,
NgZone,
OnChanges,
OnDestroy,
Optional,
Output,
SkipSelf
} from '@angular/core';
@Directive({
selector: '[appDisabled]',
})
export class DisabledDirective implements OnDestroy, OnChanges {
@Output() disabledChange: EventEmitter<boolean> = new EventEmitter<boolean>(false);
@Input() appDisabled: boolean = false;
@Input() disabledStopPropagation: boolean = false;
private disabled: boolean = false;
private element: HTMLElement;
constructor(
private elementRef: ElementRef,
@SkipSelf() @Optional() private optParent: DisabledDirective,
@Optional() private changeDetector: ChangeDetectorRef,
@Optional() private zone: NgZone,
) {
this.disabledChange = new EventEmitter<boolean>(false);
if (optParent) {
optParent.onChange(this, () => this.checkForChanges());
}
}
ngOnInit() {
this.element = this.elementRef.nativeElement;
}
private checkForChanges() {
setTimeout(() => {
let newValue = false;
if (this.disabledStopPropagation || !this.optParent) {
newValue = !!this.appDisabled;
} else {
newValue = !!this.appDisabled || this.optParent.disabled;
}
if (this.zone && newValue != this.disabled) {
this.zone.run(() => {
if (this.changeDetector) {
this.changeDetector.markForCheck();
}
this.disabled = newValue;
this.disabledChange.emit(newValue);
});
}
}, 0);
}
ngOnChanges() {
this.checkForChanges();
}
/**
* Alerts the callback when the disabled state changes
*/
onChange(directive: DisabledDirective, callback: (opt?: boolean) => void) {
const result = this.disabledChange.subscribe(callback);
}
}
const defaultDisabled = new DisabledDirective(null, null, null);
export function resolve(optDisabled?: DisabledDirective): DisabledDirective {
return optDisabled || defaultDisabled;
};
const DISABLED_OPACITY = 0.3;
@Directive({
selector: '[appDefaultDisabled]',
host: {
'[style.opacity]': 'disabled ? ' + DISABLED_OPACITY + ' : undefined',
'[style.pointerEvents]': 'disabled ? "none" : undefined',
},
})
export class DefaultDisabledStateDirective {
disabledDirective: DisabledDirective;
get disabled(): boolean {
return this.disabledDirective.disabled;
}
constructor(@Optional() optDisabled: DisabledDirective, changeDetector: ChangeDetectorRef) {
this.disabledDirective = resolve(optDisabled);
}
}
import {
Component,
NgModule,
VERSION,
Optional,
ChangeDetectorRef
} from '@angular/core'
import {DisabledDirective, resolve} from './disabled.directive';
@Component({
selector: 'app-edit-area',
template: `
<textarea class="editArea" [disabled]="disabled">
This is a prototype of a simple editor.
Here's some content.
</textarea>
`,
styles: [`
:host {display: block;}
.editArea {
width: 100%;
height: 100%;
padding: 10px;
}
`]
})
export class EditAreaComponent {
private disabledDirective: DisabledDirective;
constructor(changeDetector: ChangeDetectorRef, @Optional() optDisabled: DisabledDirective) {
this.disabledDirective = resolve(optDisabled);
this.disabledDirective.onChange(this, (newValue) => {
// perform additional actions on this when the disabled status (coming from a directive) changes
changeDetector.markForCheck();
});
}
get disabled(): boolean {
return this.disabledDirective.disabled;
}
}
(function (global) {
var angVer = '5.0.0-beta.6';
System.config({
transpiler: 'ts',
typescriptOptions: {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"sourceMap": true,
"suppressImplicitAnyIndexErrors": true,
"target": "es5"
},
meta: {
'typescript': {
"exports": "ts"
}
},
paths: {
'npm:': 'https://unpkg.com/',
'gh:': 'https://raw.githubusercontent.com/'
},
map: {
"app": "app",
"rxjs": "npm:rxjs@5.4.3",
"ts": "npm:plugin-typescript@7.1.0/lib/plugin.js",
"typescript": "npm:typescript@2.4.2/lib/typescript.js",
"@angular/core": "npm:@angular/core@" + angVer + "/bundles/core.umd.js",
"@angular/common": "npm:@angular/common@" + angVer + "/bundles/common.umd.js",
"@angular/compiler": "npm:@angular/compiler@" + angVer + "/bundles/compiler.umd.js",
"@angular/platform-browser": "npm:@angular/platform-browser@" + angVer + "/bundles/platform-browser.umd.js",
"@angular/platform-browser-dynamic": "npm:@angular/platform-browser-dynamic@" + angVer + "/bundles/platform-browser-dynamic.umd.js",
"@angular/http": "npm:@angular/http@" + angVer + "/bundles/http.umd.js",
"@angular/router": "npm:@angular/router@" + angVer + "/bundles/router.umd.js",
"@angular/forms": "npm:@angular/forms@" + angVer + "/bundles/forms.umd.js",
"@angular/animations": "npm:@angular/animations@" + angVer + "/bundles/animations.umd.js",
"@angular/animations/browser": "npm:@angular/animations@" + angVer + "/bundles/animations-browser.umd.js",
"@angular/platform-browser/animations": "npm:@angular/platform-browser@" + angVer + "/bundles/platform-browser-animations.umd.js",
"@angular/flex-layout": "npm:@angular/flex-layout@2.0.0-beta.9"
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.ts',
defaultExtension: 'ts'
},
rxjs: {
defaultExtension: 'js'
}
}
});
if (!global.noBootstrap) { bootstrap(); }
// Bootstrap the `AppModule`(skip the `app/main.ts` that normally does this)
function bootstrap() {
// Stub out `app/main.ts` so System.import('app') doesn't fail if called in the index.html
System.set(System.normalizeSync('app/main.ts'), System.newModule({ }));
// bootstrap and launch the app (equivalent to standard main.ts)
Promise.all([
System.import('@angular/platform-browser-dynamic'),
System.import('app/app.module')
])
.then(function (imports) {
var platform = imports[0];
var app = imports[1];
platform.platformBrowserDynamic().bootstrapModule(app.AppModule);
})
.catch(function(err){ console.error(err); });
}
})(this);