Инициировать обновление представления компонента из службы — нет поставщика для ChangeDetectorRef

Я хотел бы обновить свое представление приложения, вызванное событиями из службы.

Один из моих сервисов внедряет ChangeDetectorRef. Компиляция работает, но я получаю сообщение об ошибке в браузере при загрузке приложения: No provider for ChangeDetectorRef!.

Я думал, что мне нужно добавить его в свой AppModule, но я не могу найти документацию, которая предполагает, что он находится в модуле, который я могу туда импортировать. Я попытался добавить сам класс в массив импорта, но это вызвало ошибку. Я также получил ошибку, пытаясь добавить его в массив поставщиков в модуле. Вот упрощенная версия моего сервиса:

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. Как только я добавляю его обратно в один из файлов, браузер жалуется, что не может найти для него провайдера.

Я попытался импортировать его в свой модуль, но это не модуль, поэтому транспилятор жалуется. Я попытался указать его в качестве поставщика в своем модуле, но у него нет свойства предоставления, поэтому транспилятор жалуется. аналогичные проблемы, если я попытаюсь поместить его в массив declarations.


person EricJ    schedule 15.09.2016    source источник
comment
Попробуйте добавить ChangeDetectorRef в массив declerations   -  person Atal Kishore    schedule 15.09.2016
comment
Можете ли вы внедрить его в компонент?   -  person Paul Samsotha    schedule 15.09.2016
comment
Я могу представить, что вы не можете внедрить его в службу? Можете ли вы попробовать внедрить его в компонент, а затем передать его службе?   -  person Günter Zöchbauer    schedule 15.09.2016
comment
У меня точно такая же проблема.   -  person IvRRimUm    schedule 28.09.2016
comment
Любые решения? такая же проблема в ionic 2 RC0   -  person Davor    schedule 04.10.2016
comment
В конце концов я провел рефакторинг своего кода, чтобы лучше использовать собственное обнаружение изменений в службе, поэтому мне больше не нужен ChangeDetectorRef. Я сделал это, заменив объекты, а не просто обновив значения в объекте. Это не обязательно идеально для каждой ситуации, но это сработало для меня.   -  person EricJ    schedule 05.10.2016
comment
Это работает, если вы добавите его внутрь @component, а не в службу.   -  person Lelezeus    schedule 06.10.2016


Ответы (6)


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;
        });
    }
}
person Maciej Treder    schedule 16.10.2016

Другой вариант, если вы хотите инициировать изменение из службы, но позволить компонентам контролировать, когда и как они реагируют, — настроить подписки.

В вашем сервисе добавьте EventEmitter:

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

Когда в сервисе происходит что-то, что может потребовать запуска цикла обнаружения изменений, просто выполните:

this.changeDetectionEmitter.emit();

Теперь в любых компонентах, использующих эту службу, если им нужно прослушивать возможные триггеры изменений, они могут подписаться и реагировать соответствующим образом:

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

Мне нравится этот подход, потому что он оставляет контроль над обнаружением изменений компонентам, которым он принадлежит, но позволяет мне централизовать логику в сервисе. Службе не нужно ничего знать о пользовательском интерфейсе, она просто уведомляет всех, кто слушает, что что-то изменилось.

person TimTheEnchanter    schedule 11.02.2018

Я знаю, что мне давно пора это сделать, но я попытался просто передать 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)
    }
}
person Ian Samz    schedule 19.08.2019

в последних версиях теперь является частью ядра, поэтому просто сделайте ссылку на ядро, но используя необходимую переменную, и это должно сработать

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

Надежда помогает

person Matias Nombarasco    schedule 17.06.2017

Использовать ApplicationRef — хорошее решение. У меня есть еще несколько решений:

  1. Использование setTimeout

    @Injectable()
    export class MyService {
      private count: number = 0;
      constructor(){}
    
      increment() {
        setTimeout(() => this.count++, 0);
      }
    }
    
  2. Использование Promise

    @Injectable()
    export class MyService {
      private count: number = 0;
      constructor(){}
    
      increment() {
        Promise.resolve().then(() => this.count++);
      }
    }
    

Оба решения заставляют Angular инициировать обнаружение изменений, если вы используете Zone.js для обнаружения изменений — это способ по умолчанию, и его используют 99% приложений.

person Alex Okrushko    schedule 19.10.2019

Вам нужно добавить свой сервис к провайдерам на уровне компонентов:

@Component({
  // ...
  providers: [MyService]
})
export class SomeComponent {
  // ...
}
person ktretyak    schedule 16.08.2020