【问题标题】:Sending async observable data from parent to child component将异步可观察数据从父组件发送到子组件
【发布时间】:2018-11-30 19:45:34
【问题描述】:

我正在尝试将异步数据(可观察数据)从父组件传递到子组件,但似乎子组件上的更改并未更新,我不确定为什么。

我有一个(简化的)服务正在使用BehaviourSubject

@Injectable()
export class MyService {

  private _items: BehaviorSubject<any[]> = new BehaviorSubject([]);
  public items: Observable<any[]> = this._items.asObservable();

  list() {
    this.http.get('...').subscribe(items => {
      this._items.next(items);
    })
  }
}

list() 函数会定期从我的应用程序的其他位置调用以更新数据。)

然后我有一个父组件:

@Component({
  selector: 'parent',
  templateUrl: '<child [items]="myService.items | async"></child>',
})
export class Parent {

  constructor(public myService: MyService) {
  }
}

和一个孩子:

@Component({
  selector: 'child',
  templateUrl: '<ul><li *ngFor="item of _items">{{item.name}}</li></ul>',
})
export class Parent implements OnChanges {

  @Input() items;
  _items = [];

  constructor() {
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('changes', changes);
    this._items = changes.items;
  }
}

我的理解是,使用 async 会自动订阅/取消订阅我的 Observable 并将任何新数据传递给我的子组件。当新数据进来时,我的子组件应该会检测到ngOnChanges 中的这些变化并打印出来。

  1. 我的控制台输出从未触发,这表明在子节点中未检测到任何更改。我已经手动订阅了父组件中的 Observable 以验证新数据 正在 进来,所以我不确定我在哪里出错了。
  2. 在组件级别处理异步数据是否正确?

【问题讨论】:

  • 你为什么不使用主题
  • 这似乎是我的设置特有的错误,因为我已经能够创建一个它应该如何工作的示例stackblitz.com/edit/…(并且没有问题)
  • 所以这个问题的核心似乎与上面的代码无关(虽然它可以通过下面的一些建议进行改进,但应该是功能性的)。在我的应用程序中,我有一个 Google 地图事件,它从我的服务中触发了 list() 函数,这似乎是真正的潜在问题。我想这与在 Angular 运行循环(或类似的东西)之外调用的函数有关
  • @TimmyO'Mahony 相关链接已损坏

标签: angular


【解决方案1】:

我可以建议您对代码进行一些更改:

家长:

@Component({
  selector: 'app-parent',
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: '<app-child [items]="(myService.items | async)"></app-child>',
})
export class Parent {

  constructor(public myService: MyService) {
  }
}

- 在 [items] 赋值中添加括号

孩子:

@Component({
  selector: 'app-child',
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: '<ul><li *ngFor="item of _items">{{item.name}}</li></ul>',
})
export class Child implements OnChanges {

  @Input() items;
  _items = [];

  constructor() {
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('changes', changes);
    console.log(this.items);
    this._items = changes.items;
  }
}
  • 更改类名
  • 添加了 changeDetection 策略
  • 在你的选择器前加上 app 前缀也是一个好习惯

【讨论】:

    【解决方案2】:

    你快到了:-)。我只有一些提示...

    @Injectable()
    export class MyService {
    
      // Post-fixing your observable variables with `$` will make it easy to see them at the first glance.
      private _items$: BehaviorSubject<any[]> = new BehaviorSubject([]);
      get items$(): Observable<any[]> {
        // BehaviorSubject is an Observable as well
        return this._items$;
        // Or if you want to be safe and not leak your abstraction, use what you did...
        return this._items$.asObservable();
      }
    
      list() {
        // BehaviorSubject can subscribe itself
        this.http.get('...').subscribe(this._items$);
      }
    }
    

    我会将 observable 存储在您的父组件中的一个字段中。如果您在组件的其他任何地方都不需要您的服务,为什么要保留参考。

    @Component({
      selector: 'parent',
      templateUrl: '<child [items]="items$ | async"></child>',
    })
    export class ParentComponent {
      items$: Observable<any[]>;
      constructor(myService: MyService) {
        this.items$ = myService.items$;
      }
    }
    

    在这种情况下,您不需要ngOnChanges,因为您可以将输入输入到属性中。如果您不需要日志记录,则可以完全放弃访问器。

    @Component({
      selector: 'child',
      templateUrl: '<ul><li *ngFor="item of items">{{item.name}}</li></ul>',
    })
    export class ChildComponent{
    
      private _items = [];
    
      @Input() set items(_items) {
        console.log('items', _items);
        this._items = _items;
      }
      get items() {
        return this._items;
      }
    }
    

    您的异步数据处理方法很好。在我看来,关注分离到'smart' and 'dumb' components 是要走的路。

    希望这会有所帮助:-)

    【讨论】:

      猜你喜欢
      • 2017-09-13
      • 2015-08-11
      • 2020-09-28
      • 2019-09-07
      • 2018-07-07
      • 2022-08-02
      • 2016-03-18
      相关资源
      最近更新 更多