Don’t use window
directly.
Angular 2 has an ability to develop an application in cross-platform because it doesn’t depend on DOM.
But it’s breakable easily. If you use window
,document
or anything browser-specific, then of course your app will lose the ability.
We often use window
instance to get and set global variables. In browser platform, window
is a single global context object.
In the other side, Node.js environment provides a global context as global
.
To make our app platform-agnostic, we must absorb the difference.
Don’t worry. Already we have a powerful stuff for that.
Dependency injection.
Define a global variable
Prepare a global variable foo
as example.
<body>
<my-app>
loading...
</my-app>
<script>
window.DATA = {
foo: "bar"
};
</script>
</body>
Declare global type
First, we should declare our global type as an interface. In this case, global type has only foo
string field.
export interface MyGlobal {
foo: string;
}
Create GlobalRef
class
This is a hero of this story. GlobalRef
is an abstract class to access to the global object. It has only one nativeGlobal
getter. (consistent with ElementRef#nativeElement
.)
export abstract class GlobalRef {
abstract get nativeGlobal(): MyGlobal;
}
Create platform-specific classes
GlobalRef
class is just a placeholder. Let’s make platform-specific classes by extending GlobalRef
.
export class BrowserGlobalRef extends GlobalRef {
get nativeGlobal(): MyGlobal {
return window as MyGlobal;
}
}
export class NodeGlobalRef extends GlobalRef {
get nativeGlobal(): MyGlobal {
return global as MyGlobal;
}
}
Provide GlobalRef
We must provide the classes. Let’ create SharedModule
and define SharedModule.forBrowser()
and SharedModule.forNode()
.
import { NgModule, ModuleWithProviders } from "@angular/core";
import { GlobalRef, BrowserGlobalRef, NodeGlobalRef } from "./global-ref";
@NgModule({})
export class SharedModule {
static forBrowser(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [{ provide: GlobalRef, useClass: BrowserGlobalRef }]
};
}
static forNode(): ModuleWithProviders {
return {
ngModule: SharedModule,
providers: [{ provide: GlobalRef, useClass: NodeGlobalRef }]
};
}
}
And use them in BrowserAppModule
and NodeAppModule
.
@NgModule({
imports: [BrowserModule, SharedModule.forBrowser()],
declarations: [App],
bootstrap: [App]
})
export class BrowserAppModule {}
@NgModule({
imports: [BrowserModule, SharedModule.forNode()],
declarations: [App],
bootstrap: [App]
})
export class NodeAppModule {}
Bootstrap
Bootstrapping must be separated for each platform. Following is for only browser.
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppBrowserModule} from './app';
platformBrowserDynamic().bootstrapModule(AppBrowserModule)
Use `GlobalRef` in components
All done! Let’s use `GlobalRef` and access to global variables.
import {GlobalRef} from './global-ref';
@Component({
selector: 'my-app',
template: `
<div>
<pre>{{ data | json }}</pre>
</div>
`,
})
export class App {
data: any;
constructor(_global: GlobalRef) {
this.data = _global.nativeGlobal.DATA;
}
}
Plunker: http://plnkr.co/edit/ceuEBlVpWhNNYZvMOqbO?p=preview
Conclusion
- Don’t access to
window
as global context - Wrap the context and absorb the difference among platforms
- Use dependency injection and NgModule
Note: I never recommend you to use global variables. This is a small tip to tackle the real world… Good luck!