如何在角度2中创建单例服务?

我曾经读到过注入当引导应该让所有的孩子共享相同的实例,但是我的主要和头部组件(主应用包括头部组件和路由器出口)每个都得到我的服务的一个单独的实例。

我有一个 FacebookService,我用它来调用 facebook 的 javascript api 和一个使用 FacebookService 的 UserService。这是我的鞋带:

bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);

从我的日志看起来像是引导调用完成了,然后我看到在每个构造函数中的代码运行之前,FacebookService 和 UserService 被创建,MainAppComponent,HeaderComponent 和 DefaultComponent:

enter image description here

168332 次浏览

最新情况(角度6 +)

建议的创建 单身服务的方法已经更改。现在建议在服务的 @Injectable装饰器中指定它应该在“ root”中提供。这对我来说很有意义,而且根本不需要列出模块中提供的所有服务。只需在需要时导入服务,它们就会在适当的位置注册自己。还可以使用 指定一个模块,因此只有在导入模块时才提供它。

@Injectable({
providedIn: 'root',
})
export class ApiService {
}

最新情况(转角二)

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

CoreModule

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 { }

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 { }

原始答案

如果在 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) {}
}

事实上,在“提供者”中列出类会创建一个新的实例,如果任何父组件已经列出了类,那么子组件就不需要这样做了,如果他们这样做了,他们就会得到一个新的实例。

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

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

  • 引导应用程序时配置的根目录
  • 每个组件一个注入器。如果您在另一个组件中使用一个组件。组件注入器是父组件的子组件。应用程序组件(在引导应用程序时指定的组件)将根注入器作为父组件)。

当 Angular2尝试在组件构造函数中注入一些东西时:

  • 它查看与组件相关联的注入器。如果有匹配的实例,它将使用它来获取相应的实例。这个实例是惰性创建的,是这个注入器的一个单例。
  • 如果在这个级别没有提供程序,它将查看父注入器(等等)。

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

但 Angular2会从底部观察注入树。这意味着将使用最低级别的提供程序,并且关联实例的范围将为此级别。

更多细节见下面的问题:

我知道棱角像蒂埃里说的那样有分层注射器。

但是我在这里还有另一个选项,以防您发现一个用例,其中您并不真正想要在父用户中注入它。

我们可以通过创建服务的实例来实现这一点,并且总是返回这一点。

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);
}
})
];

然后在组件上使用自定义提供方法。

@Component({
providers: [YOUR_SERVICE_PROVIDER]
})

您应该有一个单例服务,而不依赖于分层注入器。

我不是说这是一个更好的方法,只是以防万一有人有问题,分层注入器是不可能的。

看起来对我很有效

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


constructor() {
return MyStaticService.instance = MyStaticService.instance || this;
}
}

语法已更改。请检查此 链接

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

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

@Injectable()
export class HeroService {
getHeroes() { return HEROES;  }
}

步骤2。在构造函数中注入

export class HeroListComponent {
constructor(heroService: HeroService) {
this.heroes = heroService.getHeroes();
}

步骤3. 注册提供程序

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

下面是一个使用 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);
}


...
}

应用程序组件

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");
});
}
...
}

可以在提供程序中使用 useValue

import { MyService } from './my.service';


@NgModule({
...
providers: [ { provide: MyService, useValue: new MyService() } ],
...
})

@Injectable装饰器添加到服务中,还有将其注册为根模块中的提供程序将使其成为一个单例。

只需在 app.module.ts 中将服务声明为提供者即可。

它帮了我大忙。

providers: [Topic1Service,Topic2Service,...,TopicNService],

然后使用构造函数私有参数实例化它:

constructor(private topicService: TopicService) { }

或者因为如果您的服务来自 html,-prod 选项将声明:

Property 'topicService' is private and only accessible within class 'SomeComponent'.

为您的服务添加一个成员,并用构造函数中接收到的实例填充它:

export class SomeComponent {
topicService: TopicService;
constructor(private topicService: TopicService) {
this.topicService= topicService;
}
}

从 Angular@6,你可以在 Injectable中得到 providedIn

@Injectable({
providedIn: 'root'
})
export class UserService {


}

检查 我是医生

有两种方法可以使一个服务成为单一的角度:

  1. 声明应该在应用程序根目录中提供服务。
  2. 将服务包含在 AppModule 或仅由 AppModule 导入的模块中。

从 Angular 6.0开始,创建单例服务的首选方法是在服务上指定它应该在应用程序根目录中提供。这是通过在服务的@Injectable 装饰器上设置 provision dIn 为 root 来完成的:

  1. 如果希望在应用程序级别实现服务单例 您应该在 < strong > app.module.ts 中定义它

    提供者: [ 我的应用服务 ] (您也可以在子模块中定义相同的内容,以使其特定于该模块)

    • 不要在提供程序中添加这个服务,因为提供程序为那个组件创建了一个实例,这打破了单例概念,只需通过构造函数注入即可。
  2. 如果您想在组件级别定义单例服务 创建服务,在 app.module.ts 中添加该服务,并在特定组件中添加提供者数组,如下面的代码片段所示。

    @ 组件({ 选择器: ‘ app-root’, TemplateUrl:’./test.Component. .html’, StyleUrls: [’./test.Component. scss’] , 提供程序: [ TestMyService ] })

  3. 角度6提供了在应用层添加服务的新方法。 您可以在@Injectable ()中设置以下配置,而不是将服务类添加到 AppModule 中的 Provider []数组中:

    @ Injectable ({ provision dIn: ‘ root’}) 导出类 MyService { ... }

“新语法”确实提供了一个优势: 服务可以由 Angular (幕后)懒惰地加载,冗余代码可以自动删除。这可以带来更好的性能和加载速度——尽管这只适用于更大的服务和应用程序。

除了上面这些出色的答案之外,如果您的单例模式中的内容仍然没有表现为单例模式,那么可能还缺少其他一些东西。当我在单例模式上调用一个公共函数并发现它使用了错误的变量时,我遇到了这个问题。原来问题在于,对于单例模式中的任何公共函数,this都不能保证绑定到单例模式。这可以通过以下 给你建议来纠正,如下所示:

@Injectable({
providedIn: 'root',
})
export class SubscriptableService {
public serviceRequested: Subject<ServiceArgs>;
public onServiceRequested$: Observable<ServiceArgs>;


constructor() {
this.serviceRequested = new Subject<ServiceArgs>();
this.onServiceRequested$ = this.serviceRequested.asObservable();


// save context so the singleton pattern is respected
this.requestService = this.requestService.bind(this);
}


public requestService(arg: ServiceArgs) {
this.serviceRequested.next(arg);
}
}

或者,你可以简单地将类成员声明为 public static而不是 public,这样上下文就无关紧要了,但是你必须像 SubscriptableService.onServiceRequested$那样访问它们,而不是使用依赖注入并通过 this.subscriptableService.onServiceRequested$访问它们。

家长及儿童服务中心

我在父服务及其子服务使用不同实例时遇到了麻烦。若要强制使用一个实例,可以在应用程序模块提供程序中使用引用子实例的父实例作为别名。父级将不能访问子级的属性,但是两个服务将使用相同的实例。https://angular.io/guide/dependency-injection-providers#aliased-class-providers

应用程序模块

providers: [
ChildService,
// Alias ParentService w/ reference to ChildService
{ provide: ParentService, useExisting: ChildService}
]

应用程序模块范围外的组件使用的服务

在创建由组件和服务组成的库时,我遇到了一个问题,需要创建两个实例。一个由我的角度项目和一个由我的库内的组件。解决办法:

外部组件

@Component({...})
export class MyOutsideComponent {
@Input() serviceInstance: MyOutsideService;
...
}

My-inside. 组件

  constructor(public myService: MyOutsideService) { }

My-inside. Component. hmtl

<app-my-outside [serviceInstance]="myService"></app-my-outside>

singleton service是一个应用程序中只存在一个实例的服务。

(2)方法为您的应用程序提供单例服务。

  1. 使用 providedIn属性,或者

  2. 直接在应用程序的 AppModule中提供模块

使用 ProviddIn

从 Angular 6.0开始,创建单例服务的首选方法是将 providedIn设置为根服务的 @Injectable()装饰器。这告诉 Angular 在应用程序根目录中提供服务。

import { Injectable } from '@angular/core';


@Injectable({
providedIn: 'root',
})
export class UserService {
}

NgModule 提供程序数组

在6.0之前使用 Angular 版本构建的应用程序中,服务是注册的 NgModule 提供程序阵列,如下所示:

@NgModule({
...
providers: [UserService],
...
})

如果这个 NgModule是根 AppModule,那么 UserService 将是一个单例并且可以在整个应用程序中使用。尽管您可能会看到它是这样编码的,但是在服务本身上使用 @Injectable()装饰器的 providedIn属性更可取,因为它使您的服务可以动摇树。

角度服务的范围取决于您在根模块、延迟加载模块或组件级别提供服务的位置。

这里有一个视频,用真实的例子美丽地描述了它。

Https://youtu.be/adyqnqrer3o