typescript - Angular Signals: Hur man destrukturerar modell
Jag har två Angular (v19) komponenter.
Jag vill ha en instans av ComponentB inuti mallen av ComponentA. ComponentB.parameters bör bindas till de individuella fälten i ComponentA på ett sådant sätt att en ändring av ComponentB.parameters uppdaterar de individuella fälten i ComponentA och en uppdatering av något av fälten i ComponentA bör uppdatera ComponentB.parameters.
Hur gör man detta?
a.component.ts
@Component({
selector: 'app-a',
templateUrl: './a.component.html',
standalone: true,
imports: [ComponentB]
})
export class ComponentA {
public readonly filter = model<string>();
public readonly orderby = model<string>();
public readonly skip = model<number>();
public readonly top = model<number>();
}
b.component.ts
@Component({
selector: 'app-b',
standalone: true,
})
export class ComponentB {
public readonly parameters = model<Parameters>()
}
parameters.ts
export interface Parameters {
filter?: string;
orderby?: string;
skip?: string;
top?: string;
}
a.component.html
<app-b [(parameters)]="???"></app-b>
Svar #1
Vi kan uppdatera parametrarna för att innehålla de individuella signalerna, så att du kan binda dem till [(ngModel)] inne i barnkomponenten.
export interface Parameters {
filter: ModelSignal<string>;
orderby: ModelSignal<string>;
skip: ModelSignal<number>;
top: ModelSignal<number>;
}
Då kräver vi inte att barnkomponenten accepterar en model-signal, utan istället en input.required-signal. Jag använder required eftersom jag annars måste omsluta varje element som använder den interna signalen som filter inom ett if-villkor, eftersom den kan vara undefined, så input.required är enklare, men låt mig veta om du vill veta hur den koden skulle se ut.
@Component({
selector: 'app-b',
standalone: true,
imports: [FormsModule],
template: ` <br/><br/><br/>
Child<br/>
<input [(ngModel)]="parameters().filter"/><br/>
<input [(ngModel)]="parameters().orderby"/><br/>
<input [(ngModel)]="parameters().skip"/><br/>
<input [(ngModel)]="parameters().top"/><br/>
`,
})
export class ComponentB {
public readonly parameters = input.required<Parameters>();
}
Nu kan vi helt enkelt skicka ett objekt som heter parameters som innehåller alla individuella signaler till barnet, och därmed har vi tvåvägsbindning mellan föräldra- och barnkomponenten.
@Component({
selector: 'app-root',
imports: [ComponentB, FormsModule],
template: `
Parent<br/>
<input [(ngModel)]="filter"/><br/>
<input [(ngModel)]="orderby"/><br/>
<input [(ngModel)]="skip"/><br/>
<input [(ngModel)]="top"/><br/>
<br/><br/><br/>
<app-b [parameters]="parameters"></app-b>
`,
})
export class App {
public readonly filter = model<string>('');
public readonly orderby = model<string>('');
public readonly skip = model<number>(1);
public readonly top = model<number>(2);
parameters: Parameters = {
filter: this.filter,
orderby: this.orderby,
skip: this.skip,
top: this.top,
};
}
Fullständig kod:
import { Component, ModelSignal, input, model } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
export interface Parameters {
filter: ModelSignal<string>;
orderby: ModelSignal<string>;
skip: ModelSignal<number>;
top: ModelSignal<number>;
}
@Component({
selector: 'app-b',
standalone: true,
imports: [FormsModule],
template: ` <br/><br/><br/>
Child<br/>
<input [(ngModel)]="parameters().filter"/><br/>
<input [(ngModel)]="parameters().orderby"/><br/>
<input [(ngModel)]="parameters().skip"/><br/>
<input [(ngModel)]="parameters().top"/><br/>
`,
})
export class ComponentB {
public readonly parameters = input.required<Parameters>();
}
@Component({
selector: 'app-root',
imports: [ComponentB, FormsModule],
template: `
Parent<br/>
<input [(ngModel)]="filter"/><br/>
<input [(ngModel)]="orderby"/><br/>
<input [(ngModel)]="skip"/><br/>
<input [(ngModel)]="top"/><br/>
<br/><br/><br/>
<app-b [parameters]="parameters"></app-b>
`,
})
export class App {
public readonly filter = model<string>('');
public readonly orderby = model<string>('');
public readonly skip = model<number>(1);
public readonly top = model<number>(2);
parameters: Parameters = {
filter: this.filter,
orderby: this.orderby,
skip: this.skip,
top: this.top,
};
}
bootstrapApplication(App);
Stackblitz-demonstration
Svar #2
Att använda en beräknad i en A-komponent för att bygga parametrar och dela dubbelriktad bindning för att destrukturera parameterutgång.
a.component.ts
@Component({
selector: 'app-a',
templateUrl: './a.component.html',
standalone: true,
imports: [ComponentB]
})
export class ComponentA {
public readonly filter = model<string>();
public readonly orderby = model<string>();
public readonly skip = model<number>();
public readonly top = model<number>();
protected parameters= computed(() => ({filter : filters(), orderby : orderby(), skip :skip(), top :top()}));
protected onParametersChange(params: Parameters | undefined) {
if (params) {
filter.set(params.filter);
orderby .set(params.orderby);
skip .set(params.skip);
top .set(params.top);
}
}
}
b.component.ts
@Component({
selector: 'app-b',
standalone: true,
})
export class ComponentB {
public readonly parameters = model<Parameters>()
}
parameters.ts
export interface Parameters {
filter?: string;
orderby?: string;
skip?: string;
top?: string;
}
a.component.html
<app-b [parameters]="parameters()" (parametersChange)="onParametersChange($event)"></app-b>
Obs: fungerar eftersom filter, orderby, skip och top är primitiva, om det finns objekt, behöver du ändra lika signal-funktionen.
