Muirik
Muirik

Reputation: 6289

@Input() Not Passing As Expected Between Parent-Child Components in Angular 2 App

I am trying to abstract out a tabular-data display to make it a child component that can be loaded into various parent components. I'm doing this to make the overall app "dryer". Before I was using an observable to subscribe to a service and make API calls and then printing directly to each component view (each of which had the tabular layout). Now I want to make the tabular data area a child component, and just bind the results of the observable for each of the parent components. For whatever reason, this is not working as expected.

Here is what I have in the parent component view:

<div class="page-view">
    <div class="page-view-left">
        <admin-left-panel></admin-left-panel>
    </div>
    <div class="page-view-right">
        <div class="page-content">
            <admin-tabs></admin-tabs>
            <table-display [records]="records"></table-display>
        </div>
    </div>
</div>

And the component file looks like this:

import { API } from './../../../data/api.service';
import { AccountService } from './../../../data/account.service';
import { Component, OnInit, Input } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { TableDisplayComponent } from './../table-display/table-display.component';

@Component({
  selector: 'account-comp',
  templateUrl: 'app/views/account/account.component.html',
  styleUrls: ['app/styles/app.styles.css']
})
export class AccountComponent extends TabPage implements OnInit {

    private section: string;
    records = [];
    errorMsg: string;

    constructor(private accountService: AccountService,
                router: Router,
                route: ActivatedRoute) {
    }

    ngOnInit() {
       this.accountService.getAccount()
            .subscribe(resRecordsData => this.records = resRecordsData,
            responseRecordsError => this.errorMsg = responseRecordsError);
    }
}

Then, in the child component (the one that contains the table-display view), I am including an @Input() for "records" - which is what the result of my observable is assigned to in the parent component. So in the child (table-display) component, I have this:

import { AccountService } from './../../../data/account.service';
import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'table-display',
  templateUrl: './table-display.component.html',
  styleUrls: ['./table-display.component.less']
})
export class TableDisplayComponent {

    @Input() records;

    constructor() {
    }

}

Lastly, here's some of the relevant code from my table-display view:

<tr *ngFor="let record of records; let i = index;">
<td>{{record.name.first}} {{record.name.last}}</td>
                <td>{{record.startDate | date:"MM/dd/yy"}}</td>
                <td><a class="bluelink" [routerLink]="['/client', record._id ]">{{record.name.first}} {{record.name.last}}</a></td>

When I use it with this configuration, I get "undefined" errors for the "records" properties I'm pulling in via the API/database. I wasn't getting these errors when I had both the table display and the service call within the same component. So all I've done here is abstract out the table-display so I can use it nested within several parent components, rather than having that same table-display show up in full in every parent component that needs it.

What am I missing here? What looks wrong in this configuration?

Upvotes: 1

Views: 271

Answers (1)

chrispy
chrispy

Reputation: 3612

You need to protect against record being null until it comes in to your child component (and therefore it's view).

Use Elvis operators to protect your template:

<tr *ngFor="let record of records; let i = index;">
    <td>{{record?.name?.first}} {{record?.name?.last}}</td>
    <td>{{record?.startDate | date:"MM/dd/yy"}}</td>
    <td><a class="bluelink" [routerLink]="['/client', record?._id ]">  {{record?.name?.first}} {{record?.name?.last}}</a></td>

You can also assign your input to an empty array to help with this issue:

@Input() records = [];

Upvotes: 2

Related Questions