如何在Angular 2中创建单件服务?

Jas*_*aat 131 typescript angular2-routing angular2-services angular

我已经读过,当引导应该让所有孩子共享相同的实例时注入,但我的主要和头部组件(主应用程序包括头部件和路由器出口)都获得了我的服务的单独实例.

我有一个FacebookService,用于调用facebook javascript api和一个使用FacebookService的UserService.这是我的引导程序:

bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);
Run Code Online (Sandbox Code Playgroud)

从我的日志记录看起来,引导调用结束,然后我看到FacebookService然后在每个构造函数运行的代码,MainAppComponent,HeaderComponent和DefaultComponent之前创建UserService:

在此输入图像描述

Jas*_*aat 132

更新(Angular 6)

创建单例服务的推荐方法已更改.现在建议@Injectable在服务的装饰器中指定它应该在'root'中提供.这对我来说很有意义,并且不再需要在模块中列出所有提供的服务.您只需在需要时导入服务,并在适当的位置注册.您还可以指定模块,以便仅在导入模块时提供该模块.

@Injectable({
  providedIn: 'root',
})
export class ApiService {
}
Run Code Online (Sandbox Code Playgroud)

更新(Angular 2)

使用NgModule,我现在的方法是创建一个包含服务类的"CoreModule",并在模块的提供者中列出服务.然后在主应用程序模块中导入核心模块,该模块将为在其构造函数中请求该类的任何子项提供一个实例:

CoreModule.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ApiService } from './api.service';

@NgModule({
    imports: [
        CommonModule
    ],
    exports: [ // components that we want to make available
    ],
    declarations: [ // components for use in THIS module
    ],
    providers: [ // singleton services
        ApiService,
    ]
})
export class CoreModule { }
Run Code Online (Sandbox Code Playgroud)

AppModule.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';

@NgModule({
    declarations: [ AppComponent ],
    imports: [
        CommonModule,
        CoreModule // will provide ApiService
    ],
    providers: [],
    bootstrap: [ AppComponent ]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)

原始答案

如果列出了提供程序bootstrap(),则无需在组件装饰器中列出它们:

import { ApiService } from '../core/api-service';

@Component({
    selector: 'main-app',
    templateUrl: '/views/main-app.html',
    // DO NOT LIST PROVIDERS HERE IF THEY ARE IN bootstrap()!
    // (unless you want a new instance)
    //providers: [ApiService]
})
export class MainAppComponent {
    constructor(private api: ApiService) {}
}
Run Code Online (Sandbox Code Playgroud)

事实上,在'providers'中列出你的类会创建一个新的实例,如果任何父组件已经列出了它,那么孩子们不需要,如果他们这样做,他们将获得一个新的实例.

  • 为什么不把'ApiService`放在`AppModule`的`providers`中,从而避免使用`CoreModule`?(不确定这是@JasonSwett的建议.) (5认同)

Thi*_*ier 125

杰森是完全正确的!它是由依赖注入的工作方式引起的.它基于分层注入器.

Angular2应用程序中有几个注入器:

  • 引导应用程序时配置的根目录
  • 每个组件的注射器.如果您在另一个组件内使用组件.组件注入器是父组件1的子组件.应用程序组件(在您对应用程序进行提升时指定的组件)将根注入器作为父注入器).

当Angular2尝试在组件构造函数中注入某些内容时:

  • 它会查看与组件关联的进样器.如果有匹配的,它将使用它来获取相应的实例.这个实例是懒惰创建的,是这个注入器的单例.
  • 如果此级别没有提供者,它将查看父级注入器(依此类推).

因此,如果您希望为整个应用程序提供单例,则需要在根注入器级别或应用程序组件注入器上定义提供程序.

但Angular2将从底部查看注入器树.这意味着将使用最低级别的提供程序,并且关联实例的范围将是此级别.

有关详细信息,请参阅此问题:


Joe*_*ida 24

我知道棱镜有分层注射器,如蒂埃里所说.

但是我在这里有另一种选择,以防你发现一个用例,你真的不想在父母那里注入它.

我们可以通过创建服务实例来实现这一点,并且提供始终返回的服务.

import { provide, Injectable } from '@angular/core';
import { Http } from '@angular/core'; //Dummy example of dependencies

@Injectable()
export class YourService {
  private static instance: YourService = null;

  // Return the instance of the service
  public static getInstance(http: Http): YourService {
    if (YourService.instance === null) {
       YourService.instance = new YourService(http);
    }
    return YourService.instance;
  }

  constructor(private http: Http) {}
}

export const YOUR_SERVICE_PROVIDER = [
  provide(YourService, {
    deps: [Http],
    useFactory: (http: Http): YourService => {
      return YourService.getInstance(http);
    }
  })
];
Run Code Online (Sandbox Code Playgroud)

然后在您的组件上使用自定义提供方法.

@Component({
  providers: [YOUR_SERVICE_PROVIDER]
})
Run Code Online (Sandbox Code Playgroud)

你应该有一个单独的服务,而不依赖于分层注入器.

我不是说这是一种更好的方法,以防万一有人出现无法使用分层注射器的问题.


Man*_*ain 16

语法已更改.检查此链接

依赖关系是注入器范围内的单例.在下面的示例中,HeroesComponent及其HeroListComponent子项之间共享一个HeroService实例.

步骤1.使用@Injectable装饰器创建单例类

@Injectable()
export class HeroService {
  getHeroes() { return HEROES;  }
}
Run Code Online (Sandbox Code Playgroud)

第2步.在构造函数中注入

export class HeroListComponent { 
  constructor(heroService: HeroService) {
    this.heroes = heroService.getHeroes();
  }
Run Code Online (Sandbox Code Playgroud)

步骤3.注册提供商

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    routing,
    HttpModule,
    JsonpModule
  ],
  declarations: [
    AppComponent,
    HeroesComponent,
    routedComponents
  ],
  providers: [
    HeroService
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)

  • 像这样的提供者:[{provide:'API_URL',useValue:'https://coolapi.com'}] (2认同)

bre*_*per 7

添加@Injectable装饰到服务,注册为在根模块提供商将使一个单身.


小智 6

这似乎对我有用

@Injectable()
export class MyStaticService {
  static instance: MyStaticService;

  constructor() {
    return MyStaticService.instance = MyStaticService.instance || this;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我称之为Angular2反模式.正确地提供服务和Angular2永远注入相同的实例.也http://stackoverflow.com/questions/12755539/why-is-singleton-considered-an-anti-pattern见 (8认同)
  • @GünterZöchbauer,请提供一些关于"正确提供服务并且Angular2将始终注入相同实例"的建议.?因为它不清楚,我通过谷歌搜索找不到任何有用的信息. (3认同)
  • 太棒了.你*应该*使用角度自己的依赖注入,但使用这种模式绝对确定你的服务是你想要的单身是没有害处的.由于您在两个不同的位置注入相同的服务,因此可以节省大量时间来寻找错误. (2认同)

Dav*_*han 5

这是Angular 2.3版的一个工作示例.只需像这个构造函数一样调用服务的构造函数(private _userService:UserService).它将为应用程序创建一个单例.

user.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Subject }    from 'rxjs/Subject';
import { User } from '../object/user';


@Injectable()
export class UserService {
    private userChangedSource;
    public observableEvents;
    loggedUser:User;

    constructor() {
       this.userChangedSource = new Subject<any>();
       this.observableEvents = this.userChangedSource.asObservable();
    }

    userLoggedIn(user:User) {
        this.loggedUser = user;
        this.userChangedSource.next(user);
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

app.component.ts

import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { UserService } from '../service/user.service';
import { User } from '../object/user';

@Component({
    selector: 'myApp',
    templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
    loggedUser:User;

    constructor(private _userService:UserService) { 
        this._userService.observableEvents.subscribe(user => {
                this.loggedUser = user;
                console.log("event triggered");
        });
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)