Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

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

<app-sidenav></app-sidenav>
<div class="row fadeIn animated">
  <div class="col s12  m-0 p-2 pt-5">
    <span class="card-title col s6 pt-4" style="font-size: x-large;">
      {{'Tasks' | translate}}
    </span>

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

Parent component (TypeScript):

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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

@Component({
  selector: "app-home",
  templateUrl: "./home.component.html",
  styleUrls: ["./home.component.css"]
})
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 = "165116511s1a";

  @ViewChild('tabGroup') tabGroup: MatTabGroup;

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

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

  async ngAfterViewInit() {
    console.log('atualizado')
  }

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

  // ACTIONS =========================================================
  setSearch(event: boolean) {
    this.searching = event;
    localStorage.setItem(this.cacheName + "as", 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):

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

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

Child component (TypeScript):

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

@Component({
  selector: 'app-queue-table',

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

  @Input() public dataSourceInput: ModTask[]

  displayedColumns: string[] = ['id', 'branch', 'createdBy', 'salesDocument', 'createdAt', 'updatedAt', 'target']

  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.

>Solution :

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. <app-queue-table *ngIf="tasks" [dataSourceInput]="inProgressTasks"></app-queue-table>

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading