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

时间:2018-03-11 16:27:44

标签: angular google-chrome-extension rxjs observable angular-template

第二次编辑:

原来如此!我已经找到了问题,但为了理解这个问题,你必须先了解目标:

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

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

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

    所以我有一个新问题:

    是否有可能告诉Angular“添加/观察”未实例化的Angular服务并将其添加到应用程序上下文中?

    编辑: 我已经添加了2个服务,看起来我的项目中存在更深层次的问题,因为这个演示代码按预期工作..将在解决时继续更新。

    原始问题

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

    该服务拥有一个可观察的&父订阅它+通过async管道传递给孩子的输入。

    问题是父母选择了改变(仅来自控制器)&没有任何UI更新。

    我可以在订阅函数中触发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 个答案:

答案 0 :(得分:1)

所以我创建了一个指令,在更新observable时强制进行视图更新。

@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管道&amp;相反,我将实际值传递给我绑定它的位置。
  2. 我将Observable传递给指令,该指令会在值更改时跟踪

答案 1 :(得分:0)

IMO,如果服务方法返回一个observable,它应该在需要数据的地方解包(订阅)。我会这样做:

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

在孩子身上:

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