web-dev-qa-db-ja.com

Angular 2注入されたサービスは未定義です

サービス(this.loggerService)が挿入されたときにDataHandlerServiceで未定義になるのはなぜですか?依存性注入がこれを処理すると思った。私のloggerServiceは他のサービスで動作します。私がどこに間違っているのかを理解してください。以下の私のDataHandlerServiceコード:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { LoggerService } from './logger.service';

@Injectable()

export class DataHandlerService 
{
constructor(private loggerService: LoggerService)
{

}

extractData(res: Response)
{
    let body = res.json();
    return body || {};
}


handleHttpError(error: any)
{
    let errMsg = (error.message) ? error.message : error.status ? `${error.status} - ${error.statusText}` : 'Server error';

    if (errMsg && this.loggerService)  //Why is this.loggerService always undefined?
    {
        this.loggerService.error(errMsg);
    }

    return Observable.throw(errMsg);
}
}

以下の私のLoggerServiceコード:

import { Injectable } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { ConfigurationService } from './configuration.service';

@Injectable()

export class LoggerService 
{
constructor(private http: Http, private configurationService: ConfigurationService)
{

}

public fatal(msg: string)
{
    if (msg)
    {
        this.log(msg, "Fatal");
    }
}

public debug(msg: string)
{
    if (msg)
    {
        this.log(msg, "Debug");
    }
}

public info(msg: string)
{
    if (msg)
    {
        this.log(msg, "Info");
    }
}

public warn(msg: string)
{
    if (msg)
    {
        this.log(msg, "Warn");
    }
}

public error(msg: string)
{
    if (msg)
    {
        this.log(msg, "Error");
    }
}

private log(msg: string, logLevel: string)
{
    if (msg && logLevel && this.configurationService && this.configurationService.coreSettings)
    {
        let headers = new Headers();
        headers.append('Content-Type', 'application/json');
        headers.append('Accept', 'application/json');

        let loggingInfo: LoggingInfo = { "message": msg, "logLevel": logLevel, "sourceName": "CoreIV", "logDirectory": this.configurationService.coreSettings.logDirectory };

        this.http.post(this.configurationService.coreSettings.logbookUrl + "/rest/LogMessage", { loggingInfo }, { headers: headers })
            .toPromise()
            .then(res => this.extractData(res))
            .catch(err => this.handleHttpError(err));
    }
}

private extractData(res: Response)
{
    let body = res.json();
    return body || {};
}

private handleHttpError(error: any)
{
    let errMsg = (error.message) ? error.message :
        error.status ? `${error.status} - ${error.statusText}` : 'Server error';
    return Observable.throw(errMsg);
}



}

export interface LoggingInfo
{
    message: string,
    logLevel: string,
    sourceName: string,
    logDirectory: string

}

以下の私のApp.Moduleコード:

import { NgModule, APP_INITIALIZER, ErrorHandler } from '@angular/core';
import { LocationStrategy, HashLocationStrategy } from '@angular/common';
import { HttpModule, JsonpModule, Jsonp } from '@angular/http';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, FormGroup, FormControl, ReactiveFormsModule }   from '@angular/forms';
import { routing, appRoutingProviders } from './app.routing';
import { AppConfig } from './app.config';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { AppErrorHandler, LOGGING_ERROR_HANDLER_OPTIONS,     LOGGING_ERROR_HANDLER_PROVIDERS } from './app.error-handler';

import { AppService } from './app.service';
import { ConfigurationService } from './shared/services/configuration.service';
import { DataHandlerService } from './shared/services/data-handler.service';
import { LoggerService } from './shared/services/logger.service';
import { AuthGuard } from './auth-guard.service';

export function init_app(appConfig: AppConfig, configurationService: ConfigurationService, loggerService: LoggerService)
{
// Do initiating of services that are required before app loads
// NOTE: this factory needs to return a function (that then returns a promise)

return appConfig.load()
    .then((res) =>        
    {
        configurationService.coreSettings = appConfig.config;
    })
    .catch((err) =>
    {
        loggerService.error(err);
    });
}

@NgModule({
imports: [
    BrowserModule,
    FormsModule,
    routing,
    HttpModule,
    JsonpModule
],
exports: [

],
declarations: [
    HomeComponent,
    AppComponent
],
providers: [
    HttpModule,
    ConfigurationService,
    LoggerService,
    { provide: LocationStrategy, useClass: HashLocationStrategy },
    LOGGING_ERROR_HANDLER_PROVIDERS,
    {
        provide: LOGGING_ERROR_HANDLER_OPTIONS,
        useValue: {
            rethrowError: false,
            unwrapError: true
        }
    },       
    appRoutingProviders,
    AuthGuard,    
    DataHandlerService,
    AppConfig,     
    {
        provide: APP_INITIALIZER,
        useFactory: init_app,
        deps: [AppConfig, ConfigurationService, LoggerService],
        multi: false
    }
    ],
    bootstrap: [AppComponent, appRoutingProviders]
   })


export class AppModule
{
constructor(private httpModule: HttpModule)
{

}
}
16
LanceM

この問題の解決策を見つけました。投稿の回答 angular 2-HTTPエラーハンドラーに注入されたサービス は、正しい方向を示してくれました。私は次を使用していました:

        .map(this.dataHandler.extractData)
        .catch(this.dataHandler.handleHttpError);

しかし、使用する必要があります:

        .map(res => this.dataHandler.extractData(res))
        .catch(err => this.dataHandler.handleHttpError(err));

何らかの理由でラムダが必要です。

35
LanceM

これは、メインアプリモジュールでサービスをインポートし、プロバイダーセクションにリストできないときに起こります

// in app.module.ts     
import { LoggerService } from './logger.service';
...
@NgModule({
...
  providers: [
    LoggerService,
3
Robert Tamlyn

答えが正解で受け入れられたとしても、これが失敗した理由と別の可能な解決策を書き留めたかった。

書く代わりに

.map(this.dataHandler.extractData)
.catch(this.dataHandler.handleHttpError);

使用できます

.map(this.dataHandler.extractData.bind(this))
.catch(this.dataHandler.handleHttpError.bind(this));

失敗した主な理由は、thisが実行コンテキストに基づいた値を取り、サービスがsetTimeoutまたはsetIntervalで呼び出された場合、私の場合のようにthisundefinedでした(厳密モードのため-そうでない場合はwindowオブジェクトでした)。 .bindを使用すると、this(コンポーネントクラスオブジェクト)の値を明示的に指定できます。

2
DrNio