Skip to content

Instantly share code, notes, and snippets.

@lstarky
Forked from jdanyow/app.html
Last active January 12, 2022 14:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save lstarky/6e97538c3888cae0f6134faed9d67362 to your computer and use it in GitHub Desktop.
Save lstarky/6e97538c3888cae0f6134faed9d67362 to your computer and use it in GitHub Desktop.
Aurelia Validation on dynamic form
<template>
<require from="./registration-form"></require>
<registration-form></registration-form>
</template>
export class App {
}
import {
ValidationRenderer,
RenderInstruction,
ValidateResult
} from 'aurelia-validation';
export class BootstrapFormRenderer {
render(instruction: RenderInstruction) {
for (let { result, elements } of instruction.unrender) {
for (let element of elements) {
this.remove(element, result);
}
}
for (let { result, elements } of instruction.render) {
for (let element of elements) {
this.add(element, result);
}
}
}
add(element: Element, result: ValidateResult) {
if (result.valid) {
return;
}
const formGroup = element.closest('.form-group');
if (!formGroup) {
return;
}
// add the has-error class to the enclosing form-group div
formGroup.classList.add('has-error');
// add help-block
const message = document.createElement('span');
message.className = 'help-block validation-message';
message.textContent = result.message;
message.id = `validation-message-${result.id}`;
formGroup.appendChild(message);
}
remove(element: Element, result: ValidateResult) {
if (result.valid) {
return;
}
const formGroup = element.closest('.form-group');
if (!formGroup) {
return;
}
// remove help-block
const message = formGroup.querySelector(`#validation-message-${result.id}`);
if (message) {
formGroup.removeChild(message);
// remove the has-error class from the enclosing form-group div
if (formGroup.querySelectorAll('.help-block.validation-message').length === 0) {
formGroup.classList.remove('has-error');
}
}
}
}
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<style>
registration-form {
display: block;
max-width: 300px;
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body aurelia-app="main">
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin('aurelia-validation');
aurelia.start().then(() => aurelia.setRoot());
}
<template>
<require from="./my-textbox"></require>
<require from="./my-textarea"></require>
<my-textbox if.bind="control.type == 'my-textbox'"
value.bind="model[control.bind] & validate"
vc.bind="control.vc">
</my-textbox>
<my-textarea if.bind="control.type == 'my-textarea'"
value.bind="model[control.bind] & validate"
rows.bind="control.rows"
vc.bind="control.vc">
</my-textarea>
</template>
import {bindable, containerless} from 'aurelia-framework';
@containerless
export class MyMetaElement {
@bindable control; // array of control objects (label, control, bind, [placeholder], [rows], [vc], [format], [enum], [validation])
@bindable model; // data for binding
attached() {
console.log("NewMeta model:", this.model);
}
}
<template>
<textarea class="form-control" rows.bind="rows" value.bind="value & validate">
</template>
import {bindable, bindingMode} from 'aurelia-framework';
export class MyTextarea {
@bindable({ defaultBindingMode: bindingMode.twoWay }) value;
}
<template>
<input class="form-control" type="text" value.bind="value & validate" />
</template>
import {bindable, bindingMode} from 'aurelia-framework';
export class MyTextbox {
@bindable({ defaultBindingMode: bindingMode.twoWay }) value;
}
<template>
<require from="./my-meta-element"></require>
<br>
<form submit.delegate="submit()">
<div class="btn-group">
<button class="btn btn-info" click.delegate="goEdit()">Edit Record</button>
<button class="btn btn-success" click.delegate="goCreate()">Create Record</button>
</div>
<br><br>
<div class="form-group" repeat.for="control of controls">
<label class="control-label">${control.label}</label>
<div class="form-control">
<my-meta-element control.bind="control" model.bind="record" containerless></my-meta-element>
</div>
</div>
<button type="submit" class="btn btn-primary" click.delegate="submit()">Submit</button>
<ul><li repeat.for="error of controller.errors">${error.message}</li></ul>
</form>
</template>
import {inject} from 'aurelia-dependency-injection';
import {
ValidationControllerFactory,
ValidationController,
ValidationRules
} from 'aurelia-validation';
import {BootstrapFormRenderer} from './bootstrap-form-renderer';
@inject(ValidationControllerFactory)
export class RegistrationForm {
record = {};
controls = [
{label: 'First name', type: 'my-textbox', bind: 'firstName', validation: '.required().minLength(3)'},
{label: 'Last name', type: 'my-textbox', bind: 'lastName', validation: '.required()'},
{label: 'Email', type: 'my-textbox', bind: 'email', validation: '.required().email()'},
{label: 'Notes', type: 'my-textarea', bind: 'notes', validation: '.required().minLength(10)'}
];
attached() {
this.rules = [];
this.controls.map(control => {
if (control.validation) {
if (control.validation.match(/\.required\(\)/)) { this.rules.push(ValidationRules.ensure(control.bind).required().rules[0]); }
if (control.validation.match(/\.email\(\)/)) { this.rules.push(ValidationRules.ensure(control.bind).email().rules[0]); }
let minLength = control.validation.match(/\.minLength\(([0-9]+)\)/);
if (minLength) { this.rules.push(ValidationRules.ensure(control.bind).minLength(minLength[1]).rules[0]); }
let maxLength = control.validation.match(/\.maxLength\(([0-9]+)\)/);
if (maxLength) { this.rules.push(ValidationRules.ensure(control.bind).maxLength(maxLength[1]).rules[0]); }
}
});
this.setValidation();
}
setValidation() {
this.controller.addObject(this.record, this.rules);
}
constructor(controllerFactory) {
this.controller = controllerFactory.createForCurrentScope();
this.controller.addRenderer(new BootstrapFormRenderer());
}
submit() {
this.controller.validate();
}
goCreate() {
this.controller.removeObject(this.record);
this.record = {
firstName: '',
lastName: '',
email: '',
notes: ''
};
this.setValidation();
}
goEdit() {
this.controller.removeObject(this.record);
this.record = {
firstName: 'Liam',
lastName: 'Starkenburg',
email: 'me@here.com',
notes: 'Contact for development project'
};
this.setValidation();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment