@Input variable undefined


#1

I know, probably more an Angular question, but if you have some idea, I would like to hear.

HTML that use my-component

<ion-content>
  <my-component [parts]="_parts"></my-component>
  ...
</ion-content>

My component

import { IONIC_DIRECTIVES, NavController, IonicApp } from 'ionic-angular';
import { Component, Input } from '@angular/core';
...

@Component({
  selector: 'my-component',
  templateUrl: 'build/pages/components/my-component.component.html',
  directives: [IONIC_DIRECTIVES],
})

export class MyComponent {
  @Input() parts: IPartInfo[];
  
  constructor(...) {   
    console.log(this.parts);
  }

Why this.parts is “undefined”?

By the way, before beta 7, everything was working. Now I suspect that Angular is giving me the trouble. What do you think?


#2

Are you certain that _parts in the controller for the top view is always initialized? This problem typically happens when you are loading _parts from an external resource (like an XHR) and it is not initialized in the constructor. My general solution is to always initialize a dummy:

_parts: IPartInfo[] = [];

constructor(partService: PartGettingService) {
  partService.fetchParts().then((parts) => {
    this._parts = parts;
  });
}

#3

Try @Input(‘parts’) parts: IPartInfo[];


#4

first some comments to your code.
Please do not start interfaces/models with I, if you have a class or interface called PartInfo it is clear you are working with Objects of this type.

Do not do async stuff in the constructor --> use the Life cycle hooks.

import {OnInit} from '@angular/core';

...
class MyComponent implements OnInit {
  ngOnInit() {
    // requests and async stuff here
  }
}

Because object instantiation should not depend on async stuff.

If you are using TypeScript do not prefix private stuff with ‘_’ you have the private keyword to do this:

private parts: ....;

And to your base question --> in the constructor functions the databindings are not ready.
So if you set an initial value in the template to your input --> you need to wait until the life cycle hook OnInit gets called.

<my-component [parts]="partList"></my-component>
import {Component, Input, OnInit} from '@angular/core';

...
class MyComponent implements OnInit {
  @Input() parts;

  constructor() {
    // this.parts = undefined
  }

  ngOnInit() {
    // this.parts is initial set!
  }
}

#5

Thank you guys for your input. It gave me the help to investigate more about the issue.

What I found is my pipes had issues!

Yep, before I was able to do:

export class OrderBy {
transform(input: IPartInfo[], [orderBy]) {

Now, I need to remove the square brackets to make it work (I also added the PipeTransform, that does not seem to be necessary, but probably cleaner based on Angular.io site)!

export class OrderBy implements PipeTransform {
transform(input: IUnitInfo[], orderBy) {

For now, it is ok, but I just don’t know how it will be possible to pass more than one parameters. I thought the [] were used for that


#6

While I agree with most of your points, not this one. TypeScript’s ability to enforce access control modifiers is limited, and I think naming conventions still have value here.


#7

pipes changed in since Beta17.

the transform function accepts now the input value and many additional parameters (how many you want to pass).

before it only takes an array of additional arguments.


#8

Thanks for that comment @bengtler

Good to know


#9

Hmmm same/similar problem here.

Cordova CLI: 6.0.0
Gulp version: CLI version 3.9.1
Gulp local: Local version 3.9.1
Ionic Framework Version: 2.0.0-beta.7
Ionic CLI Version: 2.0.0-beta.25
Ionic App Lib Version: 2.0.0-beta.15
OS:
Node Version: v6.2.0

Page:

<appcards [data]="apps">    
</appcards>

with

constructor(nav: NavController, …){

this.apps=[];
}
ngOnInit(){
this.appsService.apps$.subscribe(
apps=>{
if (apps != null && this.apps == null){
this.apps = apps;
}
}

Component:

export class AppCardsComponent implements OnInit {
@Input(‘data’) data:any;
constructor(…){
console.log(“AppCardsComponent()”,this.data); ===> undefined
}
ngOnInit(){
console.log(“AppCardsComponent.ngOnInit”,this.data) ===> works
}

But still on my page I’m getting strange error:

probably this ist related to *ngFor
with i use with the input data.

any hints?


#10

If you read what @bengtler said above, don’t get the data in the constructor and use ngOnInit. Like you saw, the this.data is there and ready to use.

Also, what kind of strange error do you see?


#11

Any chance this is relevant?


#12

I my Case the object transfered via @Input() is an Array of objects.
this array is displayed via *ngFor

   <ion-card responsive
        *ngFor="let date of data" >
    <ion-card-content>
       <h2>{{date.name}}</h2>
       <p>{{date.description}}</p>
   </ion-card-content>
....

But only the first card get display correctly, the other, don’t appear. or appear just with every propertie set to null…
so instead of seeing around 10 cards i get one right an 9 empty cards.
The data is retrieved async via nested http-calls.