【问题标题】:view not updating even when observable changes即使可观察到的变化,视图也不会更新
【发布时间】:2017-10-13 08:48:40
【问题描述】:

在我之前的一个问题 (dynamically create md-card from API response) 中,我根据来自 API 的大量响应动态创建卡片。

这是进行 API 调用的服务:

@Injectable()
export class WebSearchService {

  private readonly _results$$ = new BehaviorSubject([]);
  private readonly _isLoading$$ = new BehaviorSubject(false);

  public readonly results$ = this._results$$.asObservable().shareReplay();
  public readonly isLoading$ = this._isLoading$$.asObservable();

  public term: string;
  p: number; // page

  public config = {
    itemsPerPage: 10,
    currentPage: this.p,
    totalItems: 100
  };

  constructor(private http: Http){
  }

  search(term: string, page?: number){

    return this.http.get(`my/api/{term}`).do(response => {
      this._isLoading$$.next(false);
      this.config.totalItems = response.json().estimated_total;
      this.config.currentPage = this.p;
    })
      .map(response => response.json().results as WebResult[]).map(result => {
        this._results$$.next(result);
        this.term = term;
        return result;
      })
    .take(1);
  }

  updatePage(page: number){
    this.p = page;
    return this.search(this.term, page);
  }

}

使用服务结果的组件:

export class ResultsComponent implements OnInit {
  private readonly _results$$ = new BehaviorSubject([]);

  public readonly isLoading$ = this.webService.isLoading$;
  public results$ =  this.webService.results$;
  p = 1; //

  constructor(public webService: WebSearchService) { }

  ngOnInit() { //
  }

  changePage(page: number){
    console.log('Page: ' + page);
    this.results$ = this.webService.updatePage(page);
  }

和模板:

<ng-template [ngIf]="isLoading$ | async" [ngIfElse]="results">
  is loading ...
</ng-template>

<ng-template #results>
  <div class="container-fluid" *ngFor="let result of results | paginate: this.webService.config">
    <!-- cards get dynamically created here -->
  </div>
</ng-template>
<pagination-controls *ngIf="(results$ | async)?.length>0" maxSize="6"
                     previousLabel=""
                     nextLabel=""
                     align="center"
                     (pageChange)="changePage($event)"
                     class="my-pagination"
                     screenReaderPaginationLabel="Pagination"
                     screenReaderPageLabel="page"
                     screenReaderCurrentLabel="You are on page"
                     autoHide="true"
></pagination-controls>

我有另一个组件在服务中调用search 方法。但是,如果在除第 1 页之外的任何页面上调用搜索,则视图不会更新。如果我转到结果视图的第 3 页,然后执行另一次搜索,则 results$ 可观察对象会更新,但视图仍保留在旧搜索结果上。我必须在分页中手动更改到第 1 页才能获得新结果。有没有办法自动刷新视图?

我已尽力解释问题。该项目非常大,因此我无法创建 plunkr,但愿意解释/提供更多代码。

感谢任何帮助。

【问题讨论】:

  • 好吧,老实说......你的代码有很多问题,我什至不会考虑像 NgZone 或 ChangeDetectorRef 这样复杂的东西 - 需要进行大量更正在那些真正成为问题之前......但我真的不明白你想要做什么。例如 - 为什么有这么多 BehaviorSubjects 可以转换为 observables?仅返回 http.get() 映射结果还不够吗?您是否正在尝试实现某种缓存?只是一个提示:您的直接问题出在 ResultsComponent.changePage() 函数中。
  • 您能提供一些建议或改进吗?
  • 当然可以,但我需要一些东西来开始。我看不到您要做什么,因此除非您对此有所了解,否则我将无法提出任何有意义的建议。您正在描述 whathow 您在做什么 - 您创建了与组件 B 对话的组件 A 等.,当我问为什么时,您以这种方式而不是另一种方式创建它们。我想了解您要解决的实际任务。

标签: angular


【解决方案1】:

您的 observable 的 successerror 函数很可能已超出 Angular 区域,这意味着当发出新值时,Angulars 更改检测不会运行。当有问题的值更新时,您可以使用ChangeDetectorRef 手动运行更改检测。

constructor(private cdr: ChangeDetectorRef) {} // inject in your constructor

myObservable.subscribe(value => {
  this.myValue = value; // update out of scope
  this.cdr.detectChanges(); // run change detection manually
});

您也可以尝试手动订阅results$ observable,并在每次有新值到达时更新实例变量。实例变量用于 ngFor 指令。我从您的代码中删除了所有不相关的内容,并添加了一个快速示例来说明我的意思:

你的班级:

export class ResultsComponent implements OnInit {
  private results$ =  this.webService.results$;
  public results = [];

  constructor(private webService: WebSearchService, private cdr: ChangeDetectorRef) {
    this.results$.subscribe(result => {
      this.results = result;
      this.cdr.detectChanges();
    });
  }
}

还有你的模板:

<div *ngFor="let result of results | paginate: this.webService.config"></div>

<pagination-controls 
  *ngIf="results.length > 0" 
  maxSize="6" previousLabel="" 
  nextLabel="" 
  align="center" 
  (pageChange)="changePage($event)"
  class="my-pagination" 
  screenReaderPaginationLabel="Pagination" 
  screenReaderPageLabel="page" 
  screenReaderCurrentLabel="You are on page"
  autoHide="true">
</pagination-controls>

【讨论】:

  • 如果我不触摸分页并停留在第一页,视图会刷新。但是,当我导航到不同的页面,然后继续更改搜索词时,就会出现问题。 results$ 仍会收到更改,但只是没有反映在视图中。我尝试了您的建议,但问题仍然存在。
  • 您是否尝试过手动订阅 results$ observable(不是使用异步管道)并在模板中为 ngFor 使用实例变量,每次 results$ 收到更新时都会更新?跨度>
  • 不,我没试过。你能提供一个使用我上面的代码的例子吗?
  • 我在模板的第 1 行遇到错误,我已更新我的答案以显示完整的模板(以及您建议的更改),因此您可以建议我如何从这里继续。跨度>
  • 对不起,我完全忘了包括错误是什么。 Cannot read property 'instance' of undefined at nodeValue (core.es5.js:10324) at Object.eval [as updateDirectives] (ResultsComponent.html:1) at Object.debugUpdateDirectives [as updateDirectives] (
猜你喜欢
  • 1970-01-01
  • 2019-04-22
  • 2019-03-19
  • 1970-01-01
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 2019-09-29
  • 2015-12-30
相关资源
最近更新 更多