UNPKG

adhara

Version:

foundation for any kind of website: microframework

344 lines (289 loc) 10.7 kB
class FormField extends AdharaView{ /** * @typedef {Function} OnFieldValueChangeCallback * @param {*} old_value - old value * @param {*} new_value - new value * @param {Object} event_data - data for the event. * @param {String} event_data.initiator - sender tag who is initiating this event self and external will be set by default * */ /** * @typedef {Function} FieldValidator * @param {*} value - value * @param {AdharaMutableView} mutator - Instance of Mutator * */ /** * @constructor * @param {String} name - field name * @param {Object} [config={}] * @param {*} [config.value=undefined] - A value consumable by form field. A string for input type text and a number for input type number * @param {Map} [config.label_attributes={}] * @param {Array} [config.label_properties=[]] * @param {Map} [config.attributes={}] * @param {Array} [config.properties=[]] contentContainer * @param {String} [config.help_text=null] * @param {Boolean} [config.readonly=false] * @param {String} [config.input_type=''] * @param {String} [config.display_name=<i18n of form_name.field_name.label>] - display name of the field * @param {boolean} [config.nullable=true] - whether the field is nullable or not * @param {boolean} [config.editable=true] - whether the field is editable or not. Used for display only purposes in details page, etc * @param {Function} [config.nullable=true] - whether the field is nullable or not * @param {FieldValidator} [config.validator=null] - Field validator * @param {Array<String>} [config.depends_on=null] - field depends on another field * @param {OnFieldValueChangeCallback} [config.onChange=null] - Callback function for on change * @param {OnFieldValueChangeCallback} [config.postChange=null] - a delayed Callback function for on change event * @param {OnFieldValueChangeCallback} [config.onDependentParentChanged=null] - Callback function for on dependent change * @param {Object} [settings] * @param {String} [settings.key=undefined] - Instance key * @param {String} settings.c - CSS Selector from parent view to place content of this class * */ constructor(name, config = {}, settings){ settings = settings || {}; super(settings); this.name = name; this._value = config.value; this.readonly = config.readonly || false; this.config = config || {}; this.field_errors = []; /** * @field {Array<FormField>} dependent form field instances * */ this._dependent_fields = []; /** * {AdharaFormView} form * */ this.mutator = this.config.mutator || null; } get parentContainer(){ return super.parentContainer || `[data-field="${this.name}"]`; } set parentContainer(_){ super.parentContainer = _; } set mutator(_){ this._mutator = _; } get mutator(){ return this._mutator; } clone(key){ return new (this.constructor)(this.name, Object.assign({}, cloneObject(this.config)), Object.assign({}, cloneObject(this.settings), {key})); } get template(){ return "adhara-form-fields/index"; } get inputType(){ return this.config.input_type; } get labelTemplate(){ return 'adhara-form-fields/label'; } get prependTemplate(){ return this.config.prependTemplate; } get appendTemplate(){ if(this.config.appendTemplate && this.config.appendTemplate.call){ return this.config.appendTemplate(); } return this.config.appendTemplate; } get fieldErrorsTemplate(){ return 'adhara-form-fields/error'; } validate(){ if(this.isRequired && this.isEmpty){ this.field_errors.push(Adhara.i18n.get(`${this.mutatorName}.${this.name}.error`, 'This field is required')); } this.field_errors.push(...( (this.config.validator && this.config.validator(this.value, this.mutator)) || [] )); this.refreshErrors(); } refreshErrors(){ let errors = this.querySelector('.invalid-feedback'); if(this.field_errors.length) { errors.classList.add("d-none"); }else{ errors.classList.remove("d-none"); } errors.innerHTML = this.field_errors.join(", "); } clearErrors(){ this.field_errors = []; this.refreshErrors(); } /** * @getter * @returns {HandlebarTemplate} - template for the field * */ get fieldTemplate(){ if(this.isEditable) return this.editableFieldTemplate; return this.nonEditableFieldTemplate; } get editableFieldTemplate(){ } get nonEditableFieldTemplate(){ return 'adhara-form-fields/non-editable'; } get helpTemplate(){ return 'adhara-form-fields/help'; } get safeName(){ return this.name.replace(/\./g, '-'); } get showLabel(){ return this.config.label !== false; } get isRequired(){ return this.config.required || this.fieldProperties.indexOf("required")!==-1; } get isMultiple(){ return this.config.multiple || this.fieldProperties.indexOf("multiple")!==-1; } get isReadOnly(){ return this.config.readonly || this.fieldProperties.indexOf("readonly")!==-1; } get displayName(){ return this.config.display_name || Adhara.i18n.get(`${this.mutatorName}.${this.name}.label`); } get labelAttributes() { let _ = Object.assign({ for: this.safeName, }, this.config.label_attributes); if(this.isRequired){ _.class = _.class || []; _.class.push("required"); } return _; } get labelProperties() { return (this.config.label_properties || []).slice(); } set placeholder(_){ this.config.placeholder = _; } get mutatorName(){ return this.mutator?this.mutator.fullName:''; } get hasFieldErrors(){ return this.field_errors.length; } get placeholder(){ //placeholder can be disabled by setting config.placeholder to false to by enabling label let _p = ""; if(this.config.placeholder || (!this.showLabel && this.config.placeholder!==false)){ if(typeof this.config.placeholder === "string"){ _p = this.config.placeholder; }else{ _p = Adhara.i18n.get([this.mutatorName, this.name, 'placeholder'].filter(_=>_).join('.')); } } return (this.isRequired && _p)?`${_p} *`:_p; } get defaultFieldAttributes(){ if(this.isEditable) return {class: "form-control"}; return {}; } get fieldAttributes() { return Object.assign({ id: this.safeName, name: this.safeName, placeholder: this.placeholder || "", // title: this.value?this.value:this.placeholder }, this.config.attributes || this.defaultFieldAttributes); } get fieldProperties() { let _p = (this.config.properties || []).slice(); if(_p.indexOf("required") !== -1) _p.splice(_p.indexOf("required"), 1); if(this.config.multiple) _p.push("multiple"); if(this.config.readonly) _p.push("readonly"); return _p; } get helpText(){ return this.config.help_text; } get isNullable() { return this.config.nullable===true; } get isEditable(){ return this.config.editable===undefined && this.mutator.isExclusivelyEditable; } get dependsOn(){ return this.config.depends_on || []; } addToDependentFields(field){ if(this._dependent_fields.indexOf(field) === -1){ this._dependent_fields.push(field); } } /** * @returns {HTMLElement} dom element of the field that is rendered by the fieldTemplate * */ getField(){ return document.querySelector(this.parentContainer+" [name='"+this.safeName+"']"); } queryValue(){ return this.getField().value; } queryRaw(){ return this.queryValue(); } onDataChange(event, event_data){ this.handleDataChange(this.queryValue(), this.value, event_data); } handleDataChange(value, old_value, event_data={}){ event_data.initiator = event_data.initiator || "self"; if(this.config.onChange){ this.config.onChange(value, old_value, event_data); } for(let dependent_field of this._dependent_fields){ let d_event_data = Object.assign({}, event_data, {initiator: "dependency"}); let clearValue = true; if(dependent_field.config.onDependentParentChanged){ clearValue = dependent_field.config.onDependentParentChanged(value, old_value, d_event_data); } if(event_data.initiator==="self" && clearValue!==false){ let clearDependencyChain = (_d_field) => { _d_field.changeData(null, d_event_data); for(let _nested_dependent_field of _d_field._dependent_fields){ clearDependencyChain(_nested_dependent_field); } }; clearDependencyChain(dependent_field); } } this.value = value; if(this.field_errors.length) this.validate(); this.mutator._onFieldValueChanged(this.name, this.value, old_value, event_data); setTimeout(()=>{ if(this.config.postChange){ this.config.postChange(value, old_value, event_data); } }, 0); } changeData(value, event_data={}){ event_data = Object.assign({}, this.getField().dataset, event_data); event_data.initiator = event_data.initiator || "external"; this.handleDataChange(value, this.value, event_data); this.setState(); } get isEmpty(){ if(typeof this.value === "boolean") return false; if(this.value instanceof Array) return !this.value.length; return !this.value; } get isBlank(){ if(this.value === null || this.value === undefined) return true; if(typeof this.value === "string") return !this.value.trim(); return false; } set value(_){ this._value = _; } get value(){ return this._value; } serialize(){ if(this.value===undefined) this.value = this.queryValue(); return this.value; } }