<!DOCTYPE html>
<html>
<head>
<base href="./" />
<title>Angular - Linking Component State with Observables</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- bootstrap css -->
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://npmcdn.com/codemirror@latest/lib/codemirror.css" />
<!-- 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.6.23?main=browser"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3"></script>
<script src="https://unpkg.com/systemjs@0.19.27/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function (err) { console.error(err); });
</script>
</head>
<body>
<app>Loading...</app>
</body>
</html>
/**
* WEB ANGULAR VERSION
* (based on systemjs.config.js in angular.io)
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
// DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
transpiler: 'ts',
typescriptOptions: {
// Complete copy of compiler options in standard tsconfig.json
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"../../node_modules/@types/"
]
},
meta: {
'typescript': {
"exports": "ts"
}
},
paths: {
// paths serve as alias
'npm:': 'https://unpkg.com/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/common': 'npm:@angular/common@4.1.1/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler@4.1.1/bundles/compiler.umd.js',
'@angular/core': 'npm:@angular/core@4.1.1/bundles/core.umd.js',
'@angular/forms': 'npm:@angular/forms@4.1.1/bundles/forms.umd.js',
'@angular/http': 'npm:@angular/http@4.1.1/bundles/http.umd.js',
'@angular/http/testing': 'npm:@angular/http@4.1.1/bundles/http-testing.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser@4.1.1/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic@4.1.1/bundles/platform-browser-dynamic.umd.js',
'@angular/router': 'npm:@angular/router@4.1.1/bundles/router.umd.js',
// other libraries
'rxjs': 'npm:rxjs@5.3.1',
'ts': 'npm:plugin-typescript@7.0.6/lib/plugin.js',
'typescript': 'npm:typescript@2.3.2/lib/typescript.js',
'codemirror': 'npm:codemirror@latest/lib/codemirror.js',
'codemirror-stylus': 'npm:codemirror@latest/mode/stylus/stylus.js',
'codemirror-css': 'npm:codemirror@latest/mode/css/css.js',
'ng2-codemirror': 'npm:ng2-codemirror@next/lib/index.js'
},
// 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);
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { AppModule } from './app.module'
platformBrowserDynamic().bootstrapModule(AppModule)
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/index';
const appRoutes: Routes = [
{ path: '', component: HomeComponent },
// otherwise redirect to home
{ path: '**', redirectTo: '' }
];
export const routing = RouterModule.forRoot(appRoutes);
import { Component, OnDestroy } from '@angular/core'
import { Subscription } from 'rxjs/Subscription'
import { MessageService } from './_services/index'
// ------------------------------------------------------------
@Component({
moduleId: module.id,
selector: 'app',
templateUrl: 'app.component.html'
})
export class AppComponent implements OnDestroy {
message: any
subscription: Subscription
constructor(private messageService: MessageService) {
// subscribe to home component messages
this.subscription = this.messageService.getMessage()
.subscribe(message => { this.message = message })
}
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe()
}
}
<!-- main app container -->
<div class="container">
<div *ngIf="message" class="alert alert-success">{{message.text}}</div>
<div class="row">
<router-outlet></router-outlet>
</div>
</div>
export * from './message.service'
import { Component } from '@angular/core'
import { MessageService } from '../_services/index'
// ------------------------------------------------------------
@Component({
moduleId: module.id,
templateUrl: 'home.component.html'
})
export class HomeComponent {
constructor(private messageService: MessageService) {}
sendMessage(): void {
// send message to subscribers via observable subject
this.messageService.sendMessage('Message from Home Component to App Component!')
}
clearMessage(): void {
// clear message
this.messageService.clearMessage()
}
}
<div class="col-md-6 col-md-offset-3">
<h1>Home</h1>
<button (click)="sendMessage()">Send Message</button>
<button (click)="clearMessage()">Clear Message</button>
</div>
<div class="row">
<div class="col-md-6">
<editor [mode]="stylus"></editor>
</div>
<div class="col-md-6">
<editor [mode]="css"></editor>
</div>
</div>
export * from './home.component'
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { FormsModule } from '@angular/forms'
import { CodemirrorModule } from 'ng2-codemirror'
// ------------------------------------------------------------
import { routing } from './app.routing'
import { MessageService } from './_services/index'
import { AppComponent } from './app.component'
import { HomeComponent } from './home/index'
import { EditorComponent } from './editor/index'
// ------------------------------------------------------------
@NgModule({
imports: [
BrowserModule,
FormsModule,
CodemirrorModule,
routing
],
declarations: [
AppComponent,
HomeComponent,
EditorComponent
],
providers: [
MessageService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'
import { Subject } from 'rxjs/Subject'
// ------------------------------------------------------------
@Injectable()
export class MessageService {
private subject = new Subject<any>()
sendMessage(text: string) {
this.subject.next({ text })
}
clearMessage() {
this.subject.next()
}
getMessage(): Observable<any> {
return this.subject.asObservable()
}
}
import { Component, OnInit, Input } from '@angular/core'
import { MessageService } from '../_services/index'
// ------------------------------------------------------------
@Component({
moduleId: module.id,
selector: 'editor',
templateUrl: 'editor.component.html'
})
export class EditorComponent implements OnInit {
@Input() mode: string
config: any = {
lineNumbers: true,
mode: 'stylus'
}
content: string = ''
constructor(private messageService: MessageService) {}
ngOnInit() {
this.config.mode = this.mode
}
onBlur(): void {
// send message to subscribers via observable subject
this.messageService.sendMessage(`Content: ${this.content}`)
}
}
<div class="panel panel-default">
<div class="panel-body">
<codemirror
[(ngModel)]="content"
[config]="config"
(blur)="onBlur()"
></codemirror>
</div>
<div class="panel-footer">
{{content.length}} chars
</div>
</div>
export * from './editor.component'