skip to Main Content

I can’t get the "box-message" class using "document.querySelectorAll(‘.box-message’)" inside a tree where the "*ngFor" directive is used.

I am making an "http rest" request, after the request the "*ngFor" directive completes the building of the house and after that I am trying to get the "box-message" class using "document.querySelectorAll(‘.box-message’)".

But as I understand it (but I could be wrong), the house does not have time to be built, and that is why I do not receive the “box-message” class.

I tried using AfterViewInit but it doesn’t help.

How to solve this problem?

HTML

    <ul class="ul-ads">
      <li class="li-ads" *ngFor="let ads of ads">
        <div class="box-message">{{ads.message}}</div>
      </li>
    </ul>

SCRIPT

        export class AdsComponent implements OnInit {


          ads!: Ads[];



          public getAds(): Observable<any> {

            return this.http.get<any>(`http://localhost:8080/apis/data/ads`,
              {observe: 'response'});

          }


          ngOnInit(): void {


            this.getAds().subscribe({

              next: data => {

                this.ads = responseData.body.content;

                const boxMessage = document.querySelectorAll('.box-message');
                console.log(boxMessage) // NodeList[]

              }


            });


          }


        }

enter image description here

2

Answers


  1. You can leverage ViewChildren to accomplish this.

    In your template. add a reference for each box message:

    <ul class="ul-ads">
      <li class="li-ads" *ngFor="let ads of ads">
         <div #boxMessage class="box-message">{{ads.message}}</div>
      </li>
    </ul>
    

    In your component, add a property to hold the view children and subscribe to the changes observable:

    @ViewChildren('boxMessage')
    boxMessages: QueryList<ElementRef>;
    
    ngAfterViewInit(): void {
      this.boxMessages.changes.subscribe(boxMessage => {
        // your code here
      });
    }
    

    Any time the view children are changed, your code will execute after the children are rendered.

    There’s no need to use setTimeout to introduce an artificial delay.

    Hope that helps you out.

    Login or Signup to reply.
  2. You need "wait" Angular repaint. Generally You use a setTimeout without miliseconds.

    ...
    next: data => {
        this.ads = responseData.body.content;
        setTimeout(()=>{
           const boxMessage = document.querySelectorAll('.box-message');
           console.log(boxMessage) // NodeList[]
           console.log(this.ads.message) // to print data 
        })
      }
    

    Think that angular execute the instructions under subscribe and then repaint. It’s the reason you not get the elements.

    IMPORTANT: In Angular rarely we need use the old javascript way document.getElementBy. You can declare ViewChildren using a template reference variable

       <ul class="ul-ads">
          <li class="li-ads" *ngFor="let ads of ads">
            <!--see the #item-->
            <div #item class="box-message">{{ads.message}}</div>
          </li>
       </ul>
    

    Then you can get all of them in a QueryList

    @ViewChildren('item') items!:QueryList<ElementRef>
    

    See that in an elementRef, the nativeElement property is the html tag

    A Query list allow convert to Array, reorder, get the first and last element, subscribe to changes…

    ngAfterViewInit()
    {
         this.items.changes.subscribe(_=>{
             console.log(this.items.first.nativeElement)
             console.log(this.items.last.nativeElement)
             //the third element
             const htmlElement=this.items.find((_,index)=>index==2)?.nativeElement
             ...
    
         })
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search