Trigger update of component view from service - No Provider for ChangeDetectorRef

♀尐吖头ヾ 提交于 2019-11-28 08:03:05
Maciej Treder

ChangeDetectorRef is not option to use here. It is looking for changes in a given component and its children.

In your case It would be better to use 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();
  }
}

I checked this solution with Observables and it works without any problems:

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

and here is a component which uses it:

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

Another option if you want to trigger the change from the service but allow the components to control if, when, and how they respond is to set up subscriptions.

In your service, add an EventEmitter:

changeDetectionEmitter: EventEmitter<void> = new EventEmitter<void>();

When something happens in the service that might need to trigger a change detection cycle, simply emit:

this.changeDetectionEmitter.emit();

Now, in any components that use this service, if they need to listen for possible change triggers, they can subscribe and react appropriately:

this.myService.changeDetectionEmitter.subscribe(
    () => {
      this.cdRef.detectChanges();
    },
    (err) => {
      // handle errors...
    }
  );

I kinda like this approach because it leaves control of change detection with the components where it belongs, but allows me to centralize logic in the service. The service doesn't need to know anything about the UI, it's just notifying anyone who is listening that something changed.

Matias Nombarasco

in the latest versions is part of the core now, so just make a reference to the core but using the variable needed and should do trick

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

Hope helps

I know im long overdue for this but I tried simply to pass the ChangeDetectorRef to the Service via the function being called.

//myservice
import { Injectable, ChangeDetectorRef } from '@angular/core';

@Injectable()
export class MyService {
  constructor(){}

  increment(ref: ChangeDetectorRef, output: number) {
    output++;
    ref.detectChanges();
}

The Component

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)
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!