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

Angular 重新构造代码以避免角度变化检测无限循环,angular,angular5,Angular,Angular5,我的模型在Post和Imgs之间有一对多的关系,即每个Post都有一些Imgs。显示此结构的简化方法之一是: <ul> <li *ngFor="let post of posts"> {{post.postId}} <p *ngFor="let img of getImgs(post.postId)">{{img.url}}</p> </li> </ul> 我从Web服务中获得Pos

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

<ul>
   <li *ngFor="let post of posts">
     {{post.postId}} 
      <p  *ngFor="let img of getImgs(post.postId)">{{img.url}}</p>
   </li>
 </ul>
我从Web服务中获得Posts和Imgs。Posts不包含作为其一部分的Imgs,但是Imgs是使用postId检索的。如果我想保留Imgs不属于Posts的模型,我发现很难将Imgs存储在
appcomponent
中。这迫使我将
getImgs()
调用放在HTML模板中,每个更改检测事件都会调用该模板。Angular 5中处理此场景的正常方式是什么

当然,我可以做一些修改来保存/缓存
getImgs()
的输出,因此我不需要为后续请求调用Web服务,或者我可以更改我的模型,但我想知道这通常是如何做到的

是否有必要从模板中调用一个方法,这样就不会在每个更改检测机制中调用它


编辑 为了响应@Floor的建议,使用更智能的
getImgs()
将图像缓存在
地图中,我尝试了以下
AppComponent
:(导入和组件装饰器如上所述)

导出类AppComponent{
标题=‘应用程序’;
职位:职位[];
postdtoimgs:Map;//已添加
建造商(私人邮政服务:邮政服务){
this.positdToimgs=new Map();//已添加
}
ngOnInit():void{
这是getPosts();
}
getPosts():void{
this.postService.getPosts().subscribe(posts=>this.posts=posts);
}
getImgs(postId:string):Img[]{
var retImgs:Img[];
log(`calltogetimgs(${postId})`);//添加
如果(this.postdtoimgs.has(postId)){//添加了
console.log('在缓存中找到值');//已添加
retImgs=this.postdtoimgs.get(postId);//已添加
return retImgs;//已添加
}//增加
this.postService.getImgsByPostId(postId).subscribe(imgs=>{
this.postedtoimgs.set(postId,imgs);//已添加
retImgs=imgs;
});
返回重定时;
}
}
是的,这会在第一次迭代后停止对后端的调用,但我仍然会得到一个无限的调用序列

Cal to getImgs(#posted)
在缓存中找到值

其中#posted是我在本页上的10篇帖子之一。我在努力学习角度,所以我不只是想让它发挥作用。我想知道:

是否有一种方法可以使模板中的方法/函数在每次更改检测时都不被调用?


解决此问题的最佳方法是将两个对象都放入包装器中。 用帖子和它所属的图片填充这个包装。 然后执行使用此包装器的步骤

e、 g

从'@angular/core'导入{Component};
从“/Post”导入{Post}
从“/post.service”导入{PostService}”;
从“rxjs/Observable”导入{Observable};
从“/Img”导入{Img};
@组成部分({
选择器:'应用程序根',
templateUrl:“./app.component.html”,
样式URL:['./app.component.css'],
提供者:[邮政服务]
})
导出类AppComponent{
标题=‘应用程序’;
myWrapperList:Array=[];
构造器(私人邮政服务:邮政服务){}
ngOnInit():void{
this.init();
}
init():void{
this.postService.getPosts().subscribe(posts=>
posts.forEach(post=>{
this.postService.getImgsByPostId(post.postId).subscribe(imgs=>
让myWrapper=newmywrapper(post,imgs);
this.myWrapperList.push(myWrapper);
});
);
}
}
导出类MyWrapper{
建造师(
公共职务:职务,,
公共图像:阵列
){}
}
在你的模板中

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


解决此问题的最佳方法是将两个对象都放入包装器中。 用帖子和它所属的图片填充这个包装。 然后执行使用此包装器的步骤

e、 g

从'@angular/core'导入{Component};
从“/Post”导入{Post}
从“/post.service”导入{PostService}”;
从“rxjs/Observable”导入{Observable};
从“/Img”导入{Img};
@组成部分({
选择器:'应用程序根',
templateUrl:“./app.component.html”,
样式URL:['./app.component.css'],
提供者:[邮政服务]
})
导出类AppComponent{
标题=‘应用程序’;
myWrapperList:Array=[];
构造器(私人邮政服务:邮政服务){}
ngOnInit():void{
this.init();
}
init():void{
this.postService.getPosts().subscribe(posts=>
posts.forEach(post=>{
this.postService.getImgsByPostId(post.postId).subscribe(imgs=>
让myWrapper=newmywrapper(post,imgs);
this.myWrapperList.push(myWrapper);
});
);
}
}
导出类MyWrapper{
建造师(
公共职务:职务,,
公共图像:阵列
){}
}
在你的模板中

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


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

@管道({
名称:'getImages',
})
导出类GetImagesPipe实现PipeTransform{
postdtoimgs:Map=newmap();
构造器(私人邮政服务:邮政服务){}
转换(postId:string):任意{
var retImgs:Img[];
log(`IN-PIPE:calltogetimgs(${postId})`);
/*作为pip,不需要此注释缓存实现
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';
  myWrapperList: Array<MyWrapper> = []; 

  constructor(private postService: PostService) { }
  ngOnInit(): void {
    this.init();
  }

  init(): void {

    this.postService.getPosts().subscribe( posts => 

       posts.forEach(post => {
           this.postService.getImgsByPostId(post.postId).subscribe(imgs => 
             let myWrapper = new MyWrapper(post, imgs);
             this.myWrapperList.push(myWrapper);
       });

    );
  }

}


export class MyWrapper {
    constructor(
      public post: Post,
      public images: Array<Images>
    ){}
}
<ul>
   <li *ngFor="let wrapper of myWrapperList">
     {{wrapper?.post?.postId}} 
      <p  *ngFor="let img of wrapper.images">{{img?.url}}</p>
   </li>
 </ul>
@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;
  }


}