在 ngAfterViewInit 生命周期中,dom 的钩子图像被完全加载了吗?

Posted

技术标签:

【中文标题】在 ngAfterViewInit 生命周期中,dom 的钩子图像被完全加载了吗?【英文标题】:In ngAfterViewInit life cycle hook images of the dom gets loaded completely? 【发布时间】:2021-01-17 13:38:55 【问题描述】:

所以在我当前的项目中,我正在尝试创建一个产品滑块。为了正常工作,我需要获取所有产品的宽度,以便计算总宽度。目前我正在做的是

 ngAfterViewInit(): void 
    this.service.getData().subscribe((items) => 
      for (let key in items) 
        this.items.push(items[key]);
      
      this.cd.detectChanges();
      this.cards = this.elem.nativeElement.querySelectorAll('.card');
      console.log(this.cards); // In this log i am getting all the cards have the correct width
      this.cards.forEach((card) => 
        console.log(card.getBoundingClientRect());//But in this log widths are different
        this.totalWidth += card.clientWidth;
      );
      console.log(this.totalWidth);
    );
  

在调用我的后端并将其设置为本地变量后的 ngAfterViewInit 中,我正在手动运行更改检测。之后,如果我进行控制台日志记录,我将获取节点列表,其中所有项目的宽度都正确。但如果我尝试遍历节点列表为了获得总宽度,我得到了不同的值。在每张卡片中,我当前显示一个图像和一个文本。我没有将卡片的宽度设置为固定值,我将图像的高度设置为固定值应用宽度对应于那个高度,所以图像会响应。所以我在节点列表上的循环给了我错误的宽度值,直到图像是从内存缓存中加载的。所以我的猜测是加载图像需要一定的时间,我的卡的宽度取决于图像宽度 这就是为什么如果我尝试循环遍历节点列表会得到错误的宽度值。但是为什么节点列表中的宽度值是正确的以及如果我尝试循环遍历它们为什么它会改变?我应该如何解决这个问题以获得总宽度 b只要我的组件在不使用setTimeOut() 的情况下加载,就会根据每个卡片的宽度进行调整。

这是演示 stackblitz 代码 - https://stackblitz.com/edit/angular-ivy-czs5mo?file=src/app/app.component.ts

【问题讨论】:

为什么调用console.log(card.getBoundingClientRect()) 却用clientWidth 测量总宽度?这些通常是不同的值 即使我用 offsetwidth 测量值是不同的..但是当图像加载到内存缓存中或稍后在任何函数中我调用相同的步骤时,两个值都是相同的..我试图得到通过测量每张卡片的宽度得到总宽度。 所以你是说你需要知道如何等到图像加载后检查? 首先我想知道这种行为是否发生是因为图像需要时间加载,因为节点列表中的每个卡片宽度都有不同的宽度(console.log(this.cards))和下一个如果我尝试遍历节点列表并访问每张卡片的宽度是完全不同的值。以及如何在加载组件并渲染图像后立即测量总宽度。我不想使用 setTimeOut(),因为在获得正确的宽度值之前,我们永远不会知道需要多少时间。跨度> 【参考方案1】:

您需要检查,在您的图像中事件(load)。有些人喜欢

<img src="https://picsum.photos/200/300" (load)="onLoad()"/>

count:number=0;
yet:boolean=false;
onLoad()

   count++;
   this.checkWidth()


ngAfterViewInit()

    this.yet=true;
    this.checkWidth()

this.checkWidth()

   if (this.yet && count==imagesLength)
   
       ..do something..
   

注意:改为使用

//I don't like so much
this.elem.nativeElement.querySelectorAll('.card');

您可以在 .html 中使用 referenceVariable #card 并使用 ViewChildren 控制元素

@ViewChildren('card') cards:QueryList<ElementRef>

并使用

this.cards.forEach(x=>
   const rect=x.nativeElement.getBoundingClientRect()
   console.log(rect)
   ...
)

【讨论】:

以上是关于在 ngAfterViewInit 生命周期中,dom 的钩子图像被完全加载了吗?的主要内容,如果未能解决你的问题,请参考以下文章

ngAfterViewInit() 和 ngAfterViewChecked() 有啥区别?

离子中的组件生命周期

如何在角度生命周期钩子中使用等待?

Angular 2 动态组件加载 ngOnChanges 生命周期钩子调用

Fragment和Activity生命周期以及横竖屏切换对生命周期的影响

Cocos2d-x多场景切换生命周期