问题
I'm trying to materialize angular code for carousel. The carousel is associated to a dynamic array but when i use *ngFor
with carousel class its not working.
I tried writing the class="carousel-item"
inside for loop but console shows:
Cannot read property 'clientWidth' of undefined.
This code not working:
Template:
<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>
Component:
options = {fullWidth: false};
constructor(private http: HttpClient) {
}
ngOnInit() {
var elems = document.querySelectorAll('.carousel');
var instances = M.Carousel.init(elems, this.options);
}
I'm only getting one image that too which is cropped in half where as if i remove the for loop and write <div class="carousel-item">
one after the other like:
<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>
Doing this, I get the carousel working but it is not binded to a dynamic array which is what i want.
回答1:
Instead of initialising the carousel in OnInit
use the AfterViewInit
lifecycle hook
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);
}
}
In addition change your template like this:
<div class="carousel">
<a class="carousel-item" href="#{{hrefs[i]}}!" *ngFor="let item of items;let i = index">
<img src={{item}}/>
</a>
</div>
*ngFor
on the a-tag will create the structure required by materialize.
Using the index of the current element and a new array called 'hrefs', the href attribute is set individually for each a-tag.
Please checkout this Stackblitz for a working example.
Edit: With API response
The error "Can not read property 'clientWidth' of undefined" appears now (as well as during the initialization of the carousel within OnInit), because at the time the M.Carousel.init (...)
is called the HTML structure is not complete yet. As long as this.items
is undefined, there are no a-tags with class carousel-item
.
To work around the problem, the initialization of the materialize carousel must be delayed until the data is loaded from the server and all a-tags were added to the DOM by *ngFor
.
// removed code because it wasn't the best solution..
// it's still in the Stackblitz as comment
Edit #2
I just realized that there is a better method: instead of setting a timeout on the initialization of the carousel, let the *ngFor
trigger the init after it's done adding the last item to the DOM. To do so, change your html like this:
<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>
In your component you will only need the following now:
// 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"]);
}
}
Adjusted Stackblitz.
来源:https://stackoverflow.com/questions/55667029/why-carousel-of-materialize-css-and-ngfor-not-working-together-dynamicaly-to-ge