angular - Ger @Input en tvåvägsbindning?


Sättet jag trodde att @Input fungerade skulle direkt ge ett stort "Nej!" på den här frågan. Men idag snubblade jag över något konstigt beteende, eller kanske har jag alltid tänkt fel om @Input..

Jag har gjort en stackblitz för att visa problemet. Detta händer i stackblitz för Angular 7.0.1, men i mitt lokala projekt händer det också på Angular 6.1.2.

Stackblitz visar en enkel föräldrakomponent som har ett objekt. Objektet skickas till en barnkomponent via @Input. Både barn- och föräldrakomponenten har en funktion som ändrar objektet. De visar också båda objektets värde i mallen.

Jag förväntade mig att se att när föräldern ändrar objektet kommer det att ändras i barnet. Jag förväntade mig dock inte att när barnet ändrar objektet, ändras det även för föräldern. Stackblitz visar dock detta beteende. Jag har alltid trott att man uttryckligen skulle behöva sända en händelse via @Output för att skapa ett flöde till föräldern och ändra det där från barnkomponenten.

Svar #1

Svaret är "nej". I ditt exempel är värdet som du skickar till @Input-egenskapen en referens till ett objekt. Om du hade tvåvägsbindning skulle du kunna tilldela ett nytt objekt till den egenskapen i barnkomponenten:

this.thing = { name: "world", nbm: 10 };

och den motsvarande egenskapen i föräldrakomponenten skulle uppdateras därefter. Det är inte fallet, som du kan se i denna stackblitz.

Men eftersom föräldra- och barnkomponenterna har en referens till samma objekt, kan de båda ändra en av dess egenskaper, och den förändringen kommer att observeras i den andra komponenten.


För att implementera tvåvägsbindning kan du lägga till en @Output-egenskap med samma namn följt av Change, och sända händelsen när ändringen sker:

@Input() thing: any;
@Output() thingChange = new EventEmitter();

setNewObject(){
  this.thing = { name: "world", nmb: 10 };
  this.thingChange.emit(this.thing);
}

Ändringen kommer då att återspeglas i föräldrakomponenten om tvåvägsbindningssyntaxen används:

<child2 [(thing)]="thing"></child2>

Se denna stackblitz för en demo.


Om du vill förhindra att barnkomponenten ändrar det ursprungliga objektet bör du binda objektets egenskaper istället för själva objektet:

@Input() thingName: string;
@Input() thingNmb: number;
<child [thingName]="thing.name" [thingNmb]="thing.nmb"></child>

Svar #2

Ja, Angular Change Detection flödar från parent till child, men det du upplever är inte relaterat till Object reference. Eftersom det delade objektet i parent och child pekar på samma referens kommer det att uppdateras i båda komponenterna.

När du gör någon ändring genom barnkomponenten kommer det delade objektet också att ändras i föräldrakomponenten och Angular kommer att upptäcka ändringar och utlösa uppdatering av användargränssnittet.

Men det är inte samma sak med primitiv datatyp. Det du har tänkt är sant för primitiv datatyp. I fallet med primitiv datatyp flyttar ändringar från förälder till barn, inte tvärtom.

Här är demot för samma - https://stackblitz.com/edit/angular-1wusrc

Relaterade artiklar