Подтвердить что ты не робот

Триггерное обновление представления компонентов из службы - Нет провайдера для 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 {}

UPDATE

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

К вашим услугам добавьте EventEmitter:

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

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

this.changeDetectionEmitter.emit();

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

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

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

Ответ 3

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

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

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

Ответ 4

Я знаю, что я давно этого ждал, но я попытался просто передать 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)
    }
}

Ответ 5

Использовать 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% приложений используют его.