Angular 2注入的服务未定义。

18

为什么我的DataHandlerService中注入的 this.loggerService 是 undefined 呢?我以为依赖注入会处理好这个问题。我的 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)
{

}
}

你要在哪里以及如何提供 DataHandlerServiceLoggerService - Günter Zöchbauer
嗨,Günter,我已经编辑了帖子,将我的app.module.ts包含在其中,这是我提供这些服务的地方。 - LanceM
找不到任何可能出错的地方 :-/ - Günter Zöchbauer
3个回答

43

我找到了解决这个问题的方法。 在帖子angular 2 - Injected service in http error handler中的答案指引了我正确的方向。 我之前使用的是:

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

但应该使用:

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

由于某种原因需要使用lambda函数。


8
省了我很多麻烦! - Bill
2
如果您不使用lambda表达式,在´extractData´和´handleHttpError´中,´this´上下文将指向其他位置。为了使其正常工作,您可以绑定当前上下文:.map(this.dataHandler.extractData.bind(this).catch(this.dataHandler.handleHttpError.bind(this))。 - Alejandro Silva
仍然有一个在Angular 5中的解决方案。 - Salil Junior
第一次调用忽略了类实例,而第二次没有。这样做的方式是有道理的。 - Tony U.

5
尽管答案是正确的并被接受,但我想写下这个失败的原因和另一个可能的解决方案。
不要写成:

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

您可以使用

标签


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

这个失败的主要原因是由于this的取值基于执行上下文,如果在像我这样的情况下,在setTimeoutsetInterval中调用服务时,this的值将为undefined(由于严格模式 - 否则它将是window对象)。通过使用.bind,您可以明确提供this的值(组件类对象)。

1
这个解释确实缺失了。我建议编辑被接受的答案,使其对其他用户更易理解。 - Shachar Har-Shuv

3
当我未能在主应用程序模块中导入服务并将其列在提供者部分时,就会发生这种情况。
// in app.module.ts     
import { LoggerService } from './logger.service';
...
@NgModule({
...
  providers: [
    LoggerService,

嗨,罗伯特,我已经编辑了帖子,包括我的app.module.ts。 - LanceM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接