Angular - Наблюдаемое изменение обнаружено в контроллере компонента, но не в его шаблоне

ВТОРОЙ РЕДАКТИРОВАТЬ:

Так! Я разобрался с проблемой, но чтобы понять проблему, вы должны сначала понять цель:

В настоящее время я работаю над Angular Chrome-Extension который имеет постоянный фоновый скрипт (в отличие от всех примеров и учебных пособий, которые я нашел, которые объясняют только непостоянный фоновый скрипт или пример страницы событий).

Фоновый скрипт на самом деле является угловым компонентом, который содержит logic service которые должны быть доступны в различных компонентах Chrome (всплывающее окно, параметры и т. д.)

  1. При внедрении логического сервиса с использованием наивного метода Angular DI все начинает запутываться, потому что с точки зрения chrome страница фонового сценария и страница всплывающего сценария имеют разные window объект, который приводит к наличию 2 экземпляров приложения, 1 в "фоновом окне" и другой в "всплывающем окне".
  2. При использовании Chrome API chrome.extension.getBackgroundPage() чтобы получить сервис во всплывающем окне, я получаю правильный экземпляр, но теперь возникает другая проблема, изменения в фоновом сервисе не отражаются, потому что сервис находится вне контекста приложения Angular.

Итак, у меня есть новый вопрос:

Можно ли сказать Angular "добавить / посмотреть" службу Angular, для которой он не был создан, и добавить ее в контекст приложения?

РЕДАКТИРОВАНИЕ: я добавил 2 сервиса, кажется, у меня есть более глубокая проблема в моем проекте, потому что этот демонстрационный код работает, как и ожидалось... будет продолжать обновляться после разрешения.

Оригинальный вопрос

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

Служба содержит наблюдаемый и родитель подписывается на него + передавая его через async труба к входу ребенка.

Проблема в том, что родитель выбирает изменения (только из контроллера), и ни один из интерфейсов не обновляется.

Я могу вызвать ChangeDetectorRef.detectChanges() в функции подписки, но мне кажется, что это очень странно, потому что async труба должна была понять эти изменения, но это не так.

Какие-либо предложения?

Рассмотрим следующий код (это работает как новый проект)

Родительский компонент:

  @Component({
  selector: 'parent',
  template: '<div>
                <button (click)="logic.doAction()">Get data</button>
                <child [someInput]="logic.data$ | async"></child>
                <div>data: {{logic.data$ | async | json}}</div>
             </div>'
})
export class ParentComponent {
     constructor(public logic: LogicService) {}
}

Дочерний компонент:

@Component({
  selector: 'child',
  template: '<span>{{someInput}}</child>'
})
export class ChildComponent {
    @Input() someInput: string;
    constructor() {}
}

Логический сервис:

@Injectable()
export class LogicService {
  private readonly dataSubject: BehaviorSubject<Array<string>>;
  private readonly _data$: Observable<Array<string>>;

  constructor(private api: ApiService) {
    this.dataSubject = new BehaviorSubject([]);
    this._data$ = this.dataSubject.asObservable();
  }

  doAction(): void {
    this.api.fetchFromAPI().then((data) => this.dataSubject.next(data));
  }

  get data$(): Observable<Array<string>> {
    return this._data$;
  }
}

API сервис:

@Injectable()
export class ApiService {
  fetchFromAPI(): Promise<Array<string>> {
    return new Promise((res, rej) => {
      setInterval(() => {
        res(['some', 'demo', 'data']);
      }, 4000);
    });
  }
}

2 ответа

Решение

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

@Directive({
  selector: '[detectChanges]'
})
export class DetectChangesDirective implements OnInit, OnDestroy {
  private subscription: Subscription;

  @Input('detectChanges') observable$: Observable<any>;


  constructor(private cd: ChangeDetectorRef) {
  }

  ngOnInit() {
    this.subscription = this.observable$.subscribe((() => {
      this.cd.detectChanges();
    }));
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

Использовать его очень просто, просто примените его к представлению о проблемах с обновлением, например:

<div [detectChanges]="logic.data$">
  <button (click)="logic.doAction()">Get data</button>
  <child [someInput]="logic.dataValue"></child>
  <div>data: {{logic.dataValue | json}}</div>
</div>

ВАЖНЫЙ:

  1. Обратите внимание, что я больше не использую async труба & вместо этого я передаю фактическое значение туда, где я его связываю.
  2. Я передаю Observable в директиву, которая отслеживает, когда значение изменяется

ИМО, если сервисный метод возвращает наблюдаемое, его следует развернуть (подписать) в том месте, где нужны данные. Я бы сделал это следующим образом:

<child [someInput]="service.observable"></child>

А у ребенка:

export class ChildComponent {
    @Input() someInput: Observable;
    // subscribe to someInput in the child
}
Другие вопросы по тегам