为什么Carousel实现CSS和* ngFor无法动态地协同工作以获取图像

时间:2019-04-13 15:25:29

标签: javascript angular typescript angular-material materialize

我正在尝试实现轮播的角度代码。轮播与动态数组相关联,但是当我将*ngFor与轮播类一起使用时,它将无法正常工作。

我尝试在循环中编写class="carousel-item",但控制台显示:

  

无法读取未定义的属性“ clientWidth”。

此代码不起作用:

模板:

<div class="carousel carousel-slider">
    <div class="carousel-item" href="#one!">
       <div *ngFor ="let item of items;let i = index">
           <img src={{items[i]}}>
        </div>
    </div>
</div>

组件:

options = {fullWidth: false};

constructor(private http: HttpClient) {
}

ngOnInit() {
    var elems = document.querySelectorAll('.carousel');
    var instances = M.Carousel.init(elems, this.options);
}

我也只得到一张图像,该图像也被裁剪了一半,好像我删除了for循环并一个接一个写<div class="carousel-item">一样,

<a class="carousel-item" href="#one!"><img src={{items[0]}}></a>
<a class="carousel-item" href="#two!"><img src={{items[1]}}></a>
<a class="carousel-item" href="#three!"><img src={{items[2]}}></a>

这样做,轮播工作了,但是它没有绑定到我想要的动态数组上。

1 个答案:

答案 0 :(得分:2)

使用OnInit生命周期挂钩代替在AfterViewInit中初始化轮播

import { Component, OnInit, AfterViewInit } from '@angular/core';
// other imports

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewInit {
  // ...

  constructor(private http: HttpClient) {}

  ngOnInit() {}

  ngAfterViewInit() {
    var elems = document.querySelectorAll('.carousel');
    var instances = M.Carousel.init(elems, this.options);
  }
}

此外,请像这样更改模板:

<div class="carousel">
    <a class="carousel-item" href="#{{hrefs[i]}}!" *ngFor="let item of items;let i = index">
        <img src={{item}}/>
    </a>
</div>
A标签上的

*ngFor将创建物化所需的结构。

使用当前元素的索引和一个名为“ hrefs”的新数组,为每个a-tag分别设置href属性。

请查看此Stackblitz以获取工作示例。

编辑:带有API响应

现在(以及在OnInit中轮播初始化期间)出现错误“无法读取未定义的属性'clientWidth',未定义”,因为在调用M.Carousel.init (...)时HTML结构不完整然而。只要未定义this.items,就不会有carousel-item类的a-tag。

要变通解决此问题,必须延迟实现转盘的初始化,直到从服务器加载数据并且所有*ngFor将所有a-tag添加到DOM为止。

// removed code because it wasn't the best solution..
// it's still in the Stackblitz as comment

编辑#2

我刚刚意识到有一个更好的方法:不要在轮播的初始化上设置超时,而是在将最后一项添加到DOM之后,让*ngFor触发初始化。为此,请像这样更改您的html:

<div class="carousel">
    <a class="carousel-item" href="#{{hrefs[i]}}!" *ngFor="let item of items;let i = index; let last = last">
        <img src={{item}}/>
    <ng-container *ngIf="last">{{initCarousel()}}</ng-container>
  </a>
</div>

在您的组件中,现在只需要以下内容:

// imports
// ...

export class AppComponent implements OnInit {
  // ...

  ngOnInit() {
    // dummy api request
    this.fakeApiRequest().pipe(delay(2000)).subscribe((res) => {
      // parse api response
      this.items = res;
    });
  }

  initCarousel() {
    // timeout needed, otherwise navigation won't work.
    setTimeout(() => {
       let elems = document.querySelectorAll('.carousel');
       let instances = M.Carousel.init(elems, this.options);
    }, 100);
  }

  fakeApiRequest(): Observable<string[]> {
    return of(["https://picsum.photos/200/300?image=0", "https://picsum.photos/200/300?image=1", "https://picsum.photos/200/300?image=2", "https://picsum.photos/200/300?image=3", "https://picsum.photos/200/300?image=4"]);
  }
}

调整后的Stackblitz