82

I am stuck on trying to pass a property value into my component. From what I've read everything looks correct. But it is still not working. My test value gets output to the screen and the console as null. :(

This is my test component:

import {Component, Input} from 'angular2/angular2';

@Component({
    selector: 'TestCmp',
    template: `Test Value : {{test}}`
})

export class TestCmp {

    @Input() test: string;

    constructor()
    {
        console.log('This if the value for user-id: ' + this.test);
    }
}

This is how I am calling the component from the parent page.

<TestCmp [test]='Blue32'></TestCmp>

When the page render's the test value is empty. I only see 'Test Value :'.

Instead of 'Test Value : Blue32'.

Zameer Ansari
  • 28,977
  • 24
  • 140
  • 219
Zorthgo
  • 2,857
  • 6
  • 25
  • 37

10 Answers10

151

You have four things that I can note :

  • You are passing an input in the root component, which will not work.
  • As @alexpods mentioned, you are using CamelCase. You should not.
  • You are passing an expression instead of an string through [test]. That means that angular2 is looking for a variable named Blue32 instead of passing a raw string.
  • You are using the constructor. That will not work, it must be after the view has been initialized data-bound properties have been initialized (see docs for OnInit).

So with a few fixes it should work

Example updated to beta 1

import {Component, Input} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';

@Component({
  selector : 'childcmp',
  template: `Test Value : {{test}}`
})
class ChildCmp {
    @Input() test: string;
    ngOnInit() {
        console.log('This if the value for user-id: ' + this.test);
    }
}

@Component({
    selector: 'testcmp',
    template : `<childcmp [test]="'Blue32'"></childcmp>`
    directives : [ChildCmp]
})
export class TestCmp {}

bootstrap(TestCmp);

See this plnkr as an example.

Update

I see that people still reach this answer, so I've updated the plnkr to beta 1 and I corrected one point on the explanation : You can access inputs in ngAfterViewInit, but you can access them earlier in the lifecycle within ngOnInit.

Eric Martinez
  • 31,277
  • 9
  • 92
  • 91
  • 1
    Thanks Eric for the help!... Those are some very good points. "AfterViewInit" will really come in handy. :) – Zorthgo Oct 25 '15 at 14:02
  • That plunkr seems broken, I see only "Test Value :" on latest Chrome/Firefox. – aikeru Nov 09 '15 at 03:00
  • 1
    @aikeru thanks for letting me know. That's really weird, the plnkr barely looked like the code in the answer. Anyway, I fixed it :) – Eric Martinez Nov 09 '15 at 03:05
  • If the input variable is assigned in a async call, it is still "undefined" when it runs ngOnInit or ngAfterViewInit. In this case, where is the best place to perform actions when the input variable is initialized? – Comtaler Feb 09 '16 at 22:08
  • Hi @EricMartinez, would you mind looking at [this](http://stackoverflow.com/questions/35601876/input-binding-doesnt-work-when-i-put-it-under-a-router-component). I am very grateful if you help :) – Quy Tang Feb 24 '16 at 12:15
  • 5
    It is PascalCase, not camelCase. – Elfayer May 14 '16 at 16:32
  • @EricMartinez Great answer! Could you please reference docs where I can read more about point 1. I remember there being mention of similar limitations with root scope in Angular1 but this is new to me for Angular2. – Precastic Sep 02 '16 at 16:51
  • 5
    For anyone else the part of this solution that fixed this for me was using `` instead of `` (the double quoted thing) – FirstDivision Mar 02 '17 at 19:51
  • The two types of quotes is something where one can easily get tripped up. this code example was helpful. – Mickey Segal Oct 19 '17 at 15:06
  • How can I pass service object type of data from one component to other component by using @Input or any other method – Hidayt Rahman Apr 13 '18 at 10:30
  • If you are passing a string, there is no need to use brackets, just say test="Blue32". Use brackets when you want to use variable name that exist in the .ts file – Mulperi Jul 09 '21 at 06:22
18

It's that easy as surrounding the string with double quotes, like this:

<TestCmp [test]="'Blue32'"></TestCmp>
fgonzalez
  • 3,787
  • 7
  • 45
  • 79
7

If you use brackets [] the Angular uses property binding and expects to receive an expression inside the quotes and it looks for a property called 'Blue32' from your component class or a variable inside the template.

If you want to pass string as a value to the child component you can pass it like so:

<child-component childProperty='passing string'></child-component>

or if you for some reason want to have the brackets:

<child-component [childProperty]="'note double quotes'"></child-component>

And then take it in to child.component.ts like this:

import { Component, Input } from "@angular/core";

@Component({})
export class ChildComponent {

    @Input()
    childProperty: string;

}
Mulperi
  • 1,450
  • 1
  • 14
  • 24
6

This angular class could make the trick for static attributes: ElementRef https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html

import {ElementRef} from 'angular2/core'

constructor(elementRef: ElementRef) {
    elementRef.nativeElement.getAttribute('api')
}
Charles HETIER
  • 1,934
  • 16
  • 28
3

Sharing what worked for me:

Adding an input to the Angular 4 app

Assuming that we have 2 components:

  • parent-component
  • child-component

We wanted to pass some value from parent-component to child-component i.e. an @Input from parent-component.html to child-component.ts. Below is an example which explains the implementation:

parent-component.html looks like this:

<child-component [someInputValue]="someInputValue"></child-component>

parent-component.ts looks like this:

  
  class ParentComponent {

  someInputValue = 'Some Input Value';

}

child-component.html looks like this:

<p>Some Input Value {{someInputValue}}</p>

child-component.ts looks like this:


import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'child-component',
  templateUrl: './child-component.html'
})
export class ChildComponent implements OnInit {

  @Input() someInputValue: String = "Some default value";

  @Input()
  set setSomeInputValue(val) {
    this.someInputValue += " modified";
  }

  constructor() {
    console.log('someInputValue in constructor ************** ', this.someInputValue); //someInputValue in constructor ************** undefined
  }

  ngOnInit() {
    console.log('someInputValue  in ngOnInit ************** ', this.someInputValue); //someInputValue  in ngOnInit ************** Some Input Value
  }
}

Notice that the value of the @Input value is available inside ngOnInit() and not inside constructor().

Objects reference behaviour in Angular 2 / 4

In Javascript, objects are stored as references.

This exact behaviour can be re-produced with the help of Angular 2 / 4. Below is an example which explains the implementation:

parent-component.ts looks like this:

  
  class ParentComponent {

  someInputValue = {input: 'Some Input Value'};

}

parent-component.html looks like this:

  
{{someInputValue.input}}



child-component.html looks like this:



Some Input Value {{someInputValue}}

change input

child-component.ts looks like this:


import { Component, OnInit, Input } from '@angular/core';

@Component({
  selector: 'child-component',
  templateUrl: './child-component.html'
})
export class ChildComponent implements OnInit {

  @Input() someInputValue = {input:"Some default value"};

  @Input()
  set setSomeInputValue(val) {
    this.someInputValue.input += " set from setter";
  }

  constructor() {
    console.log('someInputValue in constructor ************** ', this.someInputValue); //someInputValue in constructor ************** undefined
  }

  ngOnInit() {
    console.log('someInputValue  in ngOnInit ************** ', this.someInputValue); //someInputValue  in ngOnInit ************** Some Input Value
  }

  changeInput(){
    this.someInputValue.input += " changed";
  }
}

The function changeInput() will change the value of someInputValue inside both ChildComponent & ParentComponent because of their reference. Since, someInputValue is referenced from ParentComponent's someInputValue object - the change in ChildComponent's someInputValue object changes the value of ParentComponent's someInputValue object. THIS IS NOT CORRECT. The references shall never be changed.

Zameer Ansari
  • 28,977
  • 24
  • 140
  • 219
2

I believe that the problem here might have to do with the page's life cycle. Because inside the constructor the value of this.test is null. But if I add a button to the template linked to a function that pushes the value to the console (same as I am doing in the constructor) this.test will actually have a value.

Zorthgo
  • 2,857
  • 6
  • 25
  • 37
1

Maybe look like a hammer, but you can put the input wrapped on an object like this:

<TestCmp [test]='{color: 'Blue32'}'></TestCmp>

and change your class

class ChildCmp {
    @Input() test: any;
    ngOnInit() {
        console.log('This if the value for user-id: ' + this.test);
    }
}
jcmordan
  • 1,038
  • 13
  • 20
1

you have to import input like this at top of child component

import { Directive, Component, OnInit, Input } from '@angular/core';
Al-Mothafar
  • 7,949
  • 7
  • 68
  • 102
Mukesh
  • 69
  • 7
0

When you are making use of @Input for the angular interaction.It is always preferred approach to pass the data from parent to child in JSON object apparently it doesn't not restrict by @Angular Team to use local variable or static variable.

In context to access the value on child component make use ngOnInit(){} angular life hook cycle regardless of constructor.

That will help you out. Cheers.

Sachin Mishra
  • 1,125
  • 1
  • 16
  • 17
0

When I had this issue there was actually just a compile error that I had to fix first (circular dependency needed resolved).

James L.
  • 12,893
  • 4
  • 49
  • 60