Angular - 在组件的控制器中检测到可观察到的变化,但在其模板中未检测到

Angular - Observable change detected in component's controller but not in its' template

第二次编辑:

所以!我已经弄清楚了问题所在,但要了解问题,您必须先了解目标:

我目前正在开发一个 Angular Chrome-Extension,它有一个持久的后台脚本(不像我发现的所有示例和教程只解释非持久的后台脚本 - 或事件页面 Example) .

后台脚本实际上是一个 Angular 组件,其中包含 logic service 应该可以在不同的 chrome 组件(弹出窗口、选项等)中访问

  1. 当使用简单的 Angular DI 方法注入逻辑服务时,事情开始变得混乱,因为从 chrome 的角度来看,后台脚本页面和弹出脚本页面有不同的 window 对象导致有 2 个应用程序实例,1 个在 'background window' 中,另一个在 'popup window'.
  2. 当使用 chrome 的 API chrome.extension.getBackgroundPage() 在弹出窗口中获取服务时,我得到了正确的实例,但现在出现另一个问题,后台服务的更改没有反映,因为该服务在 Angular 的应用上下文之外。

所以我有一个新问题是:

是否可以告诉 Angular 给 'add/watch' 一个未被实例化的 Angular 服务并将其添加到应用程序上下文中?

已编辑: 我已经添加了 2 个服务,似乎我的项目中有更深层次的问题,因为这个演示代码按预期工作。解决后将继续更新。

原题

我正在尝试将异步数据从父组件传递到子组件,我 运行 遇到了一些问题。

该服务持有一个可观察对象,父级订阅它 + 通过 async 管道将其传递给子级的输入。

问题是父级选择更改(仅来自控制器)并且 UI 的 none 已更新。

我可以在订阅函数中触发 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);
    });
  }
}

IMO,如果服务方法 returns 是一个可观察对象,它应该在需要数据的地方解包(订阅)。我会这样做:

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

并且在 child 中:

export class ChildComponent {
    @Input() someInput: Observable;
    // subscribe to someInput in the child
}

所以我创建了一个指令,在更新可观察对象时强制更新视图。

@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 传递给在值更改时跟踪的指令