-
-
Save jdanyow/ea843c24956cfffff48bb21776291f6a to your computer and use it in GitHub Desktop.
import {inject} from 'aurelia-dependency-injection'; | |
import {validationRenderer} from 'aurelia-validation'; | |
@validationRenderer | |
@inject(Element) | |
export class BootstrapFormValidationRenderer { | |
constructor(boundaryElement) { | |
this.boundaryElement = boundaryElement; | |
} | |
render(error, target) { | |
if (!target || !(this.boundaryElement === target || this.boundaryElement.contains(target))) { | |
return; | |
} | |
// tag the element so we know we rendered into it. | |
target.errors = (target.errors || new Map()); | |
target.errors.set(error); | |
// add the has-error class to the bootstrap form-group div | |
const formGroup = target.querySelector('.form-group') || target.closest('.form-group'); | |
formGroup.classList.add('has-error'); | |
// add help-block | |
const message = document.createElement('span'); | |
message.classList.add('help-block'); | |
message.classList.add('validation-error'); | |
message.textContent = error.message; | |
message.error = error; | |
formGroup.appendChild(message); | |
} | |
unrender(error, target) { | |
if (!target || !target.errors || !target.errors.has(error)) { | |
return; | |
} | |
target.errors.delete(error); | |
// remove the has-error class on the bootstrap form-group div | |
const formGroup = target.querySelector('.form-group') || target.closest('.form-group'); | |
formGroup.classList.remove('has-error'); | |
// remove all messages related to the error. | |
let messages = formGroup.querySelectorAll('.validation-error'); | |
let i = messages.length; | |
while(i--) { | |
let message = messages[i]; | |
if (message.error !== error) { | |
continue; | |
} | |
message.error = null; | |
message.remove(); | |
} | |
} | |
} | |
// Polyfill for Element.closest and Element.matches | |
// https://github.com/jonathantneal/closest/ | |
(function (ELEMENT) { | |
ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector; | |
ELEMENT.closest = ELEMENT.closest || function closest(selector) { | |
var element = this; | |
while (element) { | |
if (element.matches(selector)) { | |
break; | |
} | |
element = element.parentElement; | |
} | |
return element; | |
}; | |
}(Element.prototype)); |
import {BootstrapFormValidationRenderer} from './bootstrap-form-validation-renderer'; | |
export function configure(config) { | |
config.container.registerHandler( | |
'bootstrap-form', | |
container => container.get(BootstrapFormValidationRenderer)); | |
} |
On line 52 ChildNode.prototype.remove()
is called, which is not well supported in all browsers and should be polyfilled.
https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove
message.parentElement.removeChild(message);
Tested in IE, which was generating errors before and it works.
Sample unit test in TS with little support of JQuery:
import {BootstrapFormValidationRenderer} from "./bootstrap-form-validation-renderer";
import {RenderInstruction, RenderErrorInstruction, ValidationError} from "aurelia-validation";
import "jquery";
describe(BootstrapFormValidationRenderer.name, () => {
let renderer: BootstrapFormValidationRenderer;
let html: JQuery;
class TestRenderInstruction implements RenderInstruction {
kind;
render: RenderErrorInstruction[] = [];
unrender: RenderErrorInstruction[] = [];
addRender(errorId: number, elementSelector: string): TestRenderInstruction {
this.render.push(this.newRenderErrorInstruction(errorId, elementSelector));
return this;
}
addUnrender(errorId: number, elementSelector: string): TestRenderInstruction {
this.unrender.push(this.newRenderErrorInstruction(errorId, elementSelector));
return this;
}
private newRenderErrorInstruction(errorId: number, elementSelector: string) {
return <RenderErrorInstruction>{
error: this.newError(errorId),
elements: [html.find(`${elementSelector} input`)[0]]
}
};
private newError(id: number): ValidationError {
let error = new ValidationError({}, `Error ${id}`, {});
error.id = id;
return error;
}
}
beforeEach(() => {
html = $(`<form>
<div class="form-group first"><input type="text"></div>
<div class="form-group second"><input type="text"></div>
</form>`);
renderer = new BootstrapFormValidationRenderer;
});
it('displays an error', () => {
renderer.render(new TestRenderInstruction().addRender(1, '.first'));
expect(html.find(".first").hasClass('has-error')).toBeTruthy();
expect(html.find(".second").hasClass('has-error')).toBeFalsy();
expect(html.find(".first .help-block").length).toBe(1);
});
it('clears an error', () => {
renderer.render(new TestRenderInstruction().addRender(1, '.first'));
renderer.render(new TestRenderInstruction().addUnrender(1, '.first'));
expect(html.find(".first").hasClass('has-error')).toBeFalsy();
expect(html.find(".first .help-block").length).toBe(0);
});
it('renders many errors', () => {
renderer.render(new TestRenderInstruction().addRender(1, '.first').addRender(2, '.first'));
expect(html.find(".first").hasClass('has-error')).toBeTruthy();
expect(html.find(".first .help-block").length).toBe(2);
});
it('renders errors next to desired fields', () => {
renderer.render(new TestRenderInstruction().addRender(1, '.first').addRender(2, '.second'));
expect(html.find(".first .help-block").text()).toBe('Error 1');
expect(html.find(".second .help-block").text()).toBe('Error 2');
});
it('clears single error', () => {
renderer.render(new TestRenderInstruction().addRender(1, '.first').addRender(2, '.first'));
renderer.render(new TestRenderInstruction().addUnrender(1, '.first'));
expect(html.find(".first").hasClass('has-error')).toBeTruthy();
expect(html.find(".first .help-block").length).toBe(1);
expect(html.find(".first .help-block").text()).toBe('Error 2');
});
});
i want to use BootstrapFormValidationRenderer code in aurelia framework javascript but i have this error TypeError: Object(...) is not a function
at Module../src/bootstrap-form-validation-renderer.js
i have a registration form like this
<div class="form-group">
<label class="control-label" for="first">First Name</label>
<input type="text" class="form-control" id="first" placeholder="First Name"
value.bind="firstName & validate"><span></span>
</div>
<div class="form-group">
<label class="control-label" for="last">Last Name</label>
<input type="text" class="form-control" id="last" placeholder="Last Name"
value.bind="lastName & validate">
</div>
<div class="form-group">
<label class="control-label" for="email">Email</label>
<input type="text" class="form-control" id="email" placeholder="Email"
value.bind="email & validate">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
and a js file this
import {inject, NewInstance} from 'aurelia-dependency-injection';
import {ValidationController, validateTrigger} from 'aurelia-validation';
import {required, email} from 'aurelia-validatejs';
import {BootstrapFormValidationRenderer} from './bootstrap-form-validation-renderer';
@Inject(NewInstance.of(ValidationController), validateTrigger)
export class RegistrationForm {
@required
firstName = '';
@required
lastName = '';
@required
email = '';
constructor(controller) {
this.controller = controller;
controller.validateTrigger = validateTrigger.manual;
}
submit() {
let errors = this.controller.validate();
}
}
Why isn't this a package? I get that maybe some people will care to customize the behavior, but certainly seems like a good default should be an
npm install
away. Hopefully it's just because you haven't gotten around to it?