3

I'm using Angular 8.

I have a child component with one @Input() property. This input has to be modified before binding and thus using method to return the data for binding like

<app-child [info]="getInfo()"></app-child>

And in the parent component, the getInfo() returns value like

getInfo(): any|null {
  console.log('called...');

  if (this.data) {
    return JSON.parse(this.data);
  }

  return null;
}

But this way, the method is called again and again whenever some event occurs in the child component.

Stackblitz example: https://stackblitz.com/edit/angular-child-com

Produce issue:

The form is rendered from the child component which accepts input from a method as described above.

Click on any button or input field and you can see the console log printing string from the method call with each event.

This is resulting in multiple times OnChange event trigger in the child components.

3
  • 1
    That's expected. Angular has no idea of what getInfo() might return. So it calls it at each detection change. And you always create and return a new object, so it binds that new object every time. Parse the data once and for all, store the result in a field, and pass that field as input. Commented Jan 10, 2020 at 17:59
  • 1
    Angular's default change detection strategy checks for all the @Input bound properties in every change detection cycle. So avoid calling methods while binding the value to @Input property Commented Jan 10, 2020 at 18:23
  • You can also use changeDetection: [onPush] and invoke change detection explicitly. Commented Jul 30, 2021 at 13:36

2 Answers 2

2

You should retrieve the data and store it in a property in the parent component that you then pass to data binding with the child component. Angular will take care of the change detection

@Component({
  selector: 'app-parent',
  template: '<app-child [info]="info"></app-child>',
})
export class ParentComponent implements OnInit {
  info;

  constructor(private service: SomeDataService) {}

  ngOnInit() {
    this.info = this.service.getData();
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

But what if you need a parameter? So you have an array which needs to be filtered based on another array's selected value, and these arrays are dynamically created. So this.info(arrVal, itemId);
That can be done in a setter or using an Angular Pipe. Another option is an observable which I would recommend anyways for most data glue code in an Angular application.
1

Go stateless with async pipes. I think this is closer to what you want to do anyway. I use async pipes wherever possible because its cleaner than using properties for everything -- less unintended consequences too.

@Component({
  selector: 'app-parent',
  template: '<app-child [info]="info$ | async"></app-child>',
})
export class ParentComponent implements OnInit {
  info$ = this.service.getData().pipe(
    map(data => JSON.parse(data))
  );

  constructor(private service: MyService) { }
}

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.