Search code examples
angulartypescriptangular2-changedetection

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


I would like to udpate my application view, triggered by events from service.

One of my services injects the ChangeDetectorRef. Compilation works, but I am getting an error in the browser when the App is bootstrapped: No provider for ChangeDetectorRef!.

I thought I needed to add it to my AppModule, but I can't find any documentation that suggests it is in a module that I can import there. I tried adding the class itself to the import array, but that caused an error. I also got an error trying to add it to the providers array in the module. Here is a simplified version of my service:

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

@Injectable()
export class MyService {
  private count: number = 0;
  constructor(private ref: ChangeDetectorRef){}

  increment() {
    this.count++;
    this.ref.detectChanges();
}

And the app module:

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

UPDATE

I have since tried removing my use of ChangeDetectorRef, and I still have the same problem. I am guessing there is something wrong about how I updated my System JS config.

I originally created the app with angular-cli, and was trying to update Angular on my own since they had not updated that. With the final release of Angular 2.0.0 they have updated angular-cli to use the latest version of angular. So I am going to try using their upgrade procedures and hopefully that goes better.

Update 2

The webpack/angular-cli update went well. I have the app building now with Angular 2.0.0 in and angular-cli 1.0.0-beta14. I still get the same error in the browser. I tried removing the ChangeDetectorRef from the service, but I didn't really. I had it in two services. If I remove it from both services, then my app loads fine, and works well, except for where I was trying to use ChangeDetectorRef. Once I add it back in one of the files, the browser has complains about not being able to find a provider for it.

I tried importing it in my module, but it is not a module, so the transpiler complains. I tried listing it a a provider in my module, but it does not have a provide property, so the transpiler complains. similar issues if I try putting it in the declarations array.


Solution

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