Div id is null when using ngAfterViewInit

huangapple go评论70阅读模式
英文:

Div id is null when using ngAfterViewInit

问题

在我的应用程序中,我正在使用Angular,并尝试在页面加载后立即访问一个div。我已导入AfterViewInit如下所示:

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

创建了一个变量:

@ViewChild('editPreamblesInput') editPreamblesInput: ElementRef;

然后实现了ngAfterViewInit:

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

我的HTML如下:

<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>

然而,当我查看控制台时,我得到以下错误:

ERROR TypeError: Cannot read properties of undefined (reading 'nativeElement')
at LibrarySetupComponent.ng

并且editPreamblesInput是null。我不确定为什么它是null以及我漏掉了什么?

编辑:

所以我弄清楚了为什么会发生这种情况。这个div是表单的一部分,只有在一个名为loaded的布尔值设置为true时才会显示:

<form *ngIf="loaded" [formGroup]="libraryForm" (ngSubmit)="onSubmit()">

这是在一个名为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}`);
    }
  });
}

然后在ngOnInit方法中调用它。loaded的默认值为false,如果我将其更改为true,然后我可以访问该元素。所以我想我可以这样做:

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

然而,test显示为null。所以我在思考是否需要找到一种方法,告诉它在boolean为true之前等待访问它,但不确定如何做。

英文:

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 &#39;@angular/core&#39;;

created a variable:

  @ViewChild(&#39;editPreamblesInput&#39;) editPreamblesInput: ElementRef;

and then implemented ngAfterViewInit like so:

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

my html is like:

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

  &lt;igx-action-strip #displayPreamblesLink [displayDensity]=&quot;displayDensity&quot; class=&quot;action-strip&quot; [hidden]=&quot;true&quot;&gt;
      &lt;igx-buttongroup [multiSelection]=&quot;true&quot; class=&quot;buttons&quot;&gt;
          &lt;button igxButton type=&quot;button&quot; (click)=&quot;editPreamblesRef()&quot;&gt;
              &lt;igx-icon&gt;link&lt;/igx-icon&gt;
          &lt;/button&gt;
      &lt;/igx-buttongroup&gt;
  &lt;/igx-action-strip&gt;
  &lt;/div&gt;

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:

<form *ngIf="loaded" [formGroup]="libraryForm" (ngSubmit)="onSubmit()">

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

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

        if(this.route.snapshot.params[&#39;libraryId&#39;]) {
          const library = this.libraryList.find(l =&gt; l.id == this.route.snapshot.params[&#39;libraryId&#39;]);
          this.patchLibrary(library);
        } else {
          this.patchLibrary(this.getNewLibrary());
        }
        this.loaded = true;
      },
      error: (err) =&gt; {
        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:() =&gt; {
    const test = document.getElementById(&quot;editPreamblesInput&quot;);
    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

答案1

得分: 1

你需要使用模板变量来访问它。

在你的模板中:

&lt;div #div&gt;Hello, world!&lt;/div&gt;

在你的组件中:

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

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

这里有一个 StackBlitz 示例以查看它的运行情况:

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

英文:

You need to use a template variable to access it.

Inside your template:

&lt;div #div&gt;Hello, world!&lt;/div&gt;

Inside your component:

@Component({ ...})
export class MyComponent implements AfterViewInit {
  @ViewChild(&#39;div&#39;) 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

答案2

得分: 0

editPreamblesInput 元素在调用 ngAfterViewInit 时可用,您可以将您的代码包装在一个带有最小延迟的 setTimeout 函数中。就像这样:

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

另外,

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

{ static: false } 选项用于查询的元素位于结构性指令内,例如 ngIfngFor。这确保元素在运行时动态解析。

希望它能够工作。

英文:

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

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

Additionally

@ViewChild(&#39;editPreamblesInput&#39;, { 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

答案3

得分: 0

这不是最佳代码,但它会工作:

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

这段代码将每100毫秒执行一次,直到result不为null和undefined为止!这不是最佳代码,因为保持执行代码会有性能成本,但考虑到`setInterval`内部的代码相对较轻,应该没问题。
英文:

This is not the best code but it will work:

ngAfterViewInit() {
    const interval = setInterval(() =&gt; {
        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.

huangapple
  • 本文由 发表于 2023年5月22日 20:56:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76306429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定