Dalam proyek Angular besar, Anda sering dapat melihat perilaku berulang dalam komponen. Sebaiknya pindahkan perilaku ini keluar dari komponen ke dalam kelas terpisah yang dapat digunakan kembali. Saya akan mempertimbangkan dua kasus yang cukup populer: beralih dan pilihan ganda entitas.
Kasus 1: Beralih
Seringkali dalam kode sumber Anda melihat sesuatu seperti ini:
export class SampleComponent {
@Output somethingSelected = new EventEmitter<boolean>()
...
private _selected = false;
toggleSelected() {
this._selected = !this._selected;
this.somethingSelected.emit(this._selected);
}
}
atau seperti ini:
export class SampleComponent {
@Output somethingSelected = new EventEmitter<boolean>()
...
private _selected$ = new BehaviorSubject<boolean>(false);
toggleSelected() {
this._selected$.next(!this._selected$.value);
this.somethingSelected.emit(this._selected$.value);
}
}
, , . , , DRY. .
BehavoirSubject toggle()
export class ToggleSubject extends BehaviorSubject<boolean> {
toogle() {
this.next(!this.value);
}
}
:
export class SampleComponent {
@Output somethingSelected = new EventEmitter<boolean>()
...
private _selected$ = new ToggleSubject(false);
toggleSelected() {
this._selected$.toggle();
this.somethingSelected.emit(this._selected$.value);
}
}
, . toggleSelected _selected. ToggleSwitcher EventEmitter
export class ToggleSwitcher extends EventEmitter<boolean> {
get value(): boolean {
return this._value
}
constructor(private _value = false) {
super();
}
toggle() {
this.emit(!this.value);
}
emit(v: boolean) {
this._value = v;
super.emit(v);
}
}
:
export class SampleComponent {
@Output somethingSelected = new ToggleSwitcher()
...
}
somethingSelected.toggle() somethingSelected.value somethingSelected.emit(true / false). true, ToggleSwitcher. EventEmitter, .
@Output somethingSelected = new ToggleSwitcher(true)
: , . , SRP. EventEmitter , . , . EventEmitter, .
export class ToggleSwitcher extends BehaviorSubject<boolean> {
eventEmitter = new EventEmitter<boolean>();
next(v: boolean) {
this.eventEmitter.emit(v);
super.next(v);
}
toggle() {
this.next(!this.value)
}
}
,
export class SampleComponent {
somethingSwitcher = new ToggleSwitcher(false);
@Output somethingSelected = this.somethingSwitcher.eventEmitter;
}
2:
: , , , , . Output() .
ngFor . *ngFor , , : /
export class EntityCheckedState<T> {
entity: T;
checked: boolean
}
export class EntityMultiSelector<T> extends BehaviorSubject<T[]> {
private _list: EntityCheckedState<T>[];
eventEmitter = new EventEmitter<T[]>();
get list(): EntityCheckedState<T>[] {
return this._list;
}
set list(v: EntityCheckedState<T>[]) {
this._list = v;
this.next(this.list.filter(({checked}) => checked).map(({entity}) => entity));
}
constructor(v: T[], defaultChecked = false) {
super(defaultChecked? v : []);
this.eventEmitter.emit(defaultChecked? v : []);
this._list = v.map(entity => ({entity, checked: defaultChecked}));
}
setCheckedForEntity(entity: T, checked: boolean) {
this.list = this.list.map(v => (v.entity === entity ? { ...v, checked } : v));
}
setCheckedForAll(checked: boolean) {
this.list = this.list.map(v => ({...v, checked}));
}
next(v: T[]) {
this.eventEmitter.emit(v);
super.next(v);
}
}
:
export class SampleComponent {
@Input() set data(v: SampleDto[]) {
this.multiSelector = new EntityMultiSelector<SampleDto>(v);
this.selectedSamples = this.multiSelector.eventEmitter;
}
multiSelector: EntityMultiSelector<SampleDto>;
@Output() selectedSamples: EventEmitter<SampleDto[]>
}
:
<app-sample-entity *ngFor = "let state of multiSelector.list"
[data] = "state.entity"
[checked] = "state.checked"
(checked) = "multiSelector.setCheckedForEntity(state.entity, $event)"
></app-sample-entity>
: {{multiSelector.list.length}} : {{multiSelector.value.lenght}}
<button (click) = "multiSelector.setSelectedForAll(false)"></button>
:
https://stackblitz.com/edit/angular-ivy-kyaeac?file=src/app/app.component.html
.
Saya akan senang mendapatkan ide Anda di komentar. Kritik yang membangun didorong.