skip to Main Content

I have a list of items displayed in HTML from a Dynamic array as async. When I try to add a new item to the array, I want to select and get that item as an HTML element from DOM. How can I do that? When I try to get the item with a specific id, the DOM is not rendered with the item yet. I used ViewChild, but it returns undefined.

For example:

I have an array of vehicles as Dynamic Observable. I am trying to add a new item and get that item as an html element using the id. How can I do that?

  @ViewChild('span') span: HTMLElement;
  vehicles:Observable<any[]> = [
    {'name':'test1', 'id': 1},
    {'name':'test2', 'id': 2},
    {'name':'test3', 'id': 3},
  ]
  pushData() {
    this.vehicles
         .pipe(
         tap(() => vehicles.push({
      name: 'test10',
      id: 4
    })

    const element:HTMLElement = document.getElementById(4);
    console.log(element)
    
    // console.log(this.span.id===4)
  }
    <div *ngFor='let vehicle of vehicles | async'>
      <span #span id="{{vehicle.id}}">{{vehicle.name}}<br></span>       
    </div>
    <button (click)="pushData()">Push Data</button>

2

Answers


  1. As per comment that you need the HTML element which you newly added, for that you need wait for sometime(use setTimeout) so that element is reflected to DOM.

    pushData() {
        this.vehicles
             .pipe(
             tap(() => vehicles.push({
          name: 'test10',
          id: 4
        })
    
      setTimeout(() => {
        const element:HTMLElement = document.getElementById("4");
        console.log(element)
      }, 50);
        
        // console.log(this.span.id===4)
      }
    

    Here is the code for the to get the elements based on the ViewChildren changes.

    Updated TS:

        @ViewChildren('vehicleElement') vehicleItems!: QueryList<HTMLElement>;
        
          vehicles = [
            { name: 'test1', id: 1 },
            { name: 'test2', id: 2 },
            { name: 'test3', id: 3 },
          ];
        
          ngAfterViewInit() {
            this.vehicleItems?.changes.subscribe((elements: QueryList<HTMLElement>) => {
              console.log(elements); // all elements
              console.log(elements.last); // last elements
            });
          }
    
      push() {
        this.vehicles.push({
          name: 'test10',
          id: 4,
        });
        // console.log(document.getElementById("4"))
      }
    

    HTML Template:

    <section class="row">
        <div class="col-md-1"*ngFor='let vehicle of vehicles' >
        <span #vehicleElement id="{{vehicle.id}}">{{vehicle.name}}<br></span>       
        </div>
    </section>
    
    <button (click)="push()">Push data</button> 
    
    Login or Signup to reply.
  2. I feel you’re looking for BehaviorSubject

      vehicles:BehaviorSubject<any[]> = new BehaviorSubject([
        {'name':'test1', 'id': 1},
        {'name':'test2', 'id': 2},
        {'name':'test3', 'id': 3},
      ])
      pushData() {
        this.vehicles.next([...this.vehicles.value,{
          name: 'test10',
          id: 4
        }])
        setTimeout(()=>{
          const element:HTMLElement |null= document.getElementById("4");
          console.log(element)
      
        })
      }
    

    The setTimeout it’s necessary because you need "wait" Angular "repaint" the divs before check it.

    If you don’t like so much the setTimeout, You can also use ChangeDetectorRef

    //inject in constructor de ChangeDetecteorRef
    constructor(private cdr:ChangeDetectorRef){}
    
    pushData() {
        this.vehicles.next([...this.vehicles.value,{
          name: 'test10',
          id: 4
        }])
        this.cdr.detectChanges(); //<--force Angular see changes
        const element:HTMLElement |null= document.getElementById("4");
        console.log(element)
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search