2016-04-01 9 views
5

Desidero eseguire il bootstrap della mia applicazione con i dati che sto recuperando da un servizio. Sto facendo qualcosa sulla falsariga dibootstrap angular2 con dati da una o più chiamate Ajax

let dependencies = [ 
    //... a load of dependencies 
    MyService 
]; 

let injector = Injector.resolveAndCreate(dependencies); 
let service: MyService = injector.get(MyService); 

service.getData() // returns observable 
    .toPromise() 
    .then((d) => { 
     // use data to append to dependencies 

     bootstrap(App, dependencies) 
    }); 

Questo funziona bene, ma non mi piace usare la matrice delle dipendenze due volte, c'è un modo più pulito di fare questo? Posso aggiungere cose all'iniettore dell'applicazione dopo il bootstrap? Inoltre, noto che la funzione bootstrap restituisce una promessa, posso usare questa promessa per impedire il bootstrap dell'applicazione fino a quando non termina la richiesta di ajax?

Ovviamente per il Injector Potrei utilizzare solo le dipendenze richieste da MyService ma questo lo rende molto fragile come potete immaginare.

risposta

5

Il problema qui è che Angular2 non consente l'accesso al riferimento dell'applicazione e al relativo iniettore prima di avviare il componente principale su di esso. Vedere questa riga nel codice sorgente: https://github.com/angular/angular/blob/master/modules/angular2/platform/browser.ts#L110.

Un approccio potrebbe essere quello di implementare un bootstrap personalizzato invece di utilizzare quello predefinito. Qualcosa del genere che divide la creazione dell'applicazione e il booster sul componente dell'applicazione su di esso. In questo modo sarai in grado di caricare qualcosa tra le due attività.

Ecco un esempio di implementazione:

function customBoostrap(appComponentType, customProviders) { 
    reflector.reflectionCapabilities = new ReflectionCapabilities(); 
    let appProviders = 
    isPresent(customProviders) ? [BROWSER_APP_PROVIDERS, customProviders] : BROWSER_APP_PROVIDERS; 
    var app = platform(BROWSER_PROVIDERS).application(appProviders); 

    var service = app.injector.get(CompaniesService); 

    return service.getCompanies().flatMap((companies) => { 
    var companiesProvider = new Provider('companies', { useValue: data }); 
    return app.bootstrap(appComponentType, [ companiesProvider ]); 
    }).toPromise(); 
} 

e usarlo in questo modo:

customBoostrap(AppComponent, [ 
    HTTP_PROVIDERS, 
    CompaniesService 
]); 

aziende saranno automaticamente disponibili per l'iniezione all'interno del componente, ad esempio:

@Component({ 
    (...) 
}) 
export class AppComponent { 
    constructor(@Inject('companies') companies) { 
    console.log(companies); 
    } 
} 

Vedi questo corrispondente plunkr: https://plnkr.co/edit/RbBrQ7KOMoFVNU2ZG5jM?p=preview.

In questo momento, è un po 'hacky ma tale approccio potrebbe essere proposto come una richiesta di funzionalità ...

Modifica

Dopo aver dato un'occhiata al documento per la classe ApplicationRef, ho visto che c'è una soluzione più semplice ;-)

var app = platform(BROWSER_PROVIDERS) 
    .application([BROWSER_APP_PROVIDERS, appProviders]); 

service.getCompanies().flatMap((companies) => { 
    var companiesProvider = new Provider('companies', { useValue: data }); 
    return app.bootstrap(appComponentType, [ companiesProvider ]); 
}).toPromise(); 

Qui è corrispondente plunkr: https://plnkr.co/edit/ooMNzEw2ptWrumwAX5zP?p=preview.

1

@Thierry (come al solito) ha risposto cuore della tua domanda bene, ma penso che questo è degno di nota a parte:

Posso aggiungere cose all'iniettore applicazione dopo bootstrap?

Sì, dichiarandoli in providers o viewProviders sui decoratori dei componenti che li richiedono. es:

//main.ts 
bootstrap(MyComponent) //no dependencies declared 


//my.service.ts 
@Injectable class MyService { public getMessage =() => "foobar" } 


//my.component.ts 
@Component({ 
    selector: 'foo', 
    providers: [MyService] 
    template: `<div>{{mySvc.getMessage()}}</div>` //displays foobar 
}) 
class MyComponent { 
    constructor(private mySvc: MyService){ } 
} 

noti che providers può essere utilizzato su direttive nonché componenti (è un'opzione DirectiveMetadata, da cui ComponentMetadata estende), mentre viewProviders è disponibile solo su componenti per ragioni che sono chiare date the difference between them.

IMHO, è una buona pratica iniettare le dipendenze in questo modo, laddove possibile, invece di farlo bootstrap, poiché consente di limitare l'ambito di disponibilità di una determinata dipendenza alla parte dell'applicazione (ad es. Sottostruttura del componente) dove vuoi che sia disponibile. È anche favorevole al caricamento progressivo ed evita l'odore di SoC di configurare una miriade di iniettabili non correlati in un singolo file di bootstrap.

Problemi correlati