acq-ng-metadata
Version:
Angular 2 decorators and utils for Angular 1.x
316 lines (245 loc) • 10 kB
Markdown
# Bootstrapping ( General overview how to do things )
ng-metadata is indeed very flexible, so it allows you to do things not just in one particular way!
This document is a broad overview how to do things within your app.
You have 2 options how to bootstrap:
- using ng-metadata/platform-browser-dynamic `bootstrap` function
- using traditional `angular.bootstrap`
You have 2 options how to register your components/directives/pipes/services:
- using `/` decorators metadata ( Angular 2 way )
- using `provide` function within `ngModule.directive`,`ngModule.service` etc
You have 3 options how to define type of component/directive bindings
- by template ( angular 2 syntax )
- by Angular 1 special symbol within `Input` decorator
- by combining previous 2 types
## Bootstrap Options
### ng-metadata bootstrap
This is preferred way how to bootstrap your app, because it gives you ability to register other providers etc in Angular 2 way.
It allows you to `enableProdMode()` by very convenient way without touching the `ngModule.config` and configuring $compile and $http providers.
Also by default the app is bootstrap with `strictDi:true`, which you should be doing anyway.
Refactoring to this bootstrap is really easy, just create root app component and register all legacy angular 1 modules from your abb via bootstrap function
```typescript
import { bootstrap } from 'ng-metadata/platform-browser-dynamic';
import { enableProdMode } from 'ng-metadata/core';
// some 3rd party
import * as ngSanitize from 'angular-sanitize';
import * as uiRouter from 'angular-ui-router';
// configuration function for `ngModule.config()`
import { configProviders } from './config'
// root app component
import { AppComponent } from './app.component';
// old angular.module modules
import { UserModule, AmdminModule } from './modules';
// node env variable (available with Webpack)
if(env === 'production'){
enableProdMode();
}
bootstrap( AppComponent, [ngSanitize, uiRouter, configProviders, UserModule, AmdminModule] );
```
This is initial state, it is advised to refactor those old Angular modules to be a root component of that module and registered
via `directives` metadata on AppComponent:
```typescript
import { Component } from 'ng-metadata/core';
import {AdminComponent} from './modules/admin'
import {UserComponent} from './modules/user'
export class AppComponent{}
```
### angular.bootstrap
```typescript
import * as angular from 'angular';
// some 3rd party
import * as ngSanitize from 'angular-sanitize';
import * as uiRouter from 'angular-ui-router';
// root AppModule is name of angular.module, a string
import { AppModule } from './index';
angular.bootstrap( document, [AppModule, ngSanitize, uiRouter], {strictDi: true} )
```
You can still leverage ng2 way of components registration without ng-metadata bootstrap,
but you have to manually create your module bundle with `bundle` function:
```typescript
// index.ts - this is the index file used in previous boostrap example
import { bundle, Component } from 'ng-metadata/core';
import { UserModule } from './modules/user';
import { AdminDirectives, AdminProviders, AdminPipes, adminConfig } from './modules/admin'
export class AdminComponent{}
const AdminModule = bundle(AdminComponent,[adminConfig]).name;
export const AppModule = angular.module('myApp',[UserModule, AdminModule]);
```
## Registering parts of your app
### `/` decorators metadata ( Angular 2 way )
**Note:** Always remember that Angular 1 does not have Hierarchical Injector, so every service, directive, pipe you register, will be registered
to global Angular namespace
It highly advised to build you app as a component oriented tree and not register providers(services) within multiple nested components.
For registering services/factories/values within `provider` Component metadata property,
only [provider map literal](http://blog.thoughtram.io/angular/2016/05/13/angular-2-providers-using-map-literals.html) is allowed, `provide` function is deprecated.
```typescript
// app.component.ts
import { Component, OpaqueToken } from 'ng-metadata/core';
import { AdminComponent } from './modules/admin';
import { UserComponent } from './modules/user';
// all of these are nested array which have particular providers, ngMetadata will flatten these arrays like Angular 2
import { SharedProviders, SharedDirectives, SharedPipes } from './shared'
const MyFooToken = new OpaqueToken('myFooValue')
const MyFactoryToken = new OpaqueToken('myFooFactory');
export class AppComponent{}
```
### `provide` function within `ngModule.directive`,`ngModule.service`
- using `provide` is deprecated, although it was the only registration method in previous version (ng-metadata 1.x), and you can still use it if you want
**NOTE** with provide there was no support for `factories`, so if you needed them you have to register them via old school `ngModule.factory()`
```typescript
// index.ts
import { provide, Component, OpaqueToken } from 'ng-metadata/core';
import { AdminModule } from './modules/admin';
import { UserModule } from './modules/user';
import { FooPipe, FooDirective, FooService } from './shared'
const MyFooToken = new OpaqueToken('myFooValue')
const MyFactoryToken = new OpaqueToken('myFooFactory');
export class AppComponent{}
fooFactory.$inject = ['$log'];
function fooFactory($log){ $log.log('a girl has no name') }
export const AppModule = angular.module('myApp',[])
.directive( ...provide( AppComponent ))
.directive( ...provide( FooDirective ))
.filter( ...provide( FooPipe ))
.service( ...provide( FooService ))
.value( ...provide( MyFooToken, {useValue:'hello'} ) )
.factory( MyFactoryToken, fooFactory)
.name
```
## Binding Options
### by template ( angular 2 syntax )
This is the preferred way of defining bindings and you can easily migrate to it if you are coming from ng-metadata 1.x
```typescript
import { Component, Input, Output, EventEmitter } from 'ng-metadata/core';
export class GreeterComponent {
mutationMadness: {};
user: {name:string};
nickName: string;
greet = new EventEmitter<string>();
}
export class AppComponent{
user = {name:'Martin'};
nick = 'Hotell';
twoWayBoomerang = 'O oh, two way data binding, Im out of here!!!! Run for your life! :D';
onGreet(name:string){
console.log(`${name} says hello!`);
}
}
```
### by Angular 1 special symbol within `Input` decorator
```typescript
import { Component, Input, Output, EventEmitter } from 'ng-metadata/core';
export class GreeterComponent {
mutationMadness: {};
user: {name:string};
nickName: string;
greet = new EventEmitter<string>();
}
export class AppComponent{
user = {name:'Martin'};
nick = 'Hotell';
twoWayBoomerang = 'O oh, two way data binding, Im out of here!!!! Run for your life! :D';
onGreet(name:string){
console.log(`${name} says hello!`);
}
}
```
### combined by template + by declaration
You can also combine both type of bindings ( you may need this for directives for example ).
> we cannot bind one way to directive particular name, because this is how angular 1 compilator works.
It is although not recommended to directly bind to directive, rather create additional properties to make things clear
```typescript
import { Component, Directive, Input, Output, EventEmitter, HostListener } from 'ng-metadata/core';
export class GreeterDirective {
// here we combine both types of binding
name: string;
defaultName: string;
greet = new EventEmitter<string>();
onClick(){
this.greet.emit(this.name || this.defaultName);
}
}
export class AppComponent{
name ='Martin';
onGreet(name:string){
console.log(`${name} says hello!`);
}
}
```