0

In my child component i have two buttons like following:

child-component.html

<div>
   <button (click)="getFirstData()">First</button>
   <button (click)="getLastData()" >Last</button>
</div>

In child-component.ts:

export class ChildComponent implements OnInit, AfterViewInit {
  firstItem: number;
  lastItem: number;

  constructor() {
  }

  ngOnInit() {
  }

  ngAfterViewInit(): void {
  }

  getFirstData() {
    this.firstItem = 1;
    return this.firstItem;
  }

  getLastData() {
    this.lastItem= 10;
    return this.lastItem;
  }

}

In parent component i need to receive the firstItem and lastItem data after the respective method is invoked from child component. When i try to get that in parent component using ViewChild in AfterViewInit lifecycle hook like following:

parent-component.ts

export class ParentComponent implements OnInit, AfterViewInit {
  @ViewChild(ChildComponent, {static: true}) child: ChildComponent;

  first: number;
  last: number;

  ngOnInit() {
  }

  ngAfterViewInit() {

    console.log('first value ', this.child.getFirstData());
    console.log('last value ', this.child.getLastData());

  }

  getValues(): void {
    // some operations heer
  }
}

I can't get that data from child.

My goal is to receive those data after method invoked in child after button click event there. And after receiving, i need to call the getValues() method in parent component.

How can i achieve this ?

Nazem Mahmud Piash
  • 1,053
  • 1
  • 13
  • 34
  • Don't use ViewChild. Use Outputs. https://angular.io/guide/component-interaction#parent-listens-for-child-event – JB Nizet Jan 11 '20 at 07:30
  • @JBNizet for each Output, i need to emit each event separately. Suppose i have three or four method call, then i have to emit three or four eventemitter. And in child selector i have to declare each of them. I am trying to neglecting this. On the other hand, when i am using ViewChild, i can get the whole component in one line. – Nazem Mahmud Piash Jan 11 '20 at 07:35
  • No, you don't. You can for example use a single output and emit `{ first: 1 }`or `{ last: 10 }`. You could also of course emit always the same value (undefined for example), and then get the new state from the child component using ViewChild, but you still wouldn't know what has just been changed in the state. – JB Nizet Jan 11 '20 at 07:36

1 Answers1

1

Example - Output decorator

child component:

export class ChildComponent implements OnInit, AfterViewInit {
  firstItem: number;
  lastItem: number;

  @Output() firstItemChanged = new EventEmitter<number>();
  @Output() lastItemChanged = new EventEmitter<number>();

  constructor() {
  }

  ngOnInit() {
  }

  ngAfterViewInit(): void {
  }

  getFirstData() {
    this.firstItem = 1;
    this.firstItemChanged.emit(this.firstItem);
  }

  getLastData() {
    this.lastItem= 10;
    this.firstItemChanged.emit(this.lastItem);
  }

}

Parent component html:

    <child-component (firstItemChanged)="handleFirstItemChanged($event)" (lastItemChanged)="handleLastItemChanged($event)"></child-component>

Parent component:

export class ParentComponent {

  handleFirstItemChanged(value: number): void {
    // handle event
  }

  handleLastItemChanged(value: number): void {
    // handle event
  }
}

Example - BehaviorSubject

child component:

interface State {
  firstItem: number;
  lastItem: number;
}

export class ChildComponent {

  state = new BehaviorSubject<State>({
    firstItem: 0,
    lastItem: 0
  });

  constructor() { }

  firstButtonClicked() {
    const newState = this.state.value;
    newState.firstItem = 1;
    this.state.next(newState);
  }

  lastButtonClicked() {
    const newState = this.state.value;
    newState.lastItem = 1;
    this.state.next(newState);
  }
}

Parent component:

export class ParentComponent implements AfterViewInit {

  @ViewChild(ChildComponent, {static: true}) child: ChildComponent;

  ngAfterViewInit(): void {
    this.child.state.subscribe(state => {
      // handle
    });
  }
}
Zepa
  • 142
  • 4
  • I know i can use output decorator. But say, in future i need to emit more event, then i need to declare each event separately again and again. But using ViewChild, i can get the whole component in one line. Please give me a solution of this problem using ViewChild – Nazem Mahmud Piash Jan 11 '20 at 07:37
  • You can create a BehaviorSubject, which will emit new values, when you click on the button, and subscribe to it in the parent component. – Zepa Jan 11 '20 at 07:43
  • I have added an example with that approach – Zepa Jan 11 '20 at 07:49
  • The two approaches differ only in the fact that you cannot use `@Output` with the `BehaviorSubject`, and that `EventEmitter` does not have an initial value. If that is needed, then you shold use Zepa's second method. Given that `EventEmitter` subclasses `Subject`, you can use the state object with an `EventEmitter` and `@Output`, so you only have one. Personally, I would go that route, since it's the one proposed by the framework and is not as tightly coupled. – thomi Jan 11 '20 at 08:07