从服务触发组件视图更新 - ChangeDetectorRef 没有提供者
Trigger update of component view from service - No Provider for ChangeDetectorRef
我想更新我的应用程序视图,由来自服务的事件触发。
我的一项服务注入了 ChangeDetectorRef。编译工作正常,但在引导应用程序时我在浏览器中收到错误消息:No provider for ChangeDetectorRef!
.
我想我需要将它添加到我的 AppModule,但我找不到任何文档表明它在我可以导入的模块中。我尝试将 class 本身添加到导入数组中,但这导致了错误。我在尝试将其添加到模块中的提供程序数组时也遇到了错误。这是我的服务的简化版本:
import {Injectable, ChangeDetectorRef } from '@angular/core';
@Injectable()
export class MyService {
private count: number = 0;
constructor(private ref: ChangeDetectorRef){}
increment() {
this.count++;
this.ref.detectChanges();
}
和应用程序模块:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyService } from './my.service';
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
providers: [ MyService ],
booststrap: [ AppComponent ]
})
export AppModule {}
更新
此后我尝试删除对 ChangeDetectorRef 的使用,但我仍然遇到同样的问题。我猜我更新 System JS 配置的方式有问题。
我最初使用 angular-cli 创建该应用程序,并尝试自行更新 Angular,因为他们尚未更新。随着 Angular 2.0.0 的最终发布,他们更新了 angular-cli 以使用最新版本的 angular。所以我将尝试使用他们的升级程序,希望它会更好。
更新 2
webpack/angular-cli更新顺利。我现在使用 Angular 2.0.0 和 angular-cli 1.0.0-beta14 构建应用程序。我仍然在浏览器中遇到同样的错误。我尝试从服务中删除 ChangeDetectorRef,但我真的没有。我有两个服务。如果我从这两个服务中删除它,那么我的应用程序加载正常,并且运行良好,除了我尝试使用 ChangeDetectorRef 的地方。一旦我将它添加回其中一个文件,浏览器就会抱怨无法为其找到提供程序。
我尝试将它导入我的 模块 ,但它不是 模块 ,因此编译器会报错。我尝试在我的模块中将其列为 provider,但它没有提供 属性,因此编译器会抱怨。如果我尝试将它放在 declarations 数组中,也会出现类似的问题。
ChangeDetectorRef
不是在这里使用的选项。它正在寻找给定组件及其子组件的更改。
你的情况最好使用ApplicationRef
:
import {Injectable, ApplicationRef } from '@angular/core';
@Injectable()
export class MyService {
private count: number = 0;
constructor(private ref: ApplicationRef) {}
increment() {
this.count++;
this.ref.tick();
}
}
我用 Observables
检查了这个解决方案,它没有任何问题:
import { ApplicationRef, Injectable } from '@angular/core';
import { Observable, ReplaySubject } from "rxjs/Rx";
import * as childProcess from 'child_process';
@Injectable()
export class Reader {
private output: ReplaySubject<string> = new ReplaySubject<string>(0);
constructor(private ref: ApplicationRef) {
var readerProcess = childProcess.spawn('some-process');
readerProcess.stdout.on('data', (data) => {
this.output.next(data.toString());
this.ref.tick();
});
}
public getOutput():Observable<string> {
return this.output;
}
}
这是一个使用它的组件:
import {Component} from '@angular/core';
import { ReplaySubject, Observable } from "rxjs/Rx";
import { Reader } from './reader/reader.service';
@Component({
selector: 'app',
template: `
output:
<div>{{output}}</div>
`
})
export class App {
public output: string;
constructor(private reader: Reader) {}
ngOnInit () {
this.reader.getOutput().subscribe((val : string) => {
this.output = val;
});
}
}
在最新版本中现在是核心的一部分,所以只需引用核心但使用所需的变量并且应该做技巧
import { TestBed } from '@angular/core/testing';
import { ChangeDetectorRef } from '@angular/core';
import { MyService } from './my.service';
describe('MyService', () => {
beforeEach(() => TestBed.configureTestingModule({
providers: [ChangeDetectorRef] // <--- LOOK AT ME I'M A PROVIDER!
}));
it('should be created', () => {
const service: MyService = TestBed.get(MyService);
expect(service).toBeTruthy();
});
});
希望有所帮助
如果您想从服务触发更改但允许组件控制它们是否响应、何时响应以及如何响应,另一种选择是设置订阅。
在您的服务中,添加一个 EventEmitter
:
changeDetectionEmitter: EventEmitter<void> = new EventEmitter<void>();
当服务中发生可能需要触发变更检测周期的事情时,只需发出:
this.changeDetectionEmitter.emit();
现在,在任何使用此服务的组件中,如果它们需要侦听可能的更改触发器,它们可以订阅并做出适当的反应:
this.myService.changeDetectionEmitter.subscribe(
() => {
this.cdRef.detectChanges();
},
(err) => {
// handle errors...
}
);
我有点喜欢这种方法,因为它将变更检测的控制留给了它所属的组件,但允许我将逻辑集中在服务中。该服务不需要知道有关 UI 的任何信息,它只是通知正在收听的任何人发生了某些变化。
我知道我早就应该这样做了,但我只是尝试通过被调用的函数将 ChangeDetectorRef 传递给服务。
//myservice
import { Injectable, ChangeDetectorRef } from '@angular/core';
@Injectable()
export class MyService {
constructor(){}
increment(ref: ChangeDetectorRef, output: number) {
output++;
ref.detectChanges();
}
组件
import {Component} from '@angular/core';
import { MyService } from './myservice.service';
@Component({
selector: 'app',
template: `
output:
<div>{{output}}</div>
`
})
export class App {
public output: number;
constructor(public ref: ChangeDetectorRef, private myService: MyService) {}
ngOnInit () {
this.myService.increment(this.ref,output)
}
}
使用ApplicationRef
是一个很好的解决办法。我还有几个解决方案:
使用setTimeout
@Injectable()
export class MyService {
private count: number = 0;
constructor(){}
increment() {
setTimeout(() => this.count++, 0);
}
}
使用Promise
@Injectable()
export class MyService {
private count: number = 0;
constructor(){}
increment() {
Promise.resolve().then(() => this.count++);
}
}
如果您使用 Zone.js 进行更改检测,这两种解决方案都会强制 Angular 触发更改检测 - 这是默认方式,99% 的应用程序都使用它。
您需要在组件级别将您的服务添加到提供商:
@Component({
// ...
providers: [MyService]
})
export class SomeComponent {
// ...
}
我想更新我的应用程序视图,由来自服务的事件触发。
我的一项服务注入了 ChangeDetectorRef。编译工作正常,但在引导应用程序时我在浏览器中收到错误消息:No provider for ChangeDetectorRef!
.
我想我需要将它添加到我的 AppModule,但我找不到任何文档表明它在我可以导入的模块中。我尝试将 class 本身添加到导入数组中,但这导致了错误。我在尝试将其添加到模块中的提供程序数组时也遇到了错误。这是我的服务的简化版本:
import {Injectable, ChangeDetectorRef } from '@angular/core';
@Injectable()
export class MyService {
private count: number = 0;
constructor(private ref: ChangeDetectorRef){}
increment() {
this.count++;
this.ref.detectChanges();
}
和应用程序模块:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyService } from './my.service';
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
providers: [ MyService ],
booststrap: [ AppComponent ]
})
export AppModule {}
更新
此后我尝试删除对 ChangeDetectorRef 的使用,但我仍然遇到同样的问题。我猜我更新 System JS 配置的方式有问题。
我最初使用 angular-cli 创建该应用程序,并尝试自行更新 Angular,因为他们尚未更新。随着 Angular 2.0.0 的最终发布,他们更新了 angular-cli 以使用最新版本的 angular。所以我将尝试使用他们的升级程序,希望它会更好。
更新 2
webpack/angular-cli更新顺利。我现在使用 Angular 2.0.0 和 angular-cli 1.0.0-beta14 构建应用程序。我仍然在浏览器中遇到同样的错误。我尝试从服务中删除 ChangeDetectorRef,但我真的没有。我有两个服务。如果我从这两个服务中删除它,那么我的应用程序加载正常,并且运行良好,除了我尝试使用 ChangeDetectorRef 的地方。一旦我将它添加回其中一个文件,浏览器就会抱怨无法为其找到提供程序。
我尝试将它导入我的 模块 ,但它不是 模块 ,因此编译器会报错。我尝试在我的模块中将其列为 provider,但它没有提供 属性,因此编译器会抱怨。如果我尝试将它放在 declarations 数组中,也会出现类似的问题。
ChangeDetectorRef
不是在这里使用的选项。它正在寻找给定组件及其子组件的更改。
你的情况最好使用ApplicationRef
:
import {Injectable, ApplicationRef } from '@angular/core';
@Injectable()
export class MyService {
private count: number = 0;
constructor(private ref: ApplicationRef) {}
increment() {
this.count++;
this.ref.tick();
}
}
我用 Observables
检查了这个解决方案,它没有任何问题:
import { ApplicationRef, Injectable } from '@angular/core';
import { Observable, ReplaySubject } from "rxjs/Rx";
import * as childProcess from 'child_process';
@Injectable()
export class Reader {
private output: ReplaySubject<string> = new ReplaySubject<string>(0);
constructor(private ref: ApplicationRef) {
var readerProcess = childProcess.spawn('some-process');
readerProcess.stdout.on('data', (data) => {
this.output.next(data.toString());
this.ref.tick();
});
}
public getOutput():Observable<string> {
return this.output;
}
}
这是一个使用它的组件:
import {Component} from '@angular/core';
import { ReplaySubject, Observable } from "rxjs/Rx";
import { Reader } from './reader/reader.service';
@Component({
selector: 'app',
template: `
output:
<div>{{output}}</div>
`
})
export class App {
public output: string;
constructor(private reader: Reader) {}
ngOnInit () {
this.reader.getOutput().subscribe((val : string) => {
this.output = val;
});
}
}
在最新版本中现在是核心的一部分,所以只需引用核心但使用所需的变量并且应该做技巧
import { TestBed } from '@angular/core/testing';
import { ChangeDetectorRef } from '@angular/core';
import { MyService } from './my.service';
describe('MyService', () => {
beforeEach(() => TestBed.configureTestingModule({
providers: [ChangeDetectorRef] // <--- LOOK AT ME I'M A PROVIDER!
}));
it('should be created', () => {
const service: MyService = TestBed.get(MyService);
expect(service).toBeTruthy();
});
});
希望有所帮助
如果您想从服务触发更改但允许组件控制它们是否响应、何时响应以及如何响应,另一种选择是设置订阅。
在您的服务中,添加一个 EventEmitter
:
changeDetectionEmitter: EventEmitter<void> = new EventEmitter<void>();
当服务中发生可能需要触发变更检测周期的事情时,只需发出:
this.changeDetectionEmitter.emit();
现在,在任何使用此服务的组件中,如果它们需要侦听可能的更改触发器,它们可以订阅并做出适当的反应:
this.myService.changeDetectionEmitter.subscribe(
() => {
this.cdRef.detectChanges();
},
(err) => {
// handle errors...
}
);
我有点喜欢这种方法,因为它将变更检测的控制留给了它所属的组件,但允许我将逻辑集中在服务中。该服务不需要知道有关 UI 的任何信息,它只是通知正在收听的任何人发生了某些变化。
我知道我早就应该这样做了,但我只是尝试通过被调用的函数将 ChangeDetectorRef 传递给服务。
//myservice
import { Injectable, ChangeDetectorRef } from '@angular/core';
@Injectable()
export class MyService {
constructor(){}
increment(ref: ChangeDetectorRef, output: number) {
output++;
ref.detectChanges();
}
组件
import {Component} from '@angular/core';
import { MyService } from './myservice.service';
@Component({
selector: 'app',
template: `
output:
<div>{{output}}</div>
`
})
export class App {
public output: number;
constructor(public ref: ChangeDetectorRef, private myService: MyService) {}
ngOnInit () {
this.myService.increment(this.ref,output)
}
}
使用ApplicationRef
是一个很好的解决办法。我还有几个解决方案:
使用
setTimeout
@Injectable() export class MyService { private count: number = 0; constructor(){} increment() { setTimeout(() => this.count++, 0); } }
使用
Promise
@Injectable() export class MyService { private count: number = 0; constructor(){} increment() { Promise.resolve().then(() => this.count++); } }
如果您使用 Zone.js 进行更改检测,这两种解决方案都会强制 Angular 触发更改检测 - 这是默认方式,99% 的应用程序都使用它。
您需要在组件级别将您的服务添加到提供商:
@Component({
// ...
providers: [MyService]
})
export class SomeComponent {
// ...
}