Skip to content

Instantly share code, notes, and snippets.

@3cp
Last active April 6, 2018 03:56
Show Gist options
  • Save 3cp/ba86ba3ad3a6b165f19b2e7370d9fc6c to your computer and use it in GitHub Desktop.
Save 3cp/ba86ba3ad3a6b165f19b2e7370d9fc6c to your computer and use it in GitHub Desktop.
<template>
<require from="./dyn-field"></require>
<form>
<dyn-field
repeat.for="prop of properties"
label.bind="prop.label"
value.bind="model | get:model:prop.expression"
errors.bind="errors | get:errors:prop.expression"></dyn-field>
</form>
</template>
import {inject} from 'aurelia-dependency-injection';
import {Parser} from 'aurelia-binding';
// normally you just do
// import Validation from 'bcx-validation';
// @inject(Validation)
// Look in main.js for the the hook up DI of "Validation" to global name BcxValidation
@inject('Validation')
export class App {
model = {
firstName: 'John',
lastName: 'Doe',
address: {
address1: '1 Main Street',
address2: '',
city: 'Burlington',
state: 'VT',
zip: '05401'
}
};
properties = [
{ label: 'First Name', expression: 'firstName' },
{ label: 'Last Name', expression: 'lastName' },
{ label: 'Address 1', expression: 'address.address1' },
{ label: 'Address 2', expression: 'address.address2' },
{ label: 'City', expression: 'address.city' },
{ label: 'State', expression: 'address.state' },
{ label: 'Zip', expression: 'address.zip' }
];
validationRule = {
firstName: 'mandatory',
lastName: 'mandatory',
address: {
address1: 'mandatory',
city: 'mandatory',
state: [
'mandatory',
{
validate: 'within',
items: ['VT', 'WA'],
message: 'not a valid US state (demo only supports VT and WA, modify your rule to support full list)'
}
],
zip: ['mandatory', {validate: /^\d{5}$/, message: "not a valid US zip code"}]
}
}
constructor(validation) {
this.validator = validation.generateValidator(this.validationRule);
}
// Since model object is complex, we use dirty check to update errors,
// you can use @computedFrom('firstName', 'lastName', 'address.address1', ...) if you want efficiency.
// Sometime, dirty check is the only choice if your model is like
// {name: '', employees: [ dynamic_list_of_employees ]}.
// BTW, bcx-validation can validate the above model.
get errors() {
return this.validator(this.model);
}
}
@inject(Parser)
export class GetValueConverter {
constructor(parser) {
this.parser = parser;
}
toView(_: any, model: any, propertyExpression: string): any {
let getExp = this.parser.parse(propertyExpression);
if (!model) return; // modified to deal with missing model (the empty errors obj)
return getExp.evaluate({ bindingContext: model });
}
fromView(value: any, model: any, propertyExpression: string): any {
let setExp = this.parser.parse(propertyExpression + ' = $value');
setExp.evaluate({
bindingContext: model,
overrideContext: { $value: value }
});
return model;
}
}
export class LocalDebugValueConverter {
toView(value: any): any {
console.info(`[DEBUG-toView]`, JSON.stringify(value));
return value;
}
fromView(value: any): any {
console.info(`[DEBUG-fromView]`, JSON.stringify(value));
return value;
}
}
<template>
<div class="from-group ${errors ? 'has-error' : ''}">
<label class="control-label">${label}</label>
<input type="text" class="form-control" value.bind="value">
<span if.bind="errors" class="help-block">
<span repeat.for="err of errors">${err}<br if.bind="!$last"></span>
</span>
</div>
</template>
import {bindable, bindingMode} from 'aurelia-framework';
export class DynField {
@bindable({defaultBindingMode: bindingMode.twoWay}) value;
@bindable errors;
@bindable label;
}
<!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>
label, input {
display: block;
margin-bottom: 10px;
}
</style>
</head>
<body aurelia-app="main">
<h1>Loading...</h1>
<!-- lodash and bcx-expression-evalutor are dependencies of bcx-validation -->
<!-- for demo in gistrun, load them before requrejs -->
<!-- for real app, just do npm install bcx-validation -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<script src="https://npm-cdn.herokuapp.com/[email protected]/dist/index.js"></script>
<script src="https://npm-cdn.herokuapp.com/[email protected]/dist/index.js"></script>
<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');
// For demostration in gistrun, bcx-validation is loaded in index.html before requirejs.
// bcx-validation is in UMD format, when no AMD/commonjs loader available, it creates
// global name BcxValidation.
// It is optional to register bcx-validation as transient.
// the default DI behavior is singleton, which works out of the box.
// But if you customize a validation instance by addValidator/addHelper,
// it is better to use transient instance to isolate the customization.
aurelia.container.registerTransient('Validation', BcxValidation);
aurelia.start().then(() => aurelia.setRoot());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment