Как передать параметры, отображаемые из бэкэнда в метод начальной загрузки angular2


есть ли способ передать аргументы, отображаемые на бэкэнде, в метод начальной загрузки angular2? Я хочу установить заголовок http для всех запросов с помощью BaseRequestOptions С значение от backend. Мой main.ts файл выглядит так:

import { bootstrap } from '@angular/platform-browser-dynamic';
import { AppComponent } from "./app.component.ts";

bootstrap(AppComponent);

Я нашел, как передать эти аргументы в корневой компонент (https://stackoverflow.com/a/35553650/3455681), но мне это нужно, когда я стреляю bootstrap метод... Любой идеи?

edit:

webpack.конфиг.содержание программы

module.exports = {
  entry: {
    app: "./Scripts/app/main.ts"
  },

  output: {
    filename: "./Scripts/build/[name].js"
  },

  resolve: {
    extensions: ["", ".ts", ".js"]
  },

  module: {
    loaders: [
      {
        test: /.ts$/,
        loader: 'ts-loader'
      }
    ]
  }
};
4 54

4 ответа:

обновление 2

пример Plunker

обновление АОТ

для работы с AoT закрытие фабрики должно быть перемещено

function loadContext(context: ContextService) {
  return () => context.load();
}

@NgModule({
  ...
  providers: [ ..., ContextService, { provide: APP_INITIALIZER, useFactory: loadContext, deps: [ContextService], multi: true } ],

Смотрите также https://github.com/angular/angular/issues/11262

обновление RC.6 и 2.0.0 последний пример

function configServiceFactory (config: ConfigService) {
  return () => config.load();
}

@NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule,
        routes,
        FormsModule,
        HttpModule],
    providers: [AuthService,
        Title,
        appRoutingProviders,
        ConfigService,
        { provide: APP_INITIALIZER,
          useFactory: configServiceFactory
          deps: [ConfigService], 
          multi: true }
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

если нет необходимости ждать инициализации заполните конструктор класса модуль {} также можно использовать:

class AppModule {
  constructor(/*inject required dependencies */) {...} 
}

намек (циклическая зависимость)

например, введение маршрутизатора может вызвать циклические зависимости. Чтобы обойти, введите Injector и получить зависимость от

this.myDep = injector.get(MyDependency);

вместо введения MyDependency как:

@Injectable()
export class ConfigService {
  private router:Router;
  constructor(/*private router:Router*/ injector:Injector) {
    setTimeout(() => this.router = injector.get(Router));
  }
}

обновление

это должно работать то же самое в RC.5 но вместо того, чтобы добавить поставщика к providers: [...] корневого модуля вместо bootstrap(...)

(не проверял еще).

обновление

интересный подход, чтобы сделать это полностью внутри угловой объясняется здесь https://github.com/angular/angular/issues/9047#issuecomment-224075188

можно использовать APP_INITIALIZER, который будет выполнять функцию, когда приложение инициализируется и задерживает то, что оно предоставляет, если функция возвращает один обещать. Это означает, что приложение можно инициализировать без совсем так большая задержка, и вы также можете использовать существующие службы и рамки особенности.

в качестве примера предположим, что у вас есть мульти-арендованное решение, где информация о сайте зависит от доменного имени, с которого он обслуживается. Это может будь [имя]. letterpress.com или пользовательский домен, который сопоставляется на полное имя. Мы можем скрыть тот факт, что это за обещание используя APP_INITIALIZER.

In бутстрэп:

{provide: APP_INITIALIZER, useFactory: (sites:SitesService) => () => sites.load(), deps:[SitesService, HTTP_PROVIDERS], multi: true}),

сайты.услуга.ТС:

@Injectable()
export class SitesService {
  public current:Site;

  constructor(private http:Http, private config:Config) { }

  load():Promise<Site> {
    var url:string;
    var pos = location.hostname.lastIndexOf(this.config.rootDomain);
    var url = (pos === -1)
      ? this.config.apiEndpoint + '/sites?host=' + location.hostname
      : this.config.apiEndpoint + '/sites/' + location.hostname.substr(0, pos);
    var promise = this.http.get(url).map(res => res.json()).toPromise();
    promise.then(site => this.current = site);
    return promise;
  }

Примечание: config - это просто пользовательский класс конфигурации. rootDomain будет '.letterpress.com' для этого примера и позволит такие вещи, как aptaincodeman.letterpress.com.

компоненты и другие услуги теперь могут иметь Site вводят в их и используют .current свойство, которое будет конкретным заполненный объект без необходимости ждать каких-либо обещаний в приложении.

этот подход казалось, чтобы сократить задержку запуска, которая была в противном случае довольно заметно, если вы ждали большой угловой пакет загрузите, а затем еще один http-запрос до начала начальной загрузки.

оригинал

вы можете передать его с помощью angulars dependency injection:

var headers = ... // get the headers from the server

bootstrap(AppComponent, [{provide: 'headers', useValue: headers})]);
class SomeComponentOrService {
   constructor(@Inject('headers') private headers) {}
}

или предоставить подготовленный BaseRequestOptions как

class MyRequestOptions extends BaseRequestOptions {
  constructor (private headers) {
    super();
  }
} 

var values = ... // get the headers from the server
var headers = new MyRequestOptions(values);

bootstrap(AppComponent, [{provide: BaseRequestOptions, useValue: headers})]);

В финальном релизе Angular2, поставщик APP_INITIALIZER может быть использован для достижения того, что вы хотите.

Я написал Суть с полным примером:https://gist.github.com/fernandohu/122e88c3bcd210bbe41c608c36306db9

пример gist читает из файлов JSON, но может быть легко изменен для чтения из конечной точки REST.

что вам нужно, в основном:

a) настройка APP_INITIALIZER в существующем модуле файл:

import { APP_INITIALIZER } from '@angular/core';
import { BackendRequestClass } from './backend.request';
import { HttpModule } from '@angular/http';

...

@NgModule({
    imports: [
        ...
        HttpModule
    ],
    ...
    providers: [
        ...
        ...
        BackendRequestClass,
        { provide: APP_INITIALIZER, useFactory: (config: BackendRequestClass) => () => config.load(), deps: [BackendRequestClass], multi: true }
    ],
    ...
});

эти строки вызовут метод load () из класса BackendRequestClass перед запуском приложения.

убедитесь, что вы установили "HttpModule" в разделе "импорт", если вы хотите сделать http-вызовы на серверную часть с помощью встроенной библиотеки angular2.

b) создайте класс и назовите файл "backend.запрос.ТС":

import { Inject, Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Rx';

@Injectable()
export class BackendRequestClass {

    private result: Object = null;

    constructor(private http: Http) {

    }

    public getResult() {
        return this.result;
    }

    public load() {
        return new Promise((resolve, reject) => {
            this.http.get('http://address/of/your/backend/endpoint').map( res => res.json() ).catch((error: any):any => {
                reject(false);
                return Observable.throw(error.json().error || 'Server error');
            }).subscribe( (callResult) => {
                this.result = callResult;
                resolve(true);
            });

        });
    }
}

c) чтобы прочитать содержимое вызова backend, вам просто нужно ввести BackendRequestClass в любой класс по вашему выбору и звоните getResult (). Пример:

import { BackendRequestClass } from './backend.request';

export class AnyClass {
    constructor(private backendRequest: BackendRequestClass) {
        // note that BackendRequestClass is injected into a private property of AnyClass
    }

    anyMethod() {
        this.backendRequest.getResult(); // This should return the data you want
    }
}

Дайте мне знать, если это решает вашу проблему.

вместо того, чтобы ваша точка входа вызывала сам bootstrap, вы можете создать и экспортировать функцию, которая выполняет эту работу:

export function doBootstrap(data: any) {
    platformBrowserDynamic([{provide: Params, useValue: new Params(data)}])
        .bootstrapModule(AppModule)
        .catch(err => console.error(err));
}

вы также можете разместить эту функцию на глобальном объекте, в зависимости от вашей установки (webpack/SystemJS). Он также AOT-совместим.

Это имеет дополнительное преимущество для задержки начальной загрузки, когда это имеет смысл. Например, когда вы получаете эти данные пользователя как вызов AJAX после того, как пользователь заполняет форму. Просто вызовите экспортированный функция bootstrap с этими данными.

единственный способ сделать это-предоставить эти значения при определении ваших поставщиков:

bootstrap(AppComponent, [
  provide(RequestOptions, { useFactory: () => {
    return new CustomRequestOptions(/* parameters here */);
  });
]);

затем вы можете использовать эти параметры в свой CustomRequestOptions класс:

export class AppRequestOptions extends BaseRequestOptions {
  constructor(parameters) {
    this.parameters = parameters;
  }
}

если вы получаете эти параметры из запроса AJAX, вам нужно асинхронно загрузиться следующим образом:

var appProviders = [ HTTP_PROVIDERS ]

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

var http = app.injector.get(Http);
http.get('http://.../some path').flatMap((parameters) => {
  return app.bootstrap(appComponentType, [
    provide(RequestOptions, { useFactory: () => {
      return new CustomRequestOptions(/* parameters here */);
    }})
  ]);
}).toPromise();

см. Этот вопрос:

Edit

поскольку у вас есть данные в HTML, вы можете использовать следующее.

вы можете импортировать функцию и вызвать ее с параметрами.

вот пример основного модуля, который загружает ваше приложение:

import {bootstrap} from '...';
import {provide} from '...';
import {AppComponent} from '...';

export function main(params) {
  bootstrap(AppComponent, [
    provide(RequestOptions, { useFactory: () => {
      return new CustomRequestOptions(params);
    });
  ]);
}

затем вы можете импортировать его с главной страницы HTML следующим образом:

<script>
  var params = {"token": "@User.Token", "xxx": "@User.Yyy"};
  System.import('app/main').then((module) => {
    module.main(params);
  });
</script>

см. Этот вопрос: передать постоянные значения в угловой от _макет.cshtml.