ngParty / ng-metadata

Angular 2 decorators and utils for Angular 1.x
https://hotell.gitbooks.io/ng-metadata/content/
MIT License
355 stars 46 forks source link

add new upgrade/static api for downgrading within ng-metadata NgModule #178

Closed Hotell closed 7 years ago

Hotell commented 8 years ago

since angular 2 version 2.2.0 , there is new Api for upgrading/downgrading entities.

Implement parts of upgrade/static namespace to provide nice api for downgrading components/services within ng-metadata NgModule and also upgradeAdapter is not needed anymore.

function to implement(backport):

I would love to see something like this:

angular 2 NgModule

// /app/app.module.ng2.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static/';

import { Logger } from './logger.service.ng2';
import { LoginService } from './login.service.ng2';
import { LoginComponent } from './login.component.ng2';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ],
  declarations: [
    LoginComponent
  ],
  providers: [
    LoginService,
    Logger
  ],
  entryComponents: [
    LoginComponent
  ]
})
export class AppModule {
  ngDoBootstrap() {}
}

ng-metadata angular 1 NgModule

// /app/app.module
import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static/';
import {provideNg2Component, provideNg2Injectable} from 'ng-metadata/upgrade';
import { NgModule, bundle } from 'ng-metadata/core';

import { AppComponent } from './app.component';
import { Logger, LoggerToken } from './logger.service.ng2';
import { LoginComponent } from './login.component.ng2';

@NgModule( {
  declarations: [
    // angular 1 ngMetadata component
    AppComponent,
   // angular 2 component 
    provideNg2Component({
      component:LoginComponent,
      downgradeFn: downgradeComponent
    })
  ],
  providers: [
      provideNg2Injectable({
        token: LoggerToken, 
        injectable: Logger, 
        downgradeFn: downgradeInjectable
     } )
  ]
} )
class AppModule {}

export const Ng1AppModule = bundle(AppModule);
// /main.ts
import { UpgradeModule } from '@angular/upgrade/static/';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from 'ng-metadata/core';

import { Ng1AppModule } from './app/app.module';
// now angular 2 module is the boss
import { AppModule } from './app/app.module.ng2';

if ( ENV === 'production' ) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .then(platformRef => {
    const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
    upgrade.bootstrap(document.body, [Ng1AppModule.name], {strictDi: true});
});

We should also support old way of registering if user doesn't uses ng-metadata NgModule

import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static/';
import {downgradeNg2Component, downgradeNg2Injectable} from 'ng-metadata/upgrade';
import {provide} from 'ng-metadata/core';

import { AppComponent } from './app.component';
import { Logger, LoggerToken } from './logger.service.ng2';
import { LoginComponent } from './login.component.ng2';

export const Ng1AppModule = angular.module('myApp',[])
 .directive(...provide(AppComponent))
 .directive(...provide(downgradeNg2Component({
   component:LoginComponent,
   downgradeFn: downgradeComponent
  })))
.factory(...provide(downgradeNg2Injectable({
    token: LoggerToken, 
    injectable: Logger, 
    downgradeFn: downgradeInjectable
  })))

Upgrading services

let's say we have ng-metadata service:

// heroes.service.ts
import {Injectable} from 'ng-metadata/core'
import { Hero } from './hero';

@Injectable()
export class HeroesService {
  get() {
    return [
      new Hero(1, 'Windstorm'),
      new Hero(2, 'Spiderman'),
    ];
  }
}

registered within ng-metadata NgModule

// app.module.ts
import { NgModule, getInjectableName } from 'ng-metadata/core';
import { HeroesService } from './heroes/heroes.service';

@NgModule( {
  providers: [
    HeroesService,
  ]
} )
class AppModule {}

this is how we can upgrade it via new angular 2 API (getInjectableName needed) :

// app.module.ng2.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static/';
import { getInjectableName } from 'ng-metadata/core';

import { LoginComponent } from './login.component.ng2';
import { HeroesService } from './heroes/heroes.service';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ],
  declarations: [
    LoginComponent
  ],
  providers: [
    {
     // this can be any token, but because we are using ng-metadata we are already using TS DI via type
      provide: HeroesService,
      useFactory: ($injector: ng.auto.IInjectorService) => $injector.get<HeroesService>(getInjectableName(HeroesService)),
      deps: ['$injector']
    }
  ],
  entryComponents: [
    LoginComponent
  ]
})
export class AppModule {
  ngDoBootstrap() {}
}

registering our ng-metadata injectable to ng2 is too much typing, what I would like to see is something like this:

// app.module.ng2.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static/';
import { provideNg1Injectable } from 'ng-metadata/upgrade';

import { LoginComponent } from './login.component.ng2';
import { HeroesService } from './heroes/heroes.service';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ],
  declarations: [
    LoginComponent
  ],
  providers: [
    provideNg1Injectable(HeroesService)
  ],
  entryComponents: [
    LoginComponent
  ]
})
export class AppModule {
  ngDoBootstrap() {}
}

provideNg1Injectable should also accept just string(injected via @Inject('myString')) or OpaqueToken (injected via @Inject(myToken))