To write to a page in this book, a cascade query is required
The current building belongs to the service area, so we have selected the service area to continue according to the selected service area, that is to say, if we have selected the service area 1, and then choose the building 1, if we change the selected service area at this time The selected buildings should be removed.
If written according to the previous logic, as long as the corresponding building is obtained after passing in the new service area to change the selection list item, the selected content will be automatically reset to empty, as shown below:
@Input()
set serviceAreaId(serviceId: number) {
this.buildingId.setValue(null);
if (Number.isInteger(serviceId)) {
this.listOfOption = [];
this.serviceAreaService.getAllBuildingByServiceAreaId(serviceId)
.subscribe(buildings => {
this.buildings = buildings;
buildings.forEach(building => {
this.listOfOption.push(
{
label: building.name,
value: building.id
}
)
})
})
} else {
this.listOfOption = [];
}
}
But this doesn't work in <thy-custom-select>, which means that the selected item is not reset or deleted even if the list has been updated. This obviously does not meet our needs, so the first thing to do is to find out whether there is an API in the official documentation that gives the option to call clearing.
thy-custom-select official website
But unfortunately, there is no API for clearing options, so we can only find other ways.
At first, it was thought that the child component was not re-rendered in time. Later, I searched for these keywords and tried to re-render it after it obtained the data, but it did not work.
constructor(private serviceAreaService: BuildingService,
private cdr: ChangeDetectorRef) {
}
this.serviceAreaService.getAllBuildingByServiceAreaId(serviceId)
.subscribe(buildings => {
this.buildings = buildings;
buildings.forEach(building => {
this.listOfOption.push(
{
label: building.name,
value: building.id
}
)
})
//数据绑定变更的时候用ChangeDetectorRef驱动angular更新视图
//当输入已更改或视图中发生了事件时,组件通常会标记为脏的(需要重新渲染)。调用此方法会确保即使那些触发器没有被触发,也仍然检查该组件
this.cdr.markForCheck();
//从该组件到各个子组件执行一次变化检测 检查该视图及其子视图
this.cdr.detectChanges();
})
Then try to change to angular's native selection component. Although the desired effect can be achieved, the style is different from <thy-custom-select>, and the style is not uniform.
<select [formControl]="buildingId" class="form-control">
<option [ngValue]="null">请选择</option>
<option *ngFor="let building of buildings" [ngValue]="building.id">
{{building.name}}
</option>
</select>
Later, I thought of a simpler and more direct method. The subcomponent is judged according to *ngif whether to call or not, as long as its display is canceled when the service area selection changes (that is, the current subcomponent is destroyed) and restarted after a short delay. Let it display (that is, create a new sub-component according to the service area) to achieve the effect we want;
ngOninit(): void {
. . .
this.queryForm.get(this.formKeys.serviceArea)?.valueChanges
.subscribe(
()=> {
this.resetChildForm();
}
)
}
resetChildForm(){
this.showBuildingSelect= false;
setTimeout(() => {
this.showBuildingSelect = true
}, 10);
}
<div class="ml-10" *ngIf="queryForm.get(formKeys.serviceArea)?.value && showBuildingSelect">
<app-building-select [serviceAreaId]="queryForm.get(formKeys.serviceArea)?.value" [formControlName]="formKeys.buildingId"></app-building-select>
</div>
However, the disadvantage of this way of writing is that the readability is not high, and the integration is not high. Even after calling the selection component, these are needed to be used together, but considering that there are not many places to use this query method, this solution is acceptable.
second question
Use scene = "building new
As shown in the figure, the main problem we need to solve is how to solve the value transfer of the sub-project, and the first is how to complete the transfer of the selection box selection.
Here we directly obtain all the branch project templates and let the user choose, that is to say, the number of entries is not fixed, so we cannot use the familiar form to pass values. Even if this problem is solved, it still needs to consider whether the choice of its attached multi-structure can be passed on.
The processing of the selection box refers to the solution of the previous project: each selection box is a component, and the selection box is another component. By adding the "selected" attribute to the foreground entity, the submission is selected at the time of submission. branch engineering.
Single choice:
Parent component:
<app-check-single [checked]="divisionalWorksTemplate._checked"
(beChange)="onSingleChange($event,divisionalWorksTemplate)">
</app-check-single>
onSingleChange($event: boolean, divisionalWorksTemplate: DivisionalWorksTemplate) {
divisionalWorksTemplate._checked = $event;
//用于通知全选组件选项的变化
this.singleCheckboxChangeSubject.next();
}
Subassembly:
@Input()
set checked(checked: boolean) {
this.formControl.setValue(checked);
}
@Output()
beChange = new EventEmitter<boolean>();
formControl = new FormControl(false);
constructor() {
}
ngOnInit(): void {
this.formControl.valueChanges
.subscribe(value => {
this.beChange.emit(value);
});
}
After understanding the function of each parameter, the whole component is easy to understand. When we click to select, the child component detects the change of the formcontrol and will eject the value of the form to the parent component. The parent component receives the ejection and will change the branch project in the list _checked
field is the catapult value to let the parent component know that the item is checked/unchecked.
And notify the select-all component to change its state in time, that is, if we select all options, the select-all should be automatically selected, otherwise the select-all should not be selected.
For the select-all component, it is the same as the single-select principle, except that the select-all component passes in all sub-projects instead of one, so as to control the state of all radio buttons. The description will not be expanded here, and these components were later changed to instructions in the original project, and detailed explanations will be made after understanding the instructions.
The problem we have to solve is how to choose multiple structures. Obviously, formcomtrol cannot be used for binding, so we still adopt the processing method of single-choice components - adding the following types to the foreground entity to obtain the selection situation .
/**
* 所选择的分部工程类型
*/
_checkedDivisionalWorksType: DivisionalWorksType[]
The processing method is the same as that of the radio component. When the option value changes, the value will pop up, and the selected option will be added to _checkedDivisionalWorksType
.
After that, it is worth mentioning the processing at the time of submission:
The problem that needs to be solved here is that we can also see from the above picture that some branch projects do not have a multi-structure, they actually have a fixed multi-structure, and the structure is the default. But we ignore them when selecting them, which makes it obviously unreasonable to process them completely according to _checkedDivisionalWorksType, so we need to further filter the selected subdivision projects so that they can be processed normally. There is no multi-structure structural engineering and when we do not have a multi-structured structural engineering option but this is selected.
onSubmit(formGroup: FormGroup, divisionalWorksTemplates: DivisionalWorksTemplate[]) {
//是否允许被提交,默认为true
let permit = true;
//所要提交的分部工程列表
const checkedDivisionalWorksTemplates = [] as DivisionalWorksTemplate[];
divisionalWorksTemplates.forEach((value) => {
if(value._checked) {
let checkedDivisionalWorksTemplate = {} as DivisionalWorksTemplate;
checkedDivisionalWorksTemplate.id = value.id;
if(value._checkedDivisionalWorksType && value._checkedDivisionalWorksType?.length !=0) {
//当选择项不为空时将选择项赋给我们要提交的结构工程
checkedDivisionalWorksTemplate.divisionalWorksType = value._checkedDivisionalWorksType;
} else if(value.divisionalWorksType[0].structure === STRUCTURE.default.value && value.divisionalWorksType?.length === 1){
//当选择项目为空但是其选择总项目仅有一条并且为默认项时将总项赋给我们要提交的结构工程
checkedDivisionalWorksTemplate.divisionalWorksType = value.divisionalWorksType;
} else {
//其余情况则为选择项目为空但是总项有多个,弹出错误指导用户为其配置具体结构
this.notifyService.error('新增失败', "请为分部工程" + value.name +"选择具体结构");
//不允许提交
permit = false;
}
//将检查合格项加入所要提交的分部工程列表中
checkedDivisionalWorksTemplates.push(checkedDivisionalWorksTemplate);
}
})
if(permit) {
const newBuilding = {} as Building;
const serviceArea = {
id: formGroup.get(this.formKeys.serviceArea)?.value
} as ServiceArea
newBuilding.name = formGroup.get(this.formKeys.name)?.value;
newBuilding.serviceArea = serviceArea;
newBuilding.divisionalWorksTemplate = checkedDivisionalWorksTemplates;
this.buildingService.save(newBuilding)
.subscribe((value) => {
this.notifyService.success("新增成功");
this.isFinish.emit(value);
}, (error) => {
this.notifyService.error("新增失败", error);
})
}
}
The main idea of these methods is to pass the value through the foreground entity itself, so that we can enter the above form without the need for form binding.
However, although the above method can achieve the desired effect, because it is not bound to formcontrol, we can only check whether the submission conditions are met when submitting. If not, we will give corresponding prompts, and what we want is to enter with other information. In the same way, you can know whether the submitted information is qualified and give a prompt before submitting, and the submit button is disabled.
Changes still need to be made after the project is basically completed.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。