我想使用模板表单以及[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
})
我不明白为什么我甚至需要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();
});
}));
});
这里是指令本身(简化了一点):
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;
}
}
我已经使用ng new app
版本1.6.8和最新角度5.2的全新@angular/cli
对此进行了测试。
答案 0 :(得分:5)
在我们的谈话之后,我已经得到了它。你问我上面代码中的异步是什么:
validate()
是!
我们看到此方法将control: AbstractControl
作为参数
在它的docs中你会发现它和同步行为一起处理异步验证。
所以我在这里假设该参数的添加变为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();
})
在步骤(2)中,您正在上面的图表中有效地进行第一次评估,直接进入时间线上尚未发生任何事情的评估。
但是在(不是一步)你每次有变化时都在听(所以可能有很多电话)。你终于得到了预期的价值,因为评估的代码执行正在“按时”发生以捕获正确的结果;更好的是,它发生了 因为 的结果。
detectChanges()
可以检测到更改,因此在您运行detectChanges()
之前进行评估,即使在.then()
内,也会返回过早的值。您的第一个.detectChanges()
未检测到更改的结果,而您的fixture.whenStable().then(() => {fixture.detectChanges()})
不会发现错误,并且javascript正常运行。
(包括茉莉,茉莉是纯粹的javascript)
所以你有它!毕竟没有奇怪的行为:)
希望这有帮助!