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

Zla*_*tko 6 validation angular-directive angular angular-test

我想用模板形式[min][max]指令,所以我创造了他们和他们的工作.但测试让我感到困惑:验证不是异步执行的,但在更改了我的值和内容之后,我必须经历这个:

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
})
Run Code Online (Sandbox Code Playgroud)

我不明白为什么我甚至需要这个whenStable(),更不用说另一个detectChanges()循环了.我在这里错过了什么?为什么我需要2个周期的变化检测才能执行此验证?

如果我运行测试无关紧要async.

这是我的测试:

@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();
        });
    }));
});
Run Code Online (Sandbox Code Playgroud)

这是指令本身(简化了一下):

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经在一个全新的测试这ng new app@angular/cli1.6.8版本和最新的角5.2.

tat*_*tsu 5

在我们的谈话后,我得到了它.你问我上面代码中的异步是什么:

validate() 是!

我们看到这个方法control: AbstractControl作为参数

在它的文档中你会发现它和同步行为一起处理异步验证.

所以我在这里假设添加该参数是validate()异步的.

这反过来意味着你需要等待它最终return评估是否有变化.

......这是唯一可能引发变革的功能,我们依赖它.detectChanges();.

并且在javascript值(变量)中的任何异步情况下都可以使用时间维度来想象它们可能已经拥有的其他任何东西.

因此,javascript社区的开发人员采用了"字符串上的弹珠"或"电话线上的鸟"这些比喻来帮助解释它们.

共同主题是生命线/时间线.这是另一个,我自己的个人代表:

在此输入图像描述

在水合/回归时你必须要.subscribe()或者.then()要执行你想要执行的东西.

所以当你:

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();
})
Run Code Online (Sandbox Code Playgroud)
  • 在步骤(2)中,您正在上面的图表中有效地进行第一次评估,直接进入时间线上尚未发生任何事情的评估.

  • 但是在(不是一步)你每次有变化时都在倾听(所以可能有很多电话).你终于得到了预期的价值,因为评估的代码执行正在"按时"发生以捕获正确的结果; 更好的是,它正在发生,因为结果.

  • 是的只能detectChanges()检测到这些变化,所以在你运行之前进行评估detectChanges(),即使在其中.then(),也会返回一个过早的值.

结果是你的第一个.detectChanges()没有检测到变化,而你fixture.whenStable().then(() => {fixture.detectChanges()})的确不是一个错误,并且javascript按预期运行.

(包括茉莉,茉莉是纯粹的javascript)

所以你有它!毕竟没有奇怪的行为:)

希望这可以帮助!