Get needed attribute working

Will cancel submit, shows validation message
This commit is contained in:
nathan 2022-02-24 15:52:45 -07:00
parent ce84dd753a
commit d9e95dae87
7 changed files with 133 additions and 25 deletions

View File

@ -10,7 +10,7 @@
import {css, html} from "@lion/core";
import {FormControlMixin} from "@lion/form-core";
import {FormControlMixin, ValidateMixin} from "@lion/form-core";
import 'lit-flatpickr';
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
import {dateStyles} from "./DateStyles";
@ -270,7 +270,7 @@ export function formatDateTime(date : Date, options = {dateFormat: "", timeForma
return formatDate(date, options) + " " + formatTime(date, options);
}
export class Et2Date extends Et2InputWidget(FormControlMixin(LitFlatpickr))
export class Et2Date extends Et2InputWidget(FormControlMixin(ValidateMixin(LitFlatpickr)))
{
static get styles()
{

View File

@ -1,7 +1,8 @@
import {et2_IInput, et2_IInputNode} from "../et2_core_interfaces";
import {et2_IInput, et2_IInputNode, et2_ISubmitListener} from "../et2_core_interfaces";
import {Et2Widget} from "../Et2Widget/Et2Widget";
import {dedupeMixin} from "@lion/core";
import {ManualMessage} from "./ManualMessage";
import {dedupeMixin, PropertyValues} from "@lion/core";
import {ManualMessage} from "../Validators/ManualMessage";
import {Required} from "../Validators/Required";
/**
* This mixin will allow any LitElement to become an Et2InputWidget
@ -37,7 +38,7 @@ export declare class Et2InputWidgetInterface
const Et2InputWidgetMixin = (superclass) =>
{
class Et2InputWidgetClass extends Et2Widget(superclass) implements et2_IInput, et2_IInputNode
class Et2InputWidgetClass extends Et2Widget(superclass) implements et2_IInput, et2_IInputNode, et2_ISubmitListener
{
protected _oldValue : string | number | Object;
protected node : HTMLElement;
@ -66,6 +67,11 @@ const Et2InputWidgetMixin = (superclass) =>
type: Boolean
},
needed: {
type: Boolean,
reflect: true
},
onchange: {
type: Function
},
@ -83,6 +89,32 @@ const Et2InputWidgetMixin = (superclass) =>
this.node = this.getInputNode();
}
/**
* A property has changed, and we want to make adjustments to other things
* based on that
*
* @param {import('@lion/core').PropertyValues } changedProperties
*/
updated(changedProperties : PropertyValues)
{
super.updated(changedProperties);
// Needed changed, add / remove validator
if(changedProperties.has('needed'))
{
// Remove class
this.classList.remove("et2_required")
// Remove all existing Required validators (avoids duplicates)
this.validators = (this.validators || []).filter((validator) => validator instanceof Required)
this.setAttribute("required", this.needed);
if(this.needed)
{
this.validators.push(new Required());
this.classList.add("et2_required");
}
}
}
/**
* Change handler calling custom handler set via onchange attribute
*
@ -169,12 +201,19 @@ const Et2InputWidgetMixin = (superclass) =>
this._oldValue = this.getValue();
}
/**
* Used by etemplate2 to determine if we can submit or not
*
* @param messages
* @returns {boolean}
*/
isValid(messages)
{
var ok = true;
debugger;
// Check for required
if(this.options && this.options.needed && !this.options.readonly && !this.disabled &&
if(this.needed && !this.readonly && !this.disabled &&
(this.getValue() == null || this.getValue().valueOf() == ''))
{
messages.push(this.egw().lang('Field must not be empty !!!'));
@ -215,6 +254,29 @@ const Et2InputWidgetMixin = (superclass) =>
// it won't show up without validate()
this.validate();
}
/**
* Called whenever the template gets submitted. We return false if the widget
* is not valid, which cancels the submission.
*
* @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted.
*/
async submit(_values) : Promise<boolean>
{
this.submitted = true;
// If using Lion validators, run them now
if(this.validate)
{
// Force update now
this.validate(true);
await this.validateComplete;
return (this.hasFeedbackFor || []).indexOf("error") == -1;
}
return true;
}
}
return Et2InputWidgetClass;

View File

@ -1156,6 +1156,17 @@ export function loadWebComponent(_nodeName : string, _template_node, parent : Et
return widget;
}
/**
* Take attributes from a node in a .xet file and apply those to a WebComponent widget
*
* Any attributes provided that match a property (or attribute) on the widget will be adjusted according to
* the passed arrayManager, coerced into the proper type, and set.
* It is here that we find values or set attributes that should come from content.
*
* @param widget
* @param {et2_arrayMgr} mgr
* @param attributes
*/
function transformAttributes(widget, mgr : et2_arrayMgr, attributes)
{

View File

@ -2,8 +2,7 @@ import {ResultValidator} from "@lion/form-core";
/**
* Manual validator for server-side validation messages passed
* from Etemplate. It is always "activated", and just gives whatever
* message is passed in when created.
* from Etemplate. It just gives whatever message is passed in when created.
*
*/
export class ManualMessage extends ResultValidator

View File

@ -0,0 +1,14 @@
import {Required as LionRequired} from "@lion/form-core";
export class Required extends LionRequired
{
/**
* Give a message about this field being required. Could be customised according to MessageData.
* @param {MessageData | undefined} data
* @returns {Promise<string>}
*/
static async getMessage(data)
{
return data.formControl.egw().lang("Field must not be empty !!!");
}
}

View File

@ -158,7 +158,7 @@ export interface et2_ISubmitListener
* @param _values contains the values which will be sent to the server.
* Listeners may change these values before they get submitted.
*/
submit(_values) : void
submit(_values) : boolean | Promise<boolean>
}
export const et2_ISubmitListener = "et2_ISubmitListener";
et2_implements_registry.et2_ISubmitListener = function(obj : et2_widget)

View File

@ -905,9 +905,9 @@ export class etemplate2
*
* @param container
* @param values
* @return et2_widget|null first invalid widget or null, if all are valid
* @return Promise<et2_widget>|Promise<Et2Widget>|null
*/
isInvalid(container : et2_container | undefined, values : object | undefined) : et2_widget | null
async isInvalid(container : et2_container | undefined, values : object | undefined) : Promise<et2_ISubmitListener> | null
{
if(typeof container === 'undefined')
{
@ -917,19 +917,27 @@ export class etemplate2
{
values = this.getValues(container);
}
let invalid = null;
let invalid = [];
container.iterateOver(function(_widget)
{
if(_widget.submit(values) === false)
let submit = _widget.submit(values);
if(submit === false)
{
if(!invalid && !_widget.isValid([]))
{
invalid = _widget;
invalid.push(_widget);
}
}
else if(submit instanceof Promise)
{
invalid.push(submit.then(function(sub)
{
return sub ? false : this;
}.bind(_widget)));
}
}, this, et2_ISubmitListener);
return invalid;
return Promise.all(invalid);
}
/**
@ -962,8 +970,27 @@ export class etemplate2
{
canSubmit = !(invalid = this.isInvalid(container, values));
}
invalid.then((widgets) =>
{
let invalid_widgets = widgets.filter((widget) => widget);
if(canSubmit)
if(invalid_widgets.length)
{
// Show the first invalid widget, not the last
if(invalid_widgets[0] && invalid_widgets[0] instanceof et2_widget)
{
let messages = [];
let valid = invalid.isValid(messages);
invalid.set_validation_error(messages);
}
}
else
{
doSubmit();
}
});
let doSubmit = function()
{
if(typeof async == 'undefined' || typeof async == 'string')
{
@ -996,14 +1023,9 @@ export class etemplate2
{
this._widgetContainer.egw().debug("warn", "Missing menuaction for submit. Values: ", values);
}
}
else if(invalid !== null)
{
// Show the first invalid widget, not the last
let messages = [];
let valid = invalid.isValid(messages);
invalid.set_validation_error(messages);
}
}.bind(this);
return canSubmit;
}