1

I am trying to convert a javascript version of the following slideshow to be used in Typescript language, but I ran into an issue:

I get an error that says:

Cannot read property 'style' of undefined

It looks like it does not accept slides[this.selectedindex - 1].style.display in typescript. So I added (<HtmlElement>...) but it does not work.

I have implemented the following code:

home.component.ts

import { Component, OnInit, ViewChild } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { NgbCarousel, NgbSlideEvent, NgbSlideEventSource } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],

})
export class HomeComponent implements OnInit {
  title: string;
  description: string;
  role: Number;
  public selectedindex: number = 0;
  public images = ['../../assets/images/healthimage1.png', '../../assets/images/healthimage2.jpg', '../../assets/images/healthimage3.jpg'];
  
  selectImage(index: number) {
    console.log("Index: " + index);
    this.selectedindex = index;
    console.log("Selected Index: " + this.selectedindex);
  }

  showSlides() {
    let i;
    let slides = document.getElementsByClassName("mySlides");
    let dots = document.getElementsByClassName("dot");
    for (i = 0; i < slides.length; i++) {
      (<HTMLElement>slides[i]).style.display = "none";
    }
    this.selectedindex++;
    if (this.selectedindex > slides.length) { this.selectedindex = 1 }
    for (i = 0; i < dots.length; i++) {
      dots[i].className = dots[i].className.replace(" active", "");
    }
    (<HTMLElement>slides[this.selectedindex - 1]).style.display = "block";
    dots[this.selectedindex - 1].className += " active";
    setTimeout(this.showSlides, 2000);
  }


  constructor(
    private activatedRoute: ActivatedRoute,
    private router: Router) {
    this.role = 1;

  }

  ngOnInit() {
      this.showSlides();
  }

}

home.component.html

<div *ngIf="images">
  <div class="mySlides slideshow-container">
      <img *ngFor="let image of images; let i=index" 
          [src]="image" 
          [ngClass]="{'image-active': selectedindex == i}">  

      <div style="text-align:center; display:inline-block;"  *ngFor="let dot of images; let i=index">
          <span class="dot" 
              (click)="selectImage(i)"
              [ngClass]="{'active': selectedindex == i}">
          </span>
      </div>
  </div>
</div>

css:

.slideshow-container {
  max-width: 1200px;
  position: relative;
  margin: auto;
  text-align:center;
}

.slideshow-container img{
  display: none;
}

.slideshow-container img.image-active {
    display: block;
    width: 100%;
}

/* The dots/indicators */
.dot {
  cursor: pointer;
  height: 15px;
  width: 15px;
  margin: 0 2px;
  background-color: #bbb;
  border-radius: 50%;
  display: inline-block;
  transition: background-color 0.6s ease;
}

.active, .dot:hover {
  background-color: #717171;
}

4 Answers 4

2

working demo in this stackblitz link

You need to reference html element in angular in order to dom manipulation in angular.

html template is

<ng-container>
<h2>Automatic Slideshow</h2>
<p>Change image every 2 seconds:</p>

<div class="slideshow-container">

    <div #slides class="mySlides fade">
        <div class="numbertext">1 / 3</div>
        <img src="https://images.pexels.com/photos/6027869/pexels-photo-6027869.jpeg" style="width:100%">
        <div class="text">Caption Text</div>
    </div>

    <div #slides class="mySlides fade">
        <div class="numbertext">2 / 3</div>
        <img src="https://images.pexels.com/photos/3889926/pexels-photo-3889926.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" style="width:100%">
        <div class="text">Caption Two</div>
    </div>

    <div #slides class="mySlides fade">
        <div class="numbertext">3 / 3</div>
        <img src="https://images.pexels.com/photos/6027869/pexels-photo-6027869.jpeg" style="width:100%">
        <div class="text">Caption Three</div>
    </div>

</div>
<br>

<div style="text-align:center">
    <span #dot class="dot"></span>
    <span #dot class="dot"></span>
    <span #dot class="dot"></span>
</div>
</ng-container>

above #slides and #dots are template reference variable. We are using @ViewChildren() to get more than one slides and dots element reference from component.ts file

component.ts file

slidesIndex = 0;
@ViewChildren("slides") slides: QueryList<ElementRef>;
@ViewChildren("dot") dots: QueryList<ElementRef>;
slider$;

ngAfterViewInit() {
  this.showSlides();
}

showSlides() {
  this.slides.forEach(
   (slidesDiv: ElementRef) =>
     (slidesDiv.nativeElement.style.display = "none")
   );
this.slidesIndex += 1;

if (this.slidesIndex > this.slides.length) {
  this.slidesIndex = 1;
}
this.dots.forEach(
  dotsDiv =>
    (dotsDiv.nativeElement.className = dotsDiv.nativeElement.className.replace(
      " active",
      ""
    ))
);
this.slides.toArray()[this.slidesIndex - 1].nativeElement.style.display =
  "block";
this.dots.toArray()[this.slidesIndex - 1].nativeElement.className +=
  " active";
setTimeout(() => {
  this.showSlides();
}, 2000); // Change image every 2 seconds
}
Sign up to request clarification or add additional context in comments.

3 Comments

Wow this works great, the only catch is that some of this code I have never seen before. You removed implements OnInit and used ngAfterViewInit instead of ngOnInit. I noticed that it threw me an error for the forEach loop function with ngOnInit. Not really sure if slider$; does anything.
We are using @ViewChildren().. To get reference of this is only available in ngAfterViewOnit(). This event fires only after component view is rendered by angular and is available inside component file. In ngOnOnit() we can not get any view elements inside component.
If you have any further queries, feel free to connect to me on email : [email protected] .
0

Did you try with viewChild and not document.getElementByClassName.

So, give and id in the html to your slide section.

 <div class="mySlides slideshow-container" id="idSlides">

In your angular component, you define slides as

@ViewChild('idSlides', { static: false })
    slides: Slides;

I think, the value of slides shouldn't be null or undefined.

Document.getElementById is used for Javascript and not angular

Comments

0

After migrating from Angular 12 to 13, I had similar error cos we were using custom-webpack in angular.json. Removing and using default build configuration helped.

"builder": "@angular-builders/custom-webpack:browser", to

"builder": "@angular-devkit/build-angular:browser",

for example

Comments

0

Also I got this error when I used PrimeNG frozen dataTable controller with resizableColumns="true" as following

<p-dataTable resizableColumns="true" frozenWidth="700px" unfrozenWidth="700px" scrollable="true" scrollHeight="500px">
<p-column frozen="true" [style]="{'width':'100px'}" field="Name" header="Name"></p-column>

   

and I fixed it by removing resizableColumns="true"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.