简体   繁体   中英

I am not able to access DOM element which I am creating using ngFor loop and I am getting the data from the service. Angular

I am facing an issue related DOM Manipulation in angular. I have defined one array named colors in my component and then I am calling the service method and getting the colors data from the server and then assigning them into the colors array inside subscribe method. In HTML template using *ngFor loop, I am displaying that colors array data. Now I want to add some dynamic class or style to the HTML elements and for that, I am using ElementRef to access this element but I am not able to access it because the colors array is undefined initially. Now let me attach the code...

@Component({
  selector: 'app',
  template: "<div><li class="color" *ngFor="let color of colors">{{color}}</li></div>",
  styleUrls: ['./app.component.css'] 
})

export class AppComponent implements OnInit, AfterViewInit {
   colors:any;

   constructor(private elementRef:ElementRef, private renderer:Renderer2) {}
   
   ngOnInit():void {
     setTimeout(() => {
        this.colors = ['red', 'yellow', 'green'];
     }, 1000);
   }

   ngAfterViewInit():void {
     const color = this.elementRef.nativeElement.querySelectorAll('.color');
     console.log(color);
   }
}

Here I am using the setTimeout function in this code which is behaving like a service because the service takes some time in getting the data from the server but in my code, I am using service.

Now If I am opening console in Developer Tools then I am getting NodeList [] this means I am not able to access DOM Element. I know the issue I am not able to access DOM element because my code for accessing DOM Element is running before the data assigning to the colors array and by default or initially colors array is undefined or empty. That's why I am getting NodeList [] in the console. But I am not able to understand how to solve this problem?.

Thanks in Advance.

You need ngAfterViewCHecked . This will run every time Angular has finished running change detection on a component and it's children

Secondly there is error in template . If you are using all double quotes " that will terminate the string. You need t use single quote and double quotes for all attributes like this

'<div><li class="color" *ngFor="let color of colors">{{color}}</li></div>'

DEMO

This is because ngAfterViewInit calls before the timeout you set in ngOnInit finished.

I would go with ViewChildren approach which is more reliable.
Here's an example:

@Component({
  selector: "my-app",
  template: `
    <ul>
      <li #color *ngFor="let color of (colors$ | async)">{{ color }}</li>
    </ul>
  `,
  styles: []
})
export class AppComponent implements OnDestroy, AfterViewInit {
  @ViewChildren("color") colors: QueryList<ElementRef<HTMLLIElement>>;
  colors$: Observable<string[]> = of(["red", "green", "blue"]).pipe(
    delay(3000)
  );

  private colorsSubscription = Subscription.EMPTY;

  ngAfterViewInit(): void {
    this.colors.changes.subscribe(list => {
      list.forEach(element => {
        console.log(element.nativeElement);
      });
    });
  }

  ngOnDestroy(): void {
    this.colorsSubscription.unsubscribe();
  }
}

What we are doing in this example is:

  • Initializing colors$ to fire a string array after 3 seconds
  • Subscribing to colors$ with AsyncPipe and displaying the colors
  • Setting ViewChildren to query for all elements with #color template reference
  • Subscribing to QueryList changes (additions / deletes etc) and acting accordingly

You can find the complete example in this stackblitz

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM