Replenish
Here is a little addition to the above asynchronous validator anti-shake.
https://segmentfault.com/a/1190000041621553
vehicleBrandNameNotExist(): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
if (control.value === '') {
return of(null);
}
return timer(this.debounceTime).pipe(
// 调用服务, 获取结果
switchMap(() => this.vehicleBrandService.existByName(control.value)),
// 对结果进行处理,null表示正确,对象表示错误
map((exists: boolean) => (exists ? {vehicleBrandNameExist: true} : null)),
)
};
}
As mentioned at the end of the above, it is found that this way of writing can be done. After the teacher's judgment, it is found that the key to several writing methods lies in a question:
Does fromControl unsubscribe from the previous data source every time it gets a new value?
This kind of problem is difficult to find from the source code, so I searched for relevant information:
Find an article that also describes this approach.
As shown in the figure below, time().pipe is also used, and he explained,
When there is a new control value, fromControl unsubscribes from the previous observable that was waiting to complete, so the old observable doesn't emit the value.
I drew a diagram to illustrate:
Looking at the picture, you can see that fromControl cancels the subscription to the old value, and the old observable does not emit the value.
simplify
After understanding this truth, the anti-shake can be simplified into this way of writing:
vehicleTypeNameIsAvailable(VehicleTypeId?: number): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
if (control.value === '') {
return of(null);
}
return of(null).pipe(
delay(1000),
// 调用服务, 获取结果
switchMap(() => this.vehicleTypeService.nameIsAvailable(control.value, VehicleTypeId)),
// 对结果进行处理,null表示正确,对象表示错误
map((available: boolean) => (available ? null : {vehicleTypeNameNotAvailable: true})),
)
};
}
Return null directly as an observable. If delay(1000) passes, call the service service.
Tested and executed well and as expected.
Problems encountered in the background
The reason is this:
That's it, I added the Ding field to the user entity as a DingTalk that pushes all client information.
But when I log in, I find that as long as the logged in user has this field, the background will report an error
The error is as follows:
Reported HTTP, and stack overflow and other errors. I can't understand it, so I go to Google:
I searched a lot, most of them said that because of adding many to One, many to many and other relationships, Serializable serialization or deserialization caused stack overflow,
The suggestion is: add @JsonIgnore, although it is feasible and no error is reported, but the front desk will not accept this field after adding it. The problem is that I need this field.
Later, the solution was unsuccessful. After the teacher looked at it, he found that the request of the c-layer did not add @JsonView, which caused the error.
After adding it solved the problem.
Summary: first check which request is reporting the error, and see if there is any problem with the relevant code. If it doesn't work, then Google it, because sometimes Google can't solve its own problems well.
Problems encountered with dynamic components
question
The v layer is like this:
<form [formGroup]="formGroup" *ngIf="task">
<....> // 这里是 描述<imput>
<....> // 这里是 有无截止日期<select>
<....> // 这里是 截止日期<input>
<!-- 动态表单项-->
<div class="ad-banner-example">
<ng-template appFormItem></ng-template>
</div>
</form>
It stands to reason that the content in the red box should be displayed, that is, the dynamic form I wrote in the above code
<ng-template appFormItem></ng-template>
But the result is this: the dynamic form doesn't show up.
show code
Here is the code configuration:
The c layer sets this property, and dynamic form items can be added to it.
@ViewChild(FormItemDirective, {static: true})
appFormItem!: FormItemDirective;
This is the instruction definition, which uses the viewContainerRef in it to add dynamic form components.
@Directive({
selector: '[appFormItem]'
})
export class FormItemDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}
This is represented graphically as follows:
analyse problem
I get this error in the console:
That is to say, the property that the parent component accesses the child component through @viewChild is undefined!
Let's take a look at what is @ViewChild first
@ViewChild is a property decorator. It provides a powerful way to access child elements and attributes. @ViewChild is a very simple way to access when we want to access child component elements in the parent component, form property dirty checks, trigger events, and directives in the parent component. @ViewChild is a very simple way to access child elements.
Why does this kind of access child element attribute is undefined ? I looked up @viewChild's stuff.
I got this information:
The ViewChild decorator is not available when the parent component starts rendering. It will not be available in the ngOnInit() lifecycle hook. It will be available in the ngAfterViewInit lifecycle hook.
Seeing this, I understand the reason for the error :
During ngOnInit we access @viewChild but it is not available and all consoles report an error
new question
Then a new question comes:
Even if it's not available at ngOnInit, won't angular render it again after ngOnInit, making it available?
I mean: when the page stabilizes, we can see it available.
Here is another property of @ViewChild: static
@ViewChild(FormItemDirective, {static: true})
In my code, set it to static.
static
What is the use of the staic attribute? I found related content
Static stands for whether the ViewChild is “static” content eg Always available on the page, no matter page bindings, API calls, ngIfs etc. When set to true, we are telling Angular that the ViewChild will be available at anytime, so simply query for the ChildComponent at the earliest lifecycle hook available and then never query again.
Simply put:
Static represents whether the ViewChild is "static" content.
- When set to true, we tell Angular that the ViewChild will be available at any time, so just query and load at the earliest lifecycle hook available, and never query again.
- Set to false, we're saying that the ViewChild will be available later, so we have to check the ViewChild on every run and load it if it's not available. Obviously this creates a higher performance load as we have to always check if it is available when the component changes.
source:
https://tutorialsforangular.com/author/admin/page/2/
I don't know how big this load is for now.
There will be two results:
- Set to true, ViewChild is available during ngOnInit, but will not be queried and loaded later.
- If set to false, it is not available during ngOnInit. When accessing operations such as component changes, the query will be performed. If it is not available, it will be loaded.
After understanding the meaning, came to find the problem with my own code.
I found the problem:
Cause of the problem: *ngIf is used when static is true.
At ngOnInit, the task entity has no value, so the subcomponents below will not be loaded.
solution:
- Manually render it once when the task has a value.
- Change static to false, query it before each use, and load it if it is not loaded. But there is a certain impact on performance.
After testing, both methods work well. I used the first one.
Summary recommendations
- If you want to make @ViewChild available in ngOnInit and save performance, or always available on the page and never hidden, set static to true, but pay attention to whether it can be loaded successfully.
- If you want to call it dynamically in some way, or you want to check whether it is available every time, set static to false,
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。