Access to global variables in Angular 2
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!