重构代码以避免角度变化检测无限循环

时间:2018-03-26 19:00:12

标签: angular angular5

我的模型在帖子 Img 之间存在一对多的关系,即每个帖子都有一些 Img 。显示此结构的简化方法之一是:

<ul>
   <li *ngFor="let post of posts">
     {{post.postId}} 
      <p  *ngFor="let img of getImgs(post.postId)">{{img.url}}</p>
   </li>
 </ul>

我的问题是由于Angular的变化检测机制,页面进入无限循环。有没有更好的方法来重构此代码? (我正在使用 Angular 5 我的代码的简化版如下:

我的AppComponent看起来像:

import { Component } from '@angular/core';
import {Post} from "./post"
import {PostService} from "./post.service";
import {Observable} from "rxjs/Observable";
import {Img} from "./img";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [PostService]
})
export class AppComponent {
  title = 'app';
  posts : Post[];
  constructor(private postService: PostService) { }
  ngOnInit(): void {
    this.getPosts();
  }

  getPosts(): void {
    this.postService.getPosts().subscribe(posts => this.posts = posts);
  }

  getImgs(postId : string) : Img[] {
    var retImgs : Img[];
    this.postService.getImgsByPostId(postId).subscribe( imgs => retImgs = imgs);
    return retImgs;
  }
}

我从Web服务获得发布 Img 帖子不包含 Img 作为其自身的一部分,但使用 postId Img >。如果我想将此模型保留在 Img 不属于帖子的位置,我发现很难存储 Img AppCompnent。这迫使我将getImgs()调用放入HTML模板中,每次更改检测事件都会调用该模板。 Angular 5处理这种情况的正常方法是什么?

我可以做一些黑客来保存/缓存getImgs()的输出,所以我不需要为后续请求调用Web服务,或者我可以只改变我的模型,但我想知道这是怎么回事通常都会完成。

无论如何都要从模板中调用一个方法,以便不会在每个变更检测机制上调用它?

修改 为了回应@ Floor 建议使用更智能的getImgs()来缓存Map中的图片,我尝试了以下AppComponent( Imports和Component Decorator如上所述)

export class AppComponent {
  title = 'app';
  posts : Post[];
  postIdToImgs : Map<string, Img[]>;  //Added
  constructor(private postService: PostService) {
    this.postIdToImgs = new Map<string, Img[]>();  //Added
  }
  ngOnInit(): void {
    this.getPosts();
  }

  getPosts(): void {
    this.postService.getPosts().subscribe(posts => this.posts = posts);
  }

  getImgs(postId : string) : Img[] {
    var retImgs : Img[];
    console.log(`Call to getImgs(${postId})`);  //Added
    if(this.postIdToImgs.has(postId)) {    //Added
      console.log('Found value in Cache');  //Added
      retImgs = this.postIdToImgs.get(postId);  //Added
      return retImgs; //Added
    }  //Added
    this.postService.getImgsByPostId(postId).subscribe( imgs => {
    this.postIdToImgs.set(postId, imgs); //Added
    retImgs = imgs;
});
    return retImgs;
  }
}

是的,这会在第一次迭代后停止对后端的调用,但我仍然得到无限序列

  

Cal to getImgs(#postId)
  在Cache中找到值

其中#postId是此页面上的10个帖子之一。我正在努力学习Angular,所以我不是想让它发挥作用。我试图找出:

  

有没有办法让模板中的方法/功能在每次更改检测时都没有调用?

2 个答案:

答案 0 :(得分:2)

解决此问题的最佳方法是将两个对象放入包装器中。 使用帖子及其所属图像填充此包装器。 然后执行ngFor使用这个包装器。

e.g。

<ul>
   <li *ngFor="let wrapper of myWrapperList">
     {{wrapper?.post?.postId}} 
      <p  *ngFor="let img of wrapper.images">{{img?.url}}</p>
   </li>
 </ul>

在你的模板中

$project

答案 1 :(得分:2)

您可以将代码移动到管道,每次发生更改检测时都不会调用该管道

https://angular.io/guide/pipes#pure-pipes

@Pipe({
  name: 'getImages',

})
export class GetImagesPipe  implements PipeTransform {
   postIdToImgs : Map<string, Img[]> =  new Map<string, Img[]>();

  constructor(private postService: PostService) { }

  transform(postId: string): any {


       var retImgs : Img[];
    console.log(`IN PIPE: Call to getImgs(${postId})`); 

    /*This commented cache implementation is not needed as the pipe won't be called again if the post does not change

      if(this.postIdToImgs.has(postId)) {   
      console.log('IN PIPE: Found value in Cache'); 
      retImgs = this.postIdToImgs.get(postId);  
      return retImgs; 
    } */ 
    this.postService.getImgsByPostId(postId).subscribe( imgs => {
    this.postIdToImgs.set(postId, imgs); //Added
    retImgs = imgs;
});
    return retImgs;
  }


}

我创建了一个stackblitz https://stackblitz.com/edit/angular-9n9h18?file=app%2Fimg.pipe.ts