I've run into a couple weird change detection issues recently with Angular 7.1 where some components are only partially updating.
I am working on a decision tree for example. The decision tree component determines which decision is current, then passes it to a decision component. This works as expected for the first decision, but on the second step it does update the question, but the bound values in the decision component are not updated.
decision-tree.component.ts
private tree: any[] = [
{
id: 'understand_story',
question: 'Do you understand the intended functionality?',
type: 'boolean',
yes: [
{
action: 'go-to',
params: {decision: 'is_story_possible'}
}
],
no: [
{
action: 'update-story',
params: {blocked: true}
}
]
},
{
id: 'is_story_possible',
question: 'Is this feature going to be possible?',
type: 'boolean',
yes: [
{
action: 'go-to',
params: {decision: 'estimate_story'}
}
],
no: [
{
action: 'update-story',
params: {blocked: true}
}
]
},
{
id: 'estimate_story',
question: 'Enter an estimate to complete this story',
input: 'estimate',
type: 'text',
change: [
{
action: 'input-value',
params: {field: 'estimate'}
},
{
action: 'move-story',
params: {board: 'develop'}
}
]
}
];
private index = 'understand_story';
get currentDecision() {
return this.tree.find(d => d.id === this.index);
}
makeDecision(answer) {
this.decisions.push(answer);
const decision = this.tree.find(d => d.id === answer.id);
switch (decision.type) {
case 'boolean':
const actions = answer.answer ? decision.yes : decision.no;
this.processActions(actions);
break;
case 'input':
const actions = answer.change;
this.processActions(actions);
break;
}
}
processActions(actions) {
actions.forEach(action => {
switch (action.action) {
case 'go-to':
this.index = action.params.decision;
break;
}
});
}
decision-tree.component.html
<app-decision [decision]="currentDecision" (complete)="makeDecision($event)"></app-decision>
When the current decision index is updated the question updates as expected, but the bound values do not. These values are private properties of the decision.component.ts
file, so it looks like angular is recycling the component instance.
decision.component.ts
@Input() decision;
@Input() disabled: boolean;
@Input() data;
@Output() complete: EventEmitter<any> = new EventEmitter();
private answer = null;
private comment: string;
decision.component.html
<div class="decision">
<p class="question">{{decision.question}}</p>
<div class="answer">
<mat-radio-group aria-label="Select an option" [(ngModel)]="answer" [disabled]="disabled">
<mat-radio-button [value]="true">Yes</mat-radio-button>
<mat-radio-button [value]="false">No</mat-radio-button>
</mat-radio-group>
<mat-form-field appearance="outline">
<textarea matInput placeholder="Add a comment" [(ngModel)]="comment" [disabled]="disabled"></textarea>
</mat-form-field>
<button *ngIf="! disabled" mat-flat-button color="primary" (click)="completeDecision()" [disabled]="! valid">Go to next step</button>
</div>
</div>
Here's a quick screencast of the issue in action: https://www.loom.com/share/301dad2b15294f73892db0788d43b337