0

I asked a question: Angular controlling a list of objects visibility on the same view which was answered but has raised another question.

If I have a child component, how can I get it to know when something has changed. Here is a link to some example code:

https://stackblitz.com/edit/angular-e72hx9?file=src%2Fapp%2Fanswers%2Fanswers.component.ts

I have tried to use OnChanges on the AnswerComponent. I was hoping that when I changeda property within the object array, OnChanges would fire and then update my list, but it doesn't. I assume this is an easy thing, but I am unsure how to do it.

Can anyone help?

r3plica
  • 13,017
  • 23
  • 128
  • 290
  • ngOnChanges is meant for when the item itself changed. If you want to filter based on one of the input's attributes I would suggest creating a custom pipe and passing the questions – jgerstle Nov 18 '18 at 14:25
  • A pipe does not work and as the previous answer for the last question, it was stated that: 'The Angular team and many experienced Angular developers strongly recommend moving filtering and sorting logic into the component itself. The component can expose a filteredHeroes or sortedHeroes property and take control over when and how often to execute the supporting logic. Any capabilities that you would have put in a pipe and shared across the app can be written in a filtering/sorting service and injected into the component.' – r3plica Nov 18 '18 at 14:33
  • I'm pretty sure your answer is just as inneficient as using a pipe. If you want to do it that way then you should send up to the parent that the active flag is getting changed so it can correctly send the data down to the children. You may also want to consider using something like ngrx, which can handle stuff like this – jgerstle Nov 18 '18 at 14:47

1 Answers1

3

You can probably use @Output and only one questions collection.

In app.component template

<app-questions [questions]="questions" (setActive)="setActive($event)"></app-questions>
<app-answers [questions]="questions"></app-answers>

In app.component class

setActive(item) {
  this.questions.map(question => {
    if(question.text === item.text) {
      question.active = !question.active;
    }
 });

}

The answer component

export class AnswersComponent {
  @Input() questions: Question[]
  constructor() { }
}

The answer template

 <h2>Answers</h2>
 <div *ngIf="questions">
   <div *ngFor="let question of questions">
      {{ question.active }} <!-- check how this will change true/false -->
     <ul>
       <li *ngFor="let answer of question.answers">{{ answer.text }}</li>
     </ul>

The question component

export class QuestionsComponent {
 @Input() questions: Question[]
 @Output() setActive = new EventEmitter();
}

The question template

<h1>Questions</h1>
<ul>
  <li *ngFor="let question of questions"><button (click)="setActive.emit(question)">{{ question.text }}</button> (active: {{ question.active }})</li>
</ul>

Hope that will help!

https://stackblitz.com/edit/angular-mq48s2

freepowder
  • 440
  • 2
  • 6
  • I forked your answer: https://stackblitz.com/edit/angular-pelfw9?file=src%2Fapp%2Fanswers%2Fanswers.component.html which I think is better than your solution because it does the filter now – r3plica Nov 18 '18 at 15:22
  • Glad that you have it working! :) I believe *ngIf="question.active" in the
      will produce your desired output (no other changes required) ...but if you like to emit the event from the service I guess is ok , added *ngIf to stackblitz in case you wanna checkout. Cheers
    – freepowder Nov 18 '18 at 15:33
  • Yeah, the `ngIf` works fine for this scenario, but (as usual) the example I have given is based on a much more complicated list, which requires the list presented to the template to have **only** the items that should be displayed :) – r3plica Nov 18 '18 at 15:37