如何在 ngModel 值更改时使可观察对象重新发出
How to cause observables to re-emit when ngModel value changes
我在我的 HTML 中使用这些可观察对象,但它们不会在我的 HTML 控件的输入值每次发生变化时重新发射(并且它们依赖于这些值,因此它们会出现的同步)。例如,当此更新 ([(ngModel)]="mappedItem.selectedBusinessEntities"
) 时,selectedBusinessEntitiesOptionsAsDtos$ | async
需要重新发出。看到第二个HTML控件的选项是第一个HTML控件的选中选项。我是否需要以某种方式使 [(ngModel)]="mappedItem.selectedBusinessEntities"
值成为可观察值?
HTML:
<cb-selection-list name="selectedBusinessEntities"
[(ngModel)]="mappedItem.selectedBusinessEntities"
[options]="businessEntitiesOptions$ | async"
[readonly]="isView()"
maxHeight="400px"
[slim]="true">
</cb-selection-list>
<cb-select label="Primary Business Entity"
name="primaryBusinessEntity"
[required]="true"
[(ngModel)]="mappedItem.primaryBusinessEntity"
[options]="selectedBusinessEntitiesOptionsAsDtos$ | async"
[readonly]="isView()">
</cb-select>
打字稿:
@Component({
selector: 'cb-user-details',
templateUrl: './user-details.component.html',
styleUrls: ['./user-details.component.scss']
})
export class UserDetailsComponent implements OnInit {
public teamsOptions$: Observable<ITeamDto[]>;
public userRoleTagsOptions$: Observable<ITagDto[]>;
public userRoleTagsOptions: ITagDto[];
public businessEntitiesOptions$: Observable<IBusinessEntityDto[]>;
public isMobileNumberMandatory$: Observable<boolean>;
public selectedBusinessEntitiesOptionsAsDtos$: Observable<IBusinessEntityDto[]>;
public ngOnInit(): void {
this._initSelectOptions();
}
private _initSelectOptions(): void {
this.teamsOptions$ = this.teamsLogicService.$getList();
this.userRoleTagsOptions$ = this.tagsLogicService.$getList();
this.businessEntitiesOptions$ = this.businessEntitiesLogicService
.$getList()
.pipe(
map(businessEntities => {
return orderBy(businessEntities, businessEntity => businessEntity?.name?.toLowerCase());
})
);
this.selectedBusinessEntitiesOptionsAsDtos$ = this.businessEntitiesOptions$.pipe(
map(businessEntities => {
return businessEntities
.filter(businessEntity => includes(
this.mappedItem.selectedBusinessEntities, businessEntity.id)
);
}));
this.isMobileNumberMandatory$ = this.selectedBusinessEntitiesOptionsAsDtos$.pipe(
map(businessEntities => {
const buildingConsultantTag = this.userRoleTagsOptions?.find(
tag => tag.key === USER_TAG_CONSTANTS_CONST.BUILDING_CONSULTANT);
return this.mappedItem?.selectedTags
.some(tag => tag === buildingConsultantTag?.id);
})
);
this.isMobileNumberMandatory$.subscribe();
this.teamsOptions$.subscribe();
this.userRoleTagsOptions$.subscribe(res => this.userRoleTagsOptions = res);
}
编辑:
试过像这样分解 ngModel:
<cb-selection-list name="selectedBusinessEntities"
[ngModel]="mappedItem.selectedBusinessEntities"
(ngModelChange)="selectedBusinessEntitiesChanged($event)"
[options]="businessEntitiesOptions$ | async"
[readonly]="isView()"
maxHeight="400px"
[slim]="true">
</cb-selection-list>
学生:
public selectedBusinessEntitiesChanged(entities: number[]): void {
this.mappedItem.selectedBusinessEntities = entities;
this.businessEntitiesOptions$.subscribe();
this.cdRef.detectChanges();
}
令人恼火的是,这确实导致 this.selectedBusinessEntitiesOptionsAsDtos$
变为 运行 并发出新列表(使用调试器检查)。但是 UI 没有更新。这就是为什么我添加了 this.cdRef.detectChanges();
但它不起作用的原因。
您可以使用 ngModelChange 事件:
(ngModelChange)="handleNgModelChangedEvent($event)
每次ngModel发生变化时都会触发。
$event 负载将保存表单字段的当前值。
更新:
另外,不确定它是否有帮助,但你不应该在私有函数中定义你的可观察对象,而是在你声明它们的地方,作为 class 成员:
例如:
businessEntitiesOptions$ = this.businessEntitiesLogicService.getList()
.pipe(
map(businessEntities => {
orderBy(businessEntities, businessEntity => businessEntity?.name?.toLowerCase());
})
);
而不是:
public businessEntitiesOptions$: Observable<IBusinessEntityDto[]>;
同样适用于所有可观察值...
您甚至应该能够在此 class.
中使用 OnPush 更改检测策略
您可以尝试在您的管道 observable 中添加 tap(x => console.log(x))
以查看它们是否按预期工作...
例如:
.pipe(
tap(x => console.log(x)),
map(businessEntities => {
orderBy(businessEntities, businessEntity => businessEntity?.name?.toLowerCase()),
tap(x => console.log(x))
})
您可能还想删除 returns...除非您正在捕获错误或其他东西,否则您不想 return 任何东西。 Observables 只是流。
我在我的 HTML 中使用这些可观察对象,但它们不会在我的 HTML 控件的输入值每次发生变化时重新发射(并且它们依赖于这些值,因此它们会出现的同步)。例如,当此更新 ([(ngModel)]="mappedItem.selectedBusinessEntities"
) 时,selectedBusinessEntitiesOptionsAsDtos$ | async
需要重新发出。看到第二个HTML控件的选项是第一个HTML控件的选中选项。我是否需要以某种方式使 [(ngModel)]="mappedItem.selectedBusinessEntities"
值成为可观察值?
HTML:
<cb-selection-list name="selectedBusinessEntities"
[(ngModel)]="mappedItem.selectedBusinessEntities"
[options]="businessEntitiesOptions$ | async"
[readonly]="isView()"
maxHeight="400px"
[slim]="true">
</cb-selection-list>
<cb-select label="Primary Business Entity"
name="primaryBusinessEntity"
[required]="true"
[(ngModel)]="mappedItem.primaryBusinessEntity"
[options]="selectedBusinessEntitiesOptionsAsDtos$ | async"
[readonly]="isView()">
</cb-select>
打字稿:
@Component({
selector: 'cb-user-details',
templateUrl: './user-details.component.html',
styleUrls: ['./user-details.component.scss']
})
export class UserDetailsComponent implements OnInit {
public teamsOptions$: Observable<ITeamDto[]>;
public userRoleTagsOptions$: Observable<ITagDto[]>;
public userRoleTagsOptions: ITagDto[];
public businessEntitiesOptions$: Observable<IBusinessEntityDto[]>;
public isMobileNumberMandatory$: Observable<boolean>;
public selectedBusinessEntitiesOptionsAsDtos$: Observable<IBusinessEntityDto[]>;
public ngOnInit(): void {
this._initSelectOptions();
}
private _initSelectOptions(): void {
this.teamsOptions$ = this.teamsLogicService.$getList();
this.userRoleTagsOptions$ = this.tagsLogicService.$getList();
this.businessEntitiesOptions$ = this.businessEntitiesLogicService
.$getList()
.pipe(
map(businessEntities => {
return orderBy(businessEntities, businessEntity => businessEntity?.name?.toLowerCase());
})
);
this.selectedBusinessEntitiesOptionsAsDtos$ = this.businessEntitiesOptions$.pipe(
map(businessEntities => {
return businessEntities
.filter(businessEntity => includes(
this.mappedItem.selectedBusinessEntities, businessEntity.id)
);
}));
this.isMobileNumberMandatory$ = this.selectedBusinessEntitiesOptionsAsDtos$.pipe(
map(businessEntities => {
const buildingConsultantTag = this.userRoleTagsOptions?.find(
tag => tag.key === USER_TAG_CONSTANTS_CONST.BUILDING_CONSULTANT);
return this.mappedItem?.selectedTags
.some(tag => tag === buildingConsultantTag?.id);
})
);
this.isMobileNumberMandatory$.subscribe();
this.teamsOptions$.subscribe();
this.userRoleTagsOptions$.subscribe(res => this.userRoleTagsOptions = res);
}
编辑:
试过像这样分解 ngModel:
<cb-selection-list name="selectedBusinessEntities"
[ngModel]="mappedItem.selectedBusinessEntities"
(ngModelChange)="selectedBusinessEntitiesChanged($event)"
[options]="businessEntitiesOptions$ | async"
[readonly]="isView()"
maxHeight="400px"
[slim]="true">
</cb-selection-list>
学生:
public selectedBusinessEntitiesChanged(entities: number[]): void {
this.mappedItem.selectedBusinessEntities = entities;
this.businessEntitiesOptions$.subscribe();
this.cdRef.detectChanges();
}
令人恼火的是,这确实导致 this.selectedBusinessEntitiesOptionsAsDtos$
变为 运行 并发出新列表(使用调试器检查)。但是 UI 没有更新。这就是为什么我添加了 this.cdRef.detectChanges();
但它不起作用的原因。
您可以使用 ngModelChange 事件:
(ngModelChange)="handleNgModelChangedEvent($event)
每次ngModel发生变化时都会触发。 $event 负载将保存表单字段的当前值。
更新:
另外,不确定它是否有帮助,但你不应该在私有函数中定义你的可观察对象,而是在你声明它们的地方,作为 class 成员:
例如:
businessEntitiesOptions$ = this.businessEntitiesLogicService.getList()
.pipe(
map(businessEntities => {
orderBy(businessEntities, businessEntity => businessEntity?.name?.toLowerCase());
})
);
而不是:
public businessEntitiesOptions$: Observable<IBusinessEntityDto[]>;
同样适用于所有可观察值...
您甚至应该能够在此 class.
中使用 OnPush 更改检测策略您可以尝试在您的管道 observable 中添加 tap(x => console.log(x))
以查看它们是否按预期工作...
例如:
.pipe(
tap(x => console.log(x)),
map(businessEntities => {
orderBy(businessEntities, businessEntity => businessEntity?.name?.toLowerCase()),
tap(x => console.log(x))
})
您可能还想删除 returns...除非您正在捕获错误或其他东西,否则您不想 return 任何东西。 Observables 只是流。