import { inject, customAttribute, templateController, BoundViewFactory, ViewSlot } from 'aurelia-framework';
import userService from 'services/api/userService';
@customAttribute('if-permission')
@inject(BoundViewFactory, ViewSlot)
@templateController
export class IfPermission {
constructor(viewFactory, viewSlot) {
this.viewFactory = viewFactory;
this.viewSlot = viewSlot;
this.showing = false;
this.view = null;
this.bindingContext = null;
this.overrideContext = null;
}
/**
* Binds the if to the binding context and override context
* @param bindingContext The binding context
* @param overrideContext An override context for binding.
*/
bind(bindingContext, overrideContext) {
// Store parent bindingContext, so we can pass it down
this.bindingContext = bindingContext;
this.overrideContext = overrideContext;
this.valueChanged(this.value);
}
valueChanged(newValue) {
if (this.__queuedChanges) {
this.__queuedChanges.push(newValue);
return;
}
let maybePromise = this._runValueChanged(newValue);
if (maybePromise instanceof Promise) {
let queuedChanges = this.__queuedChanges = [];
let runQueuedChanges = () => {
if (!queuedChanges.length) {
this.__queuedChanges = undefined;
return;
}
let nextPromise = this._runValueChanged(queuedChanges.shift()) || Promise.resolve();
nextPromise.then(runQueuedChanges);
};
maybePromise.then(runQueuedChanges);
}
}
_runValueChanged(newValue) {
newValue = userService.hasPermission(newValue);
if (!newValue) {
let viewOrPromise;
if (this.view !== null && this.showing) {
viewOrPromise = this.viewSlot.remove(this.view);
if (viewOrPromise instanceof Promise) {
viewOrPromise.then(() => this.view.unbind());
} else {
this.view.unbind();
}
}
this.showing = false;
return viewOrPromise;
}
if (this.view === null) {
this.view = this.viewFactory.create();
}
if (!this.view.isBound) {
this.view.bind(this.bindingContext, this.overrideContext);
}
if (!this.showing) {
this.showing = true;
return this.viewSlot.add(this.view);
}
}
/**
* Unbinds the if
*/
unbind() {
if (this.view === null) {
return;
}
this.view.unbind();
if (!this.viewFactory.isCaching) {
return;
}
if (this.showing) {
this.showing = false;
this.viewSlot.remove(this.view, true, true);
}
this.view.returnToCache();
this.view = null;
}
}
import { inject, customAttribute, templateController, BoundViewFactory, ViewSlot } from 'aurelia-framework';
import userService from 'services/api/userService';
@customAttribute('if-user-role')
@inject(BoundViewFactory, ViewSlot)
@templateController
export class IfUserRole {
constructor(viewFactory, viewSlot) {
this.viewFactory = viewFactory;
this.viewSlot = viewSlot;
this.showing = false;
this.view = null;
this.bindingContext = null;
this.overrideContext = null;
}
/**
* Binds the if to the binding context and override context
* @param bindingContext The binding context
* @param overrideContext An override context for binding.
*/
bind(bindingContext, overrideContext) {
// Store parent bindingContext, so we can pass it down
this.bindingContext = bindingContext;
this.overrideContext = overrideContext;
this.valueChanged(this.value);
}
valueChanged(newValue) {
if (this.__queuedChanges) {
this.__queuedChanges.push(newValue);
return;
}
let maybePromise = this._runValueChanged(newValue);
if (maybePromise instanceof Promise) {
let queuedChanges = this.__queuedChanges = [];
let runQueuedChanges = () => {
if (!queuedChanges.length) {
this.__queuedChanges = undefined;
return;
}
let nextPromise = this._runValueChanged(queuedChanges.shift()) || Promise.resolve();
nextPromise.then(runQueuedChanges);
};
maybePromise.then(runQueuedChanges);
}
}
_runValueChanged(newValue) {
newValue = userService.hasRole(newValue);
if (!newValue) {
let viewOrPromise;
if (this.view !== null && this.showing) {
viewOrPromise = this.viewSlot.remove(this.view);
if (viewOrPromise instanceof Promise) {
viewOrPromise.then(() => this.view.unbind());
} else {
this.view.unbind();
}
}
this.showing = false;
return viewOrPromise;
}
if (this.view === null) {
this.view = this.viewFactory.create();
}
if (!this.view.isBound) {
this.view.bind(this.bindingContext, this.overrideContext);
}
if (!this.showing) {
this.showing = true;
return this.viewSlot.add(this.view);
}
}
/**
* Unbinds the if
*/
unbind() {
if (this.view === null) {
return;
}
this.view.unbind();
if (!this.viewFactory.isCaching) {
return;
}
if (this.showing) {
this.showing = false;
this.viewSlot.remove(this.view, true, true);
}
this.view.returnToCache();
this.view = null;
}
}
Hi. I tried to simplify the html needed to create a form with fields+validation+error display by creating a custom component.
validated-input.html
<template>
<div class="form-input ${class}">
<slot name="icon"></slot>
<label if.bind="!floatingLabel">${label}</label>
<div>
<div class="ui input primary-input">
<input type="text" ref="field" class="${errors.length > 0 ? 'has-errors': ''}"
keydown.delegate="keyDownHandler($event)" value.bind="value" blur.trigger="blur()" input.trigger="input()" autocomplete="off">
<div if.bind="floatingLabel" class="floating-label"><span class="floating-label-focus">${label}</span></div>
</div>
<div if.bind="errors.length > 0" class="validation-error">
<i class="exclamation triangle icon"></i>
<span repeat.for="e of errors">${e.message}</span>
</div>
</div>
</div>
</template>
validated-input.ts
import { ValidationController, ValidateResult, validateTrigger } from "aurelia-validation";
import { customElement, bindable, containerless, autoinject, bindingMode, DOM } from "aurelia-framework";
@autoinject
@containerless
@customElement("validated-input")
export class ValidatedInput {
@bindable field: HTMLInputElement;
@bindable label: string;
@bindable class: string = "";
@bindable({ defaultBindingMode: bindingMode.twoWay }) value: string;
@bindable name: string;
@bindable floatingLabel: boolean = false;
@bindable validationTrigger: string = "change";
@bindable maxlength: string | null = null;
@bindable pattern: string | null = null;
errors: ValidateResult[];
private controller: ValidationController;
constructor(private element: Element) {
}
bind(bindingContext: any) {
this.field.setAttribute("value", this.value || "");
this.controller = bindingContext.controller;
if (this.maxlength) {
this.field.setAttribute("maxlength", this.maxlength);
}
if (this.pattern) {
this.field.setAttribute("pattern", this.pattern);
}
this.controller.subscribe((e) => this.errors = e.results.filter((x) => x.propertyName === this.name && !x.valid));
}
blur() {
if (this.validationTrigger === "blur") {
this.validate();
}
}
input() {
if (this.validationTrigger === "change") {
this.validate();
}
}
private validate() {
this.field.setAttribute("value", this.value || "");
this.controller.validate().then((validate) => {
this.errors = validate.results.filter((x) => x.propertyName === this.name && !x.valid);
});
}
private keyDownHandler(e: KeyboardEvent) {
if (e.key === "Enter") {
this.element.dispatchEvent(DOM.createCustomEvent("enter"));
}
return true;
}
}
The problem I have is that I need (I think) to have each field subscribe to the validation callback to perform their logic to filter the errors
collection. This is because a validation can be triggered by the page that contains the submit button. But by having each field subscribe to the validation callback this also means that as soon as I validate one of the fields in the blur
or change
handlers all of the fields are validated. I only want this full form validation when you try to submit the form.
The ValidationController and the ValidationRules are created and defined in the containing page.
I feel that I have over complicated this solution and that there must be something simpler?
FluentRules
from a duplicate package
node_modules/aurelia-validation/dist/commonjs/property-info.d.ts(3,11): error TS2451: Cannot redeclare block-scoped variable 'getContextFor'.
Does aurelia-validation require that every "property" referred to in .ensure(...)
is not null? I have the following piece of code
editinfoChanged(newEditInfo: any, oldValue: any) : void {
if (this.domainobject && newEditInfo) {
this.editinfo.forEach((field: { requisiteness: string; name: string | number; label: string }) => {
if (this.isMandatory(field.requisiteness)) {
ValidationRules.ensure(this.domainobject[field.name]).displayName(field.label).required().on(this.domainobject);
};
});
}
}
which fails and produces the following error message:
Uncaught TypeError: Cannot read property 'toString' of null
at PropertyAccessorParser.parse (aurelia-validation.js?f0d7:91)
at FluentEnsure.ensure (aurelia-validation.js?f0d7:1700)
at Function.ValidationRules.ensure (aurelia-validation.js?f0d7:1767)
at eval (new-item-view.ts?c01b:112)
at Array.forEach (<anonymous>)
at NewItemView.editinfoChanged (new-item-view.ts?c01b:110)
When I look at the value of this.domainobject[field.name]
(in the first iteration) it is null and this makes me wonder if that's the cause of the error?
hello guys can someone help me with "form-field-validation" i'm trying to validate a checkbox table the validation works but the red border css not working
in the noop.bind i passe the validated list
<div class="box stackpanel vertical fit form-field-validation" noop.bind="posteUFDTOList & validate:validationController">
<div class="box-header bg-primary stackpanel horizontal fit">
<input id="chx_all" class="form-control colSelection" type="checkbox" change.delegate="cocherDecocherTout()" />
<label for="chx_all" class="box-title" i18n="sante:ui.fwm.gestionutilisateur.postes.postedetail.cocherdecocher"></label>
</div>
</div>
if (this.posteUFDTOList) {
this.validationController.validate({ object: this.posteUFDTOList });
}
Hi,
I am a newbie in aurelia.
Aurelia Validation Demo - Nested object properties
https://gist.github.com/jsobell/d81428170f1572ae916e3fbf67779014
BootstrapFormRenderer example
https://gist.github.com/y2k4life/8adacec7627d792a304f62a899f21049
I am getting correct validation when using controller.validate() method
Problem is that in BootstrapFormRenderer
In render method, elements are always empty.
render(instruction: RenderInstruction) {
debugger;
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);
}
}
}