@Input() 被定义为 undefined,即使在屏幕上 @Input() 数据可见

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

@Input() as undefined, even with @Input() data visible on the screen

问题

我有一个使用Angular 12制作的大型应用程序,在其中的一部分中有一个父组件和一个子组件。

父组件(HTML):

<!-- 代码省略 -->

父组件(TypeScript):

// 代码省略

子组件(HTML):

<!-- 代码省略 -->

子组件(TypeScript):

// 代码省略

你可以看到,在父组件中,我从API中获取数据,进行必要的过滤,并将这些数据作为输入传递给子组件。在子组件中,我使用Angular的Material表格将数据显示在屏幕上。尽管数据以我期望的方式显示在屏幕上,但在ngAfterViewInit中执行的那个console.log()返回undefined。我想知道为什么会发生这种情况,以及如何在浏览器控制台中正确返回数据。我已经尝试过在@Input()中以几种不同的方式使用它,但没有成功。

英文:

I have a large application made with Angular 12 where in one part of it there is a parent component and a child component.

Parent component (HTML):

&lt;app-sidenav&gt;&lt;/app-sidenav&gt;
&lt;div class=&quot;row fadeIn animated&quot;&gt;
  &lt;div class=&quot;col s12  m-0 p-2 pt-5&quot;&gt;
    &lt;span class=&quot;card-title col s6 pt-4&quot; style=&quot;font-size: x-large;&quot;&gt;
      {{&#39;Tasks&#39; | translate}}
    &lt;/span&gt;

    &lt;div class=&quot;col s6&quot;&gt;
      &lt;app-filter [tasks]=&quot;tasks&quot;&gt;&lt;/app-filter&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class=&quot;col s12&quot;&gt;
    &lt;mat-tab-group dynamicHeight mat-align-tabs=&quot;center&quot; class=&quot;p-2 pb-0&quot; (selectedIndexChange)=&quot;setTab($event)&quot;
    [selectedIndex]=&quot;selectedTab&quot; #tabGroup&gt;
      &lt;mat-tab *ngIf=&quot;!searching&quot; matBadge=&quot;7&quot;&gt;
        &lt;ng-template mat-tab-label&gt;
          &lt;div class=&quot;mr-5 ml-5 pr-5 pl-5&quot;&gt;
            {{&#39;Open&#39; | translate}}
          &lt;/div&gt;
          &lt;span *ngIf=&quot;openTasks.length &gt; 0&quot; class=&quot;btn btn-small btn-floating blue lighten-3 pulse&quot;&gt;
            {{openTasks.length}}
          &lt;/span&gt;
        &lt;/ng-template&gt;
        &lt;div class=&quot;card m-1&quot;&gt;
          &lt;app-queue-table [dataSourceInput]=&quot;openTasks&quot;&gt;&lt;/app-queue-table&gt;
        &lt;/div&gt;
      &lt;/mat-tab&gt;
      &lt;mat-tab *ngIf=&quot;!searching&quot;&gt;
        &lt;ng-template mat-tab-label&gt;
          &lt;div class=&quot;mr-5 ml-5 pr-5 pl-5&quot;&gt;
            {{&#39;In Progress&#39; | translate}}
          &lt;/div&gt;
          &lt;span *ngIf=&quot;inProgressTasks.length &gt; 0&quot; class=&quot;btn btn-small btn-floating weg-blue darken-4&quot;&gt; {{inProgressTasks.length}}
          &lt;/span&gt;
        &lt;/ng-template&gt;
        &lt;div class=&quot;card m-1&quot;&gt;
          &lt;app-queue-table [dataSourceInput]=&quot;inProgressTasks&quot;&gt;&lt;/app-queue-table&gt;
        &lt;/div&gt;
      &lt;/mat-tab&gt;
      &lt;mat-tab *ngIf=&quot;!searching&quot;&gt;
        &lt;ng-template mat-tab-label&gt;
          &lt;div class=&quot;mr-5 ml-5 pr-5 pl-5&quot;&gt;
            {{&#39;Finished&#39; | translate}}
          &lt;/div&gt;
          &lt;span *ngIf=&quot;finishedTasks.length &gt; 0&quot;
            class=&quot;btn btn-small btn-floating green lighten-4 weg-blue-text text-darken-4&quot;&gt;
            {{finishedTasks.length}}
          &lt;/span&gt;
        &lt;/ng-template&gt;
        &lt;div class=&quot;card m-1&quot;&gt;
          &lt;app-queue-table [dataSourceInput]=&quot;finishedTasks&quot;&gt;&lt;/app-queue-table&gt;
        &lt;/div&gt;
      &lt;/mat-tab&gt;
      &lt;mat-tab *ngIf=&quot;!searching&quot;&gt;
        &lt;ng-template mat-tab-label&gt;
          &lt;div class=&quot;mr-5 ml-5 pr-5 pl-5&quot;&gt;
            {{&#39;Canceled&#39; | translate}}
          &lt;/div&gt;
          &lt;span *ngIf=&quot;canceledTasks.length &gt; 0&quot; class=&quot;btn btn-small btn-floating red lighten-1&quot;&gt;
          {{canceledTasks.length}} &lt;/span&gt;
        &lt;/ng-template&gt;
        &lt;div class=&quot;card m-1&quot;&gt;
          &lt;app-queue-table [dataSourceInput]=&quot;canceledTasks&quot;&gt;&lt;/app-queue-table&gt;
        &lt;/div&gt;
      &lt;/mat-tab&gt;
      &lt;mat-tab *ngIf=&quot;searching&quot; class=&quot;p-4&quot;&gt;
        &lt;ng-template mat-tab-label&gt;
          &lt;div class=&quot;mr-5 ml-5 pr-5 pl-5&quot;&gt;
            {{&#39;Searching&#39; | translate}}
          &lt;/div&gt;
          &lt;span *ngIf=&quot;search &gt; 0&quot; class=&quot;btn btn-small btn-floating disabled&quot;&gt; {{search}} &lt;/span&gt;
        &lt;/ng-template&gt;
        &lt;div class=&quot;card p-3&quot;&gt;
          &lt;app-queue-table [dataSourceInput]=&quot;canceledTasks&quot;&gt;&lt;/app-queue-table&gt;
        &lt;/div&gt;
      &lt;/mat-tab&gt;
    &lt;/mat-tab-group&gt;
  &lt;/div&gt;
&lt;/div&gt;

Parent component (TypeScript):

import { Component, OnInit, ViewChild } from &#39;@angular/core&#39;
import { MatTabGroup, MatTabHeaderPosition } from &#39;@angular/material/tabs&#39;;
import { Router } from &#39;@angular/router&#39;;
import { ModTask } from &#39;src/app/core/models/modTask.model&#39;;
import { UserService } from &#39;src/app/core/services/user.service&#39;;
import { ModTaskService } from &#39;src/app/shared/services/modTask.service&#39;;

@Component({
  selector: &quot;app-home&quot;,
  templateUrl: &quot;./home.component.html&quot;,
  styleUrls: [&quot;./home.component.css&quot;]
})
export class HomeComponent implements OnInit{

  public tasks: ModTask[]
  public openTasks: ModTask[]
  public inProgressTasks: ModTask[]
  public finishedTasks: ModTask[]
  public canceledTasks: ModTask[]

  public searching = false;
  public open: number = 0;
  public inProgress: number = 0;
  public finished: number = 0;
  public canceled: number = 0;
  public working: number = 0;
  public search: number = 0;
  public selectedTab: number;
  public coordinator: string

  private cacheName = &quot;165116511s1a&quot;;

  @ViewChild(&#39;tabGroup&#39;) tabGroup: MatTabGroup;

  constructor(
    private router: Router,
    private modTaskService: ModTaskService,
    private userService: UserService
  ) {}

  async ngOnInit(): Promise&lt;void&gt; {
    this.userService.getLoggedUser().subscribe(user =&gt; { this.coordinator = user.userLogin })
    this.tasks = await this.modTaskService.getByCoordinator(this.coordinator)
    this.openTasks = this.tasks.filter(tasks =&gt; tasks.status.status === &quot;Open&quot;)
    this.inProgressTasks = this.tasks.filter(tasks =&gt; tasks.status.status === &quot;In Progress&quot;)
    this.finishedTasks = this.tasks.filter(tasks =&gt; tasks.status.status === &quot;Finished&quot;)
    this.canceledTasks = this.tasks.filter(tasks =&gt; tasks.status.status === &quot;Canceled&quot;)
    console.log(this.openTasks)
  }

  async ngAfterViewInit() {
    console.log(&#39;atualizado&#39;)
  }

  setTab(tab: number) {
    this.ngAfterViewInit()
  }

  // ACTIONS =========================================================
  setSearch(event: boolean) {
    this.searching = event;
    localStorage.setItem(this.cacheName + &quot;as&quot;, event.toString());
  }

  // QUEUE COUNTS ====================================================
  setResultLength(type: number, resultLength: number = 0) {
    switch (type) {
      case 1:
        this.open = resultLength;
        break;
      case 2:
        this.inProgress = resultLength;
        break;
      case 3:
        this.finished = resultLength;
        break;
      case 4:
        this.canceled = resultLength;
        break;
      case 5:
        this.open = 0;
        this.inProgress = 0;
        this.finished = 0;
        this.search = resultLength;
        break;
    }
  }
}

Child component (HTML):

&lt;div class=&quot;row fadeIn animated m-0 p-0 table-wrapper&quot; style=&quot;min-height:480px&quot;&gt;
  &lt;!-- &lt;div class=&quot;col s12 vertical-center&quot; style=&quot;height:100%&quot; *ngIf=&quot;dataSource.length === 0&quot;&gt;
    &lt;span class=&quot;material-icons large grey-text text-lighten-3&quot;
        style=&quot;margin-right: auto; margin-left: auto;&quot;&gt;inbox&lt;/span&gt;
    &lt;/div&gt; --&gt;
    &lt;!--COM RESULTADOS--&gt;
    &lt;table mat-table [dataSource]=&quot;dataSourceInput&quot;&gt;
      &lt;ng-container matColumnDef=&quot;id&quot;&gt;
        &lt;th mat-header-cell *matHeaderCellDef&gt; ID &lt;/th&gt;
        &lt;td mat-cell *matCellDef=&quot;let task&quot; (click)=&quot;openRequest(task)&quot;&gt; {{task.id}} &lt;/td&gt;
      &lt;/ng-container&gt;
      &lt;ng-container matColumnDef=&quot;branch&quot;&gt;
        &lt;th mat-header-cell *matHeaderCellDef&gt; Branch &lt;/th&gt;
        &lt;td mat-cell *matCellDef=&quot;let task&quot; (click)=&quot;openRequest(task)&quot;&gt; {{task.branchId}} &lt;/td&gt;
      &lt;/ng-container&gt;
      &lt;ng-container matColumnDef=&quot;createdBy&quot;&gt;
        &lt;th mat-header-cell *matHeaderCellDef&gt; Created By &lt;/th&gt;
        &lt;td mat-cell *matCellDef=&quot;let task&quot; (click)=&quot;openRequest(task)&quot;&gt; {{task.creatorUserName}} &lt;/td&gt;
      &lt;/ng-container&gt;
      &lt;ng-container matColumnDef=&quot;salesDocument&quot;&gt;
        &lt;th mat-header-cell *matHeaderCellDef&gt; Sales Document &lt;/th&gt;
        &lt;td mat-cell *matCellDef=&quot;let task&quot; (click)=&quot;openRequest(task)&quot;&gt; {{task.salesDocument}} &lt;/td&gt;
      &lt;/ng-container&gt;
      &lt;ng-container matColumnDef=&quot;createdAt&quot;&gt;
        &lt;th mat-header-cell *matHeaderCellDef&gt; Created At &lt;/th&gt;
        &lt;td mat-cell *matCellDef=&quot;let task&quot; (click)=&quot;openRequest(task)&quot;&gt; {{task.createdAt}} &lt;/td&gt;
      &lt;/ng-container&gt;
      &lt;ng-container matColumnDef=&quot;updatedAt&quot;&gt;
        &lt;th mat-header-cell *matHeaderCellDef&gt; Updated At &lt;/th&gt;
        &lt;td mat-cell *matCellDef=&quot;let task&quot; (click)=&quot;openRequest(task)&quot;&gt; {{task.updatedAt}} &lt;/td&gt;
      &lt;/ng-container&gt;
      &lt;ng-container matColumnDef=&quot;target&quot;&gt;
        &lt;th mat-header-cell *matHeaderCellDef&gt; Target &lt;/th&gt;
        &lt;td mat-cell *matCellDef=&quot;let task&quot; (click)=&quot;openRequest(task)&quot;&gt; {{task.target}} &lt;/td&gt;
      &lt;/ng-container&gt;

      &lt;tr mat-header-row *matHeaderRowDef=&quot;displayedColumns&quot;&gt;&lt;/tr&gt;
      &lt;tr mat-row *matRowDef=&quot;let row; columns: displayedColumns;&quot;&gt;&lt;/tr&gt;
    &lt;/table&gt;
    &lt;mat-paginator #paginator [pageSizeOptions]=&quot;[5, 10, 20]&quot;
                 showFirstLastButtons
                 aria-label=&quot;Select page of periodic elements&quot;&gt;
    &lt;/mat-paginator&gt;
&lt;/div&gt;

Child component (TypeScript):

import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from &#39;@angular/core&#39;
import { Router } from &#39;@angular/router&#39;
import { ModTask } from &#39;src/app/core/models/modTask.model&#39;
import { QueueTable } from &#39;src/app/shared/components/queue-table/queue-table.component&#39;
import {MatPaginator} from &#39;@angular/material/paginator&#39;
import { MatTableDataSource } from &#39;@angular/material/table&#39;

@Component({
  selector: &#39;app-queue-table&#39;,

  templateUrl: &#39;./task-queue.component.html&#39;,
  styleUrls: [&#39;./task-queue.component.css&#39;]
})
export class QueueTableComponent {

  @Input() public dataSourceInput: ModTask[]

  displayedColumns: string[] = [&#39;id&#39;, &#39;branch&#39;, &#39;createdBy&#39;, &#39;salesDocument&#39;, &#39;createdAt&#39;, &#39;updatedAt&#39;, &#39;target&#39;]

  ngAfterViewInit(): void {
    console.log(this.dataSourceInput)
  }

  constructor(
    private router: Router
  ) {
  }

  public openRequest(modTask: ModTask) {
    this.router.navigate([`moduleTask/process/${modTask.id}`])
  }
}

As you can see, I fetch data in my API in the parent component, do the filtering that needs to be done and put this data as an Input in the child component. And in the child component, I show this data on screen using Angular's Material Table. The data is shown as I would like, but when that console.log() in ngAfterViewInit is executed, it returns undefined, and I need to do some work on this data to filter, but I can't because it's set to undefined, even showing the data on screen.

I wanted to know why this happens and what can be done to return the data correctly in the browser console.

I wanted to know why this happens and what can be done to return the data correctly in the browser console.
I've tried using @Input() in a few different ways, but without success.

答案1

得分: 1

你用于输入的值是异步获取的。你的输入数据会显示,因为在异步调用设置数据后,变更检测会运行并将其更新为返回的值。你可以通过将子生命周期钩子从 ngAfterViewInit(仅运行一次)更改为 ngAfterViewChecked(在每次视图检查后运行)来测试这一点。你会看到现在它会先记录未定义(你的初始值),然后是更新后的视图值。

这个问题的一个简单解决方法是在子元素上放置一个 ngIf,直到 tasks 被设置,例如 &lt;app-queue-table *ngIf=&quot;tasks&quot; [dataSourceInput]=&quot;inProgressTasks&quot;&gt;&lt;/app-queue-table&gt;

这样,只有在数据已经存在时子元素才会被实例化,因此传递给输入的初始值始终是定义的。在这里提醒一下,如果你使用了严格的 TypeScript 检查并相应地为变量设置了类型,你的 IDE 将会警告你尝试将 "X | undefined" 的值设置为仅应该是 "X" 的东西。

英文:

The value you're using for the input is fetched asynchronously. Your input data is shown, because after your asynchronous call to this data is set, change detection runs and updates it to the returned value. You can test this by changing your child lifecycle hook from ngAfterViewInit (which runs once) to ngAfterViewChecked (which runs after every view check). You will see it will now log undefined (your initial value) then your updated view value.

An easy fix for this is just to put an ngIf on your child until tasks is set, e.g. &lt;app-queue-table *ngIf=&quot;tasks&quot; [dataSourceInput]=&quot;inProgressTasks&quot;&gt;&lt;/app-queue-table&gt;

This way your child only gets instantiated when the data already exists, so the initial value passed in the input will always be defined. As a heads up here, if you were using strict TypeScript checking and had typed your variables accordingly, your IDE would have warned you that you were trying to set a value of "X | undefined" to something that should only be "X".

答案2

得分: 1

在Angular中,当你使用@Input时,可以通过ngOnChanges事件捕获事件。

export class QueueTableComponent implements OnChanges {
  ngOnChanges(changes: SimpleChanges): void {
    if (changes['dataSourceInput']) {
      console.log(this.dataSourceInput)
    }
  }
}

通过这种方式,将监听@Input中变量的所有更改,并在“changes”对象中找到dataSourceInput属性,该属性是从父组件接收的列表。

英文:

in angular when you use an @Input, the event can be captured with the ngOnChanges event.

export class QueueTableComponent implements OnChanges{
    ngOnChanges(changes: SimpleChanges): void {
		if (changes[&#39;dataSourceInput&#39;]) {
           console.log(this.dataSourceInput)
		}
	}
}

With this, all the changes of the variable in @Input will be listened and in the "changes" object on the property dataSourceInput, which is the list received from the parent component, will be found.

答案3

得分: 0

尝试创建任务变量,例如 public openTasks = new MatTableDataSource&lt;ModTask&gt;();,并设置数据如下: this.openTasks.data = this.tasks.filter(tasks =&gt; tasks.status.status === &quot;Open&quot;);

英文:

Try to create tasks variable like public openTasks = new MatTableDataSource&lt;ModTask&gt;(); and set your data like this.openTasks.data = this.tasks.filter(tasks =&gt; tasks.status.status === &quot;Open&quot;).

huangapple
  • 本文由 发表于 2023年3月15日 21:08:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75745173.html
匿名

发表评论

匿名网友

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

确定