即使 @Injectable 提供了 providedIn: 'root',服务也会创建另一个实例。我怎样才能确保服务是单例的?
Service creates another instance even though @Injectable has providedIn: 'root'. How can I make sure the service is a Singleton?
我有 2 个服务位于 /services,AccountService 和 AuthenticationService。我还有 2 个页面:accounts.page 和 add-accounts.page 位于 /accounts 中,add-accounts.page 位于 /accounts/add-accounts 中。
我在 accounts.page 和 add-accounts.page 中都使用了 AccountService,它从本地 json 加载帐户并将它们存储在 Ionic/Storage 中。所以计划是使用该服务从存储中获取帐户并添加新帐户。我在服务中有一个 ReplaySubject 来检测我想在帐户 html 页面中使用的新更改,以显示帐户数组。
我在 Angular 版本 7
中使用 Ionic 4
在四处修改后,我发现该服务正在获取 2 个实例,因此我没有向最初开始使用的 ReplaySubject 添加新的更改。我通过向我的服务的构造函数添加 console.log 并在控制台中查找发现了这一点,我在从帐户页面转到添加帐户页面后发现了相同 console.log 的 2 个实例。所以我在添加帐户页面中对 accountService 所做的更改是另一个实例,并且在第一个实例中没有更改。
当我使用我的其他服务 (AuthenticationService) 对此进行测试时,我没有在控制台中获得 2 console.log 输出。
我仔细检查了 @Injectable 装饰器是否具有 providedIn: 'root' 的正确语法,并在测试了删除 providedIn: 'root' 部分并在 [= 中导入服务的旧 angular 方法之后88=] 并将其添加到提供者数组中,我得到一个 NullInjectorError: No provider for AccoutService Error 我无法解决。
我尝试使用 ionic g service services/newService
创建一个新服务并制作了一个函数来输出一些文本。我还创建了一个 BehaviourSubject 来查看和跟踪状态变化。它看起来像这样:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class NewAccoutService {
authenticationState = new BehaviorSubject(false);
constructor() {
this.authenticationState.next(true);
console.log('This text should only happen once');
}
consoleLog() {
console.log('did it work?');
}
}
我还尝试做的是创建一个新的空白离子项目并创建 2 个页面和 2 个服务。在那个项目中,他们表现得像他们应该的那样,只被创建一次。
在与我的同事交谈后,他们建议创建一个具有相同文件结构的新测试项目,并将代码 copy/paste 放入新项目中,这可能会解决问题,因为某些文件可能未创建。然而,这并没有解决,所以可能还有其他问题。
account.service.ts
这里是AccountService的相关代码
import { Injectable } from '@angular/core';
@Injectable(
{ providedIn: 'root' }
)
export class AccountService {
changed: ReplaySubject<Account[]> = new ReplaySubject(1);
constructor(
private http: HttpClient,
private storage: Storage
) {
console.log('Account Service constructor Text');
// some other code
}
}
account.page.ts
这是帐户页面的相关代码
import { AccountService } from '../services/account.service.js';
import { AuthenticationService } from '../services/authentication.service.js';
@Component({
selector: 'app-accounts',
templateUrl: './accounts.page.html',
styleUrls: ['./accounts.page.scss'],
})
export class AccountsPage implements OnInit,OnDestroy {
accounts: Account[] = [];
accountsLoaded = false;
subscription: any;
constructor(private http: HttpClient,
private accountService: AccountService,
private router: Router,
private authService: AuthenticationService,
private changeRef: ChangeDetectorRef,
private testService: NewAccoutService) {
}
ngOnInit() {
this.testService.authenticationState.next(false);
console.log('Test Service state: ', this.testService.authenticationState.value);
}
}
添加-account.page.ts
添加账号页面的相关代码如下
import { AccountService } from '../../services/account.service';
import { AuthenticationService } from '../../services/authentication.service';
@Component({
selector: 'app-add-accounts',
templateUrl: './add-accounts.page.html',
styleUrls: ['./add-accounts.page.scss'],
})
export class AddAccountsPage implements OnInit {
public account: FormGroup;
public currentSliderValue: number = 1;
constructor( private platform: Platform,
private changeRef: ChangeDetectorRef,
private router: Router,
private accountService: AccountService,
private authentication: AuthenticationService,
private testService: NewAccoutServiceService ) {
this.testService.authenticationState.next(false);
console.log('Test Service state: ', this.testService.authenticationState.value);
}
}
当我加载帐户页面时,我希望看到以下内容:
预期结果:当我加载 accounts.page 时,我想看到 console.log 消息。 this.testService.authenticationState.value
是 false
.
当我从 accounts.page 转到添加帐户时,AccountService 中的 console.log 应该 而不是 被再次调用。 this.testService.authenticationState.value
是 true
.
实际结果:正在加载到 account.page 我看到 console.log 消息并且 this.testService.authenticationState.value
是 false
。从 accounts.page 加载到添加-accounts.page 我得到另一个构造函数 console.log 消息也 this.testService.authenticationState.value
仍然是 false
我 运行 无法尝试,因为我对 Ionic 和 Angular 还是个新手。每个服务 都应该 作为单例工作,但不知何故不起作用。如果您需要其他代码来帮助我解决此问题,请告诉我。
编辑:
我找到了非常愚蠢的解决方案。 VS Code 曾在 accounts.page.ts 中为我的服务生成导入,出于某种未知原因,它在文件位置后添加了 .js
我的意思是看这个比较。
import { AuthenticationService } from '../services/authentication.service';
import { AuthenticationService } from '../services/authentication.service.js';
在工作中查看我的代码的 3 个人中没有一个发现这个问题。直到今天又尝试了 3 个小时并非常仔细地查看代码后,我和我的同事突然想到“为什么那个导入中有一个 .js,那是不对的”。
无论你想在何处接收主题的值,都需要通过它的可观察性订阅主题。
@Injectable({
providedIn: 'root'
})
export class Service {
randomSubject = new BehaviorSubject(initialValue);
randomObservable = authenticationStateSubject.asObservable();
}
然后在你的组件中,你需要在初始化时订阅它。
export class RandomComponent implements OnInit {
constructor(private service: Service) {}
ngOnInit(): void {
this.service.randomObservable.subscribe(randomValue => console.log(randomValue));
}
}
每次分配新值时都会发生订阅回调中的任何事情。
要更新它的值,请在主题上调用 next 并传入一个值。
this.service.randomSubject.next(newValue);
请注意,但是,如果您计划执行 Http 请求来检索本地 JSON,我会使用 ReplaySubject 来代替 b/c 您不必初始化具有价值的主题。
如果您使用延迟加载检查模块。可能服务出现在延迟加载模块提供程序中。
我找到了解决方案
对于以某种方式遇到此错误的任何人。在我的 VS Code 生成的导入中,文件位置后有一个 .js 扩展名。
之前:
import { AuthenticationService } from '../services/authentication.service.js';
之后:
import { AuthenticationService } from '../services/authentication.service';
删除此扩展程序后,它可以正常工作。我不知道为什么 VS Code 只在我的 accounts.page.ts 中做了一次,但那是另一个不再发生的问题。
从 providers
中删除
我有类似的问题(我尝试在单例服务中共享 rxjs 流)。我测试 angular 使用以下代码创建许多理论上的单例服务 (LoginService) 实例:
private timestamp = +new Date();
constructor() {
this.timestamp = +new Date();
console.log('constructor: ' + this.timestamp);
}
public getTimestamp() {
return this.timestamp;
}
然后在不同的页面中打印 getTimestamp()。在控制台中,我多次看到构造函数 运行(但理论上对于单身人士来说,它应该只 运行 一次)。
在这一点上,我发现我将我的 LoginService 放在许多组件的提供程序部分 - 这就是为什么 angular 创建多个单例服务实例的原因 - 在全部删除之后 - 它只创建一个 :)
@Component({
selector: '...',
providers: [LoginService, ...], // REMOVE this service from all components
styleUrls: [...],
templateUrl: '...',
})
我有 2 个服务位于 /services,AccountService 和 AuthenticationService。我还有 2 个页面:accounts.page 和 add-accounts.page 位于 /accounts 中,add-accounts.page 位于 /accounts/add-accounts 中。 我在 accounts.page 和 add-accounts.page 中都使用了 AccountService,它从本地 json 加载帐户并将它们存储在 Ionic/Storage 中。所以计划是使用该服务从存储中获取帐户并添加新帐户。我在服务中有一个 ReplaySubject 来检测我想在帐户 html 页面中使用的新更改,以显示帐户数组。 我在 Angular 版本 7
中使用 Ionic 4在四处修改后,我发现该服务正在获取 2 个实例,因此我没有向最初开始使用的 ReplaySubject 添加新的更改。我通过向我的服务的构造函数添加 console.log 并在控制台中查找发现了这一点,我在从帐户页面转到添加帐户页面后发现了相同 console.log 的 2 个实例。所以我在添加帐户页面中对 accountService 所做的更改是另一个实例,并且在第一个实例中没有更改。
当我使用我的其他服务 (AuthenticationService) 对此进行测试时,我没有在控制台中获得 2 console.log 输出。 我仔细检查了 @Injectable 装饰器是否具有 providedIn: 'root' 的正确语法,并在测试了删除 providedIn: 'root' 部分并在 [= 中导入服务的旧 angular 方法之后88=] 并将其添加到提供者数组中,我得到一个 NullInjectorError: No provider for AccoutService Error 我无法解决。
我尝试使用 ionic g service services/newService
创建一个新服务并制作了一个函数来输出一些文本。我还创建了一个 BehaviourSubject 来查看和跟踪状态变化。它看起来像这样:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class NewAccoutService {
authenticationState = new BehaviorSubject(false);
constructor() {
this.authenticationState.next(true);
console.log('This text should only happen once');
}
consoleLog() {
console.log('did it work?');
}
}
我还尝试做的是创建一个新的空白离子项目并创建 2 个页面和 2 个服务。在那个项目中,他们表现得像他们应该的那样,只被创建一次。
在与我的同事交谈后,他们建议创建一个具有相同文件结构的新测试项目,并将代码 copy/paste 放入新项目中,这可能会解决问题,因为某些文件可能未创建。然而,这并没有解决,所以可能还有其他问题。
account.service.ts
这里是AccountService的相关代码
import { Injectable } from '@angular/core';
@Injectable(
{ providedIn: 'root' }
)
export class AccountService {
changed: ReplaySubject<Account[]> = new ReplaySubject(1);
constructor(
private http: HttpClient,
private storage: Storage
) {
console.log('Account Service constructor Text');
// some other code
}
}
account.page.ts
这是帐户页面的相关代码
import { AccountService } from '../services/account.service.js';
import { AuthenticationService } from '../services/authentication.service.js';
@Component({
selector: 'app-accounts',
templateUrl: './accounts.page.html',
styleUrls: ['./accounts.page.scss'],
})
export class AccountsPage implements OnInit,OnDestroy {
accounts: Account[] = [];
accountsLoaded = false;
subscription: any;
constructor(private http: HttpClient,
private accountService: AccountService,
private router: Router,
private authService: AuthenticationService,
private changeRef: ChangeDetectorRef,
private testService: NewAccoutService) {
}
ngOnInit() {
this.testService.authenticationState.next(false);
console.log('Test Service state: ', this.testService.authenticationState.value);
}
}
添加-account.page.ts
添加账号页面的相关代码如下
import { AccountService } from '../../services/account.service';
import { AuthenticationService } from '../../services/authentication.service';
@Component({
selector: 'app-add-accounts',
templateUrl: './add-accounts.page.html',
styleUrls: ['./add-accounts.page.scss'],
})
export class AddAccountsPage implements OnInit {
public account: FormGroup;
public currentSliderValue: number = 1;
constructor( private platform: Platform,
private changeRef: ChangeDetectorRef,
private router: Router,
private accountService: AccountService,
private authentication: AuthenticationService,
private testService: NewAccoutServiceService ) {
this.testService.authenticationState.next(false);
console.log('Test Service state: ', this.testService.authenticationState.value);
}
}
当我加载帐户页面时,我希望看到以下内容:
预期结果:当我加载 accounts.page 时,我想看到 console.log 消息。 this.testService.authenticationState.value
是 false
.
当我从 accounts.page 转到添加帐户时,AccountService 中的 console.log 应该 而不是 被再次调用。 this.testService.authenticationState.value
是 true
.
实际结果:正在加载到 account.page 我看到 console.log 消息并且 this.testService.authenticationState.value
是 false
。从 accounts.page 加载到添加-accounts.page 我得到另一个构造函数 console.log 消息也 this.testService.authenticationState.value
仍然是 false
我 运行 无法尝试,因为我对 Ionic 和 Angular 还是个新手。每个服务 都应该 作为单例工作,但不知何故不起作用。如果您需要其他代码来帮助我解决此问题,请告诉我。
编辑:
我找到了非常愚蠢的解决方案。 VS Code 曾在 accounts.page.ts 中为我的服务生成导入,出于某种未知原因,它在文件位置后添加了 .js
我的意思是看这个比较。
import { AuthenticationService } from '../services/authentication.service';
import { AuthenticationService } from '../services/authentication.service.js';
在工作中查看我的代码的 3 个人中没有一个发现这个问题。直到今天又尝试了 3 个小时并非常仔细地查看代码后,我和我的同事突然想到“为什么那个导入中有一个 .js,那是不对的”。
无论你想在何处接收主题的值,都需要通过它的可观察性订阅主题。
@Injectable({
providedIn: 'root'
})
export class Service {
randomSubject = new BehaviorSubject(initialValue);
randomObservable = authenticationStateSubject.asObservable();
}
然后在你的组件中,你需要在初始化时订阅它。
export class RandomComponent implements OnInit {
constructor(private service: Service) {}
ngOnInit(): void {
this.service.randomObservable.subscribe(randomValue => console.log(randomValue));
}
}
每次分配新值时都会发生订阅回调中的任何事情。
要更新它的值,请在主题上调用 next 并传入一个值。
this.service.randomSubject.next(newValue);
请注意,但是,如果您计划执行 Http 请求来检索本地 JSON,我会使用 ReplaySubject 来代替 b/c 您不必初始化具有价值的主题。
如果您使用延迟加载检查模块。可能服务出现在延迟加载模块提供程序中。
我找到了解决方案
对于以某种方式遇到此错误的任何人。在我的 VS Code 生成的导入中,文件位置后有一个 .js 扩展名。
之前:
import { AuthenticationService } from '../services/authentication.service.js';
之后:
import { AuthenticationService } from '../services/authentication.service';
删除此扩展程序后,它可以正常工作。我不知道为什么 VS Code 只在我的 accounts.page.ts 中做了一次,但那是另一个不再发生的问题。
从 providers
中删除
我有类似的问题(我尝试在单例服务中共享 rxjs 流)。我测试 angular 使用以下代码创建许多理论上的单例服务 (LoginService) 实例:
private timestamp = +new Date();
constructor() {
this.timestamp = +new Date();
console.log('constructor: ' + this.timestamp);
}
public getTimestamp() {
return this.timestamp;
}
然后在不同的页面中打印 getTimestamp()。在控制台中,我多次看到构造函数 运行(但理论上对于单身人士来说,它应该只 运行 一次)。
在这一点上,我发现我将我的 LoginService 放在许多组件的提供程序部分 - 这就是为什么 angular 创建多个单例服务实例的原因 - 在全部删除之后 - 它只创建一个 :)
@Component({
selector: '...',
providers: [LoginService, ...], // REMOVE this service from all components
styleUrls: [...],
templateUrl: '...',
})