简体   繁体   English

为什么此Angular验证指令显示为异步?

[英]Why does this Angular Validation Directive appear async?

I wanted to use template forms and [min] and [max] directives, so I have created them and they work. 我想使用模板表单和[min][max]指令,所以我创建了它们并且它们有效。 But the test confuses me: validation is not executed asynchronously, yet after changing my values and stuff, I have to go through this: 但测试让我感到困惑:验证不是异步执行的,但在更改了我的值和内容之后,我必须经历这个:

component.makeSomeChangeThatInvalidatesMyInput();
// control.invalid = false, expected

fixture.detectChanges();
// control.invalid is still false, not expected

// but if I go do this
fixture.whenStable().then(() => {
  // control.invalid is STILL false, not expected
  fixture.detectChanges();
  // control.invalid now true
  // expect(... .errors ... ) now passes
})

I don't understand why would I need even that whenStable() , let alone another detectChanges() cycle. 我不明白为什么我甚whenStable()需要whenStable() ,更不用说另一个detectChanges()循环了。 What am I missing here? 我在这里错过了什么? Why do I need 2 cycles of change detection for this validation to be executed? 为什么我需要2个周期的变化检测才能执行此验证?

Doesn't matter if I run the test as async or not. 如果我将测试作为async运行并不重要。

Here's my test: 这是我的测试:

@Component({
    selector: 'test-cmp',
    template: `<form>
        <input [max]="maxValue" [(ngModel)]="numValue" name="numValue" #val="ngModel">
        <span class="error" *ngIf="val.invalid">Errors there.</span>
    </form>`
})
class TestMaxDirectiveComponent {
    maxValue: number;
    numValue: number;
}
fdescribe('ValidateMaxDirective', () => {
    let fixture: ComponentFixture<TestMaxDirectiveComponent>;
    let component: TestMaxDirectiveComponent;

    beforeEach(async(() => TestBed.configureTestingModule({
        imports: [FormsModule],
        declarations: [TestMaxDirectiveComponent, ValidateMaxDirective],
    }).compileComponents()
        .then(() => {
            fixture = TestBed.createComponent(TestMaxDirectiveComponent);
            component = fixture.componentInstance;
            return fixture.detectChanges();
        })
    ));
    fit('should have errors even when value is greater than maxValue', async(() => {
        component.numValue = 42;
        component.maxValue = 2;
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            fixture.detectChanges();
            expect(fixture.nativeElement.querySelector('.error')).toBeTruthy();
        });
    }));
});

And here's the directive itself (simplified a bit): 这是指令本身(简化了一下):

const VALIDATE_MAX_PROVIDER = {
    provide: NG_VALIDATORS, useExisting: forwardRef(() => ValidateMaxDirective), multi: true,
};
@Directive({
    selector: '[max][ngModel]',
    providers: [VALIDATE_MAX_PROVIDER],
})
export class ValidateMaxDirective implements Validator {
    private _max: number | string;
    @Input() get max(): number | string {
        return this._max;
    }
    set max(value: number | string) {
        this._max = value;
    }

    validate(control: AbstractControl): ValidationErrors | null {
        if (isEmptyInputValue(control.value) || isEmptyInputValue(this._max)) {
            return null;  // don't validate empty values to allow optional controls
        }
        const value = parseFloat(control.value);
        return !isNaN(value) && value > this._max ? {'max': {'max': this._max, 'actual': control.value}} : null;
    }
}

I have tested this on a brand new ng new app with @angular/cli version 1.6.8 and latest angular 5.2. 我已经在@angular/cli版本1.6.8和最新角度5.2的全新ng new app上进行了测试。

After our conversation I've got it. 在我们的谈话后,我得到了它。 You asked me what is async in the code above : 你问我上面代码中的异步是什么:

validate() is ! validate()是!

we see that this method takes control: AbstractControl as a parameter 我们看到这个方法需要control: AbstractControl作为参数

in it's docs you'll find that as well as a synchronous behavior this handles asynchronous validation. 在它的文档中你会发现它和同步行为一起处理异步验证。

So I'm running on the assumption here that the adding of that parameter turned validate() asynchronous. 所以我在这里假设添加该参数使validate()异步。

this in turn means that you need to wait for it's eventual return to assess whether there have been changes or not. 这反过来意味着您需要等待它最终return以评估是否有变化。

...This being the only function likely to trigger a change, we depend on it when we .detectChanges(); ......这是唯一可能触发变更的功能,我们依赖它.detectChanges(); .

and in any async case in javascript values (variable) are to be imagined using the time dimension on top of whatever others they may already possess. 并且在javascript值(变量)中的任何异步情况下,可以使用时间维度来想象它们可能已经拥有的任何其他内容。

as such developers in the javascript community have adopted the "marbles on a string" or "birds on a telephone line" metaphors to help explain them. 因此,javascript社区的开发人员采用了“字符串上的弹珠”或“电话线上的鸟”这些比喻来帮助解释它们。

the common theme being a lifeline/timeline. 共同主题是生命线/时间线。 Here's another, my own personal representation : 这是另一个,我自己的个人代表:

在此输入图像描述

you'll have to .subscribe() or .then() to have what you want executed executed at the time of hydration/return. 你必须.subscribe().then()有你想要的水化/返回时执行执行了什么。

so when you : 所以当你:

component.makeSomeChangeThatInvalidatesMyInput(); // (1)
fixture.detectChanges();                          // (2)
fixture.whenStable()                              // (3)
.then(() => {                                     // (not a step) :we are now outside the
                        //logic of "order of execution" this code could happen much after.
  fixture.detectChanges();
})
  • In step (2) you are effectively making that first assessment in my diagram above, the one straight onto the timeline where nothing has yet happened. 在步骤(2)中,您正在上面的图表中有效地进行第一次评估,直接进入时间线上尚未发生任何事情的评估。

  • but in (not a step) you are listening for everytime there is a change (so potentially many calls). 但是在(不是一步)你每次有变化时都在倾听(所以可能有很多电话)。 you get the expected value there at last because the code execution for assessing is happening "right on time" to catch the correct result; 你终于得到了预期的价值,因为评估的代码执行正在“按时”发生以捕获正确的结果; even better, it's happening because of the result(s). 更好的是,它正在发生, 因为结果。

  • and yes only detectChanges() can detect the changes so assessing before you run detectChanges() , even once within the .then() , will return a premature value. 并且是唯一detectChanges()可以检测到变化,从而评估之前运行detectChanges()连内一次.then()会返回一个值为时过早。

the result that your first .detectChanges() does not detect a change whereas your fixture.whenStable().then(() => {fixture.detectChanges()}) does is not a bug and is javascript functioning as intended. 你的第一个.detectChanges()没有检测到更改的结果,而你的.detectChanges() fixture.whenStable().then(() => {fixture.detectChanges()})确实不是一个错误,并且javascript按预期运行。

(that includes jasmine, jasmine is pure javascript) (包括茉莉,茉莉是纯粹的javascript)

So there you have it! 所以你有它! there was no odd behavior after all :) 毕竟没有奇怪的行为:)

hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM