-
-
Notifications
You must be signed in to change notification settings - Fork 130
/
validation-errors-custom-attribute.ts
81 lines (67 loc) · 2.4 KB
/
validation-errors-custom-attribute.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import { bindingMode } from 'aurelia-binding';
import { Lazy } from 'aurelia-dependency-injection';
import { customAttribute, bindable } from 'aurelia-templating';
import { ValidationController } from './validation-controller';
import { ValidateResult } from './validate-result';
import { ValidationRenderer, RenderInstruction } from './validation-renderer';
import { DOM } from 'aurelia-pal';
export interface RenderedError {
error: ValidateResult;
targets: Element[];
}
@customAttribute('validation-errors')
export class ValidationErrorsCustomAttribute implements ValidationRenderer {
public static inject(): [typeof DOM.Element, Lazy<ValidationController>] {
return [DOM.Element, Lazy.of(ValidationController)];
}
@bindable({ defaultBindingMode: bindingMode.oneWay })
public controller: ValidationController | null = null;
@bindable({ primaryProperty: true, defaultBindingMode: bindingMode.twoWay })
public errors: RenderedError[] = [];
private errorsInternal: RenderedError[] = [];
constructor(private boundaryElement: Element, private controllerAccessor: () => ValidationController) {
}
public sort() {
this.errorsInternal.sort((a, b) => {
if (a.targets[0] === b.targets[0]) {
return 0;
}
// tslint:disable-next-line:no-bitwise
return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1;
});
}
public interestingElements(elements: Element[]): Element[] {
return elements.filter(e => this.boundaryElement.contains(e));
}
public render(instruction: RenderInstruction) {
for (const { result } of instruction.unrender) {
const index = this.errorsInternal.findIndex(x => x.error === result);
if (index !== -1) {
this.errorsInternal.splice(index, 1);
}
}
for (const { result, elements } of instruction.render) {
if (result.valid) {
continue;
}
const targets = this.interestingElements(elements);
if (targets.length) {
this.errorsInternal.push({ error: result, targets });
}
}
this.sort();
this.errors = this.errorsInternal;
}
public bind() {
if (!this.controller) {
this.controller = this.controllerAccessor();
}
// this will call render() with the side-effect of updating this.errors
this.controller.addRenderer(this);
}
public unbind() {
if (this.controller) {
this.controller.removeRenderer(this);
}
}
}