54

I have a parent and child component. Parent component has index and passes this through to the child component as an @Input. This index value constantly changes in the parent component, and inside my child component, I want to run a function everytime the Parent component changes index. This has a slide component that constantly changes it. How can I achieve this? I've tried with the below code (this.index.subscribe(() =>), but I'm getting that it's not a function everytime I try and initialise the subscription.

EDIT: There is no related code in the Child template that could impact this, so it's not provided. ngOnChange doesn't seem to work as the change is happening in the directive as opposed to the parent's template.

Child:

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

@Component({
    selector: "child",
    templateUrl: "components/child/child.html",
})

export class ChildComponent implements OnInit {
    @Input() index: string;
    currentIndex: any;

    constructor() {}

    ngOnInit() {}

    ngOnChanges(){}

    ngAfterViewInit() {
        console.log("index " + this.index);
        this.currentIndex = this.index.subscribe(() => {
             console.log("index " + this.index);
        })
    }

    ngOnDestroy() {
        this.currentIndex.unsubscribe();
    }

}

Parent:

import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {Page} from "ui/page";
import {ChildComponent} from '/components/child/child.component'

@Component({
    selector: "parent",
    template: "<child [index]="index"></child>",
    directives: [ChildComponent]
})

export class ParentComponent implements OnInit {

    index: string = "0,1";

    constructor(private page: Page) {
    }



}
8
  • 1
    First off, ParentComponent's template should be template, not templateUrl. Besides, could you show the template for ChildComponent ? Commented Jul 20, 2016 at 1:07
  • index in ParentComponent is a string and ChildComponent expects it to be an Observable<any>. Is that how you are using them? Commented Jul 20, 2016 at 1:27
  • Yes, otherwise you get Error: this.index.subscribe is not a function. Commented Jul 20, 2016 at 1:31
  • please provide the code of your template Commented Jul 20, 2016 at 1:40
  • Where's this.slides coming from? From your code, it can't be anything other than undefined (it is not being set anywhere), which throws an error at let SlidesXml = this.slides.nativeElement;. Commented Jul 20, 2016 at 1:58

4 Answers 4

71

https://angular.io/docs/ts/latest/api/core/index/Input-var.html

To quote:

Angular automatically updates data-bound properties during change detection.

If you need to do some processing on the input, look at the get and set. https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-setter

From the documentation, here is an example.

import { Component, Input } from '@angular/core';
@Component({
  selector: 'name-child',
  template: `
    <h3>"{{name}}"</h3>
  `
})
export class NameChildComponent {
  _name: string = '<no name set>';
  @Input()
  set name(name: string) {
    this._name = (name && name.trim()) || '<no name set>';
  }
  get name() { return this._name; }
}

You don't need to use an observable.

Sign up to request clarification or add additional context in comments.

2 Comments

sometimes you want to re-trigger a method of your component after any input changed... then using ngOnChanges() is a lot simpler than running that method in each setter of every input, I guess.
for those who feel the setter technique is tedious, checkout subjectize, which wraps the setter under the hood and you don't need a private variable.
29

As mentioned in the other answer, you can use ngOnChanges life hook. From Angular`s documentation:

ngOnChanges: Respond after Angular sets a data-bound input property. The method receives a changes object of current and previous values.

Check the example below to see how you can use it:

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

@Component({
  selector: 'child',
  template: `
    Value:
    <span>{{ value }}</span>
    <br />
    <br />
    Number of changes: {{ numberOfChanges }}
  `
})
export class ChildComponent implements OnChanges {
  @Input() value: any;

  private numberOfChanges: number = 0;

  ngOnChanges() {
    this.numberOfChanges++;
  }
}

Plunker: http://plnkr.co/edit/XjD1jjCnqiFdbhpTibIe

3 Comments

What directive do you mean that is updating the index?
Please, check the context inside the SlidesXml.on("start", (event) => { ... } event
@bnussey: I have modified Alexandre Junges's plunker and forked it, this plunker shows that even changing the code in the directive will update the child.
15

ngOnChange is called each time an input parameter changes. You even have it in your Child component, but it's not implemented correctly:

ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
  // check the object "changes" for new data
}

More details: https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#onchanges

Update: index in the parent component is a string. For some reason it became an Observable in the child component.

4 Comments

Thanks for your response. Can you advise how to implement it correctly with some more detail please?
Seems this only work on initial load - for array inputs, at least.
@RemiSture, technically, arrays are passes by reference. If you are reassigning array, then it'll be triggered, but if you modify array, reference stayed unchanged. You may need to notify child component. Check blog.thoughtram.io/angular/2016/02/22/….
4

Another solution for "transforming" the changing value into an observable should be

@Input() status: Status;
private status$: Subject<Status>;

constructor() {
    this.status$ = new Subject<Status>();
}

ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {

    this.status.next(this.status);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.