0

In my application I am using angular, and I am trying to access a div right after the page loads. I have imported Afterviewinit like so:

import { Component, OnInit, ViewChild, AfterViewInit, ElementRef } from '@angular/core';

created a variable:

  @ViewChild('editPreamblesInput') editPreamblesInput: ElementRef;

and then implemented ngAfterViewInit like so:

  ngAfterViewInit(){
    const result = this.editPreamblesInput.nativeElement;
    console.log(result);
  }

my html is like:

  <div id="editPreamblesInput" #editPreamblesInput (mouseenter)="userEditable && isUrl? displayPreamblesLink.show() : null" (mouseleave)="displayPreamblesLink.hide()">
    <input-validation>
      <igx-input-group>
        <input igxInput name="preamblesRef" id="preamblesRef" type="text" formControlName="preamblesRef" [readonly]="!userEditable"
        inputValidationDirective [control]="libraryForm.controls['preamblesRef']"/>
        <label igxLabel for="preamblesRef">Preambles Reference</label>
      </igx-input-group>
    </input-validation>

  <igx-action-strip #displayPreamblesLink [displayDensity]="displayDensity" class="action-strip" [hidden]="true">
      <igx-buttongroup [multiSelection]="true" class="buttons">
          <button igxButton type="button" (click)="editPreamblesRef()">
              <igx-icon>link</igx-icon>
          </button>
      </igx-buttongroup>
  </igx-action-strip>
  </div>

However, when I look at my console I get the error: ERROR TypeError: Cannot read properties of undefined (reading 'nativeElement') at LibrarySetupComponent.ng

and editPreamnlesInput is null.

I'm not sure why this is null and what I am missing?

Edit:

So I've figured out why this is happening. The div is a part of a form which only shows if a boolean called loaded is set to true:

this gets set to true in a method called getData():

  getData() {
    this.libraryService.getAll().subscribe({
      next: (libraries) => {
        this.libraryList = libraries;

        if(this.route.snapshot.params['libraryId']) {
          const library = this.libraryList.find(l => l.id == this.route.snapshot.params['libraryId']);
          this.patchLibrary(library);
        } else {
          this.patchLibrary(this.getNewLibrary());
        }
        this.loaded = true;
      },
      error: (err) => {
        this.alertService.error(`Error getting ${this.alertVariable} data - ${err.message}`);
      }
    });
  }

this is then called in the ngOnInit method. loaded has a default value of false, if I change this to true I can then access that element. So I thought I could do something like:

  complete:() => {
    const test = document.getElementById("editPreamblesInput");
    console.log(test);

however test comes up as null. So I'm thinking I need to find a way so that I tell it to wait until the boolean is true before it tries to access it but not sure how

5
  • You don't have a condition to display editPreamblesInput? A *ngIf somewhere
    – Wandrille
    Commented May 22, 2023 at 13:00
  • Works in isolation: stackblitz.com/edit/angular-frs5au There's something else going on here. Commented May 22, 2023 at 13:09
  • Please see edit, figured out why this is happening but not sure the best way to solve it
    – lross33
    Commented May 22, 2023 at 14:33
  • dont use getElementById in Angular, use ElementRef instead
    – danday74
    Commented May 22, 2023 at 15:39
  • what's the issue with using getElementById in Angular?
    – lross33
    Commented May 22, 2023 at 15:40

3 Answers 3

1

You need to use a template variable to access it.

Inside your template:

<div #div>Hello, world!</div>

Inside your component:

@Component({ ...})
export class MyComponent implements AfterViewInit {
  @ViewChild('div') div?: ElementRef;

  ngAfterViewInit(): void {
    if (this.div) {
      console.log(this.div);
    }
  }
}

Here's a StackBlitz to see it in action:

https://stackblitz.com/edit/angular-l1qyro?file=src%2Fmain.ts

3
  • not sure if you saw my edit but what you have put here also won't work. It is due to the form only showing on the condition that the boolean loaded is set to true, so I will need a different way to get around this
    – lross33
    Commented May 22, 2023 at 14:46
  • I'm sorry. I didn't see your edit. I updated the StackBlitz to address your edit. I used RxJS timers to simulate network delays when you fetch data. While data is being fetched, I update the BehaviourSubject to hide the form -- along with the div that you're trying to access. When the form comes back into view, I use Angular's ChangeDetectorRef to check for changes, then print the ElementRef (div) to the console. Commented May 22, 2023 at 15:21
  • that detectChanges method was exactly what I was missing. Thank you!
    – lross33
    Commented May 22, 2023 at 15:34
0

editPreamblesInput element is available when ngAfterViewInit is called, you can wrap your code in a setTimeout function with a minimal delay. like

ngAfterViewInit() {
  setTimeout(() => {
    const result = this.editPreamblesInput.nativeElement;
    console.log(result);
  }, 0);
}

Additionally

@ViewChild('editPreamblesInput', { static: false }) editPreamblesInput: ElementRef;

{ static: false } option is used when the queried element is inside a structural directive such as *ngIf or *ngFor. This ensures that the element is resolved dynamically during runtime.

hope it's work

1
  • unfortunately this did not work, although if you check my edit, I have figured out why this is happening
    – lross33
    Commented May 22, 2023 at 14:47
0

This is not the best code but it will work:

ngAfterViewInit() {
    const interval = setInterval(() => {
        const result = this.editPreamblesInput.nativeElement;
        if (result != null) {
            console.log(result);
            clearInterval(interval);
        }
    }, 100)
}

This code will keep executing every 100ms until result is not null and not undefined! Not the best code because there's a performance cost to keep executing code but given that the code inside setInterval is fairly lightweight you should be OK.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.