import {ObjectHelper} from '../helpers/object-helper'; import {Indexable} from '../interfaces'; /** * Binding for Form Fields */ export class VetproviehBinding { // Object which is used for the Binding private _targetObject: any; // Property which is used by the Target Object private _targetProperty: any; // Bindings to input-elements private _bindings: any[]; /** * Default Constructor needs object and a property to bind * @param {any} target * @param {target} property */ constructor(target: any, property: any) { this._targetObject = target; this._targetProperty = property; this._bindings = []; } /** * Clear all Bindings */ public clear() { this._bindings = []; } /** * Gets current property value * @return {any} */ get value(): any { return ObjectHelper.get(this._targetObject, this._targetProperty); } /** * Sets current property value * @param {any} val */ set value(val: any) { if (val !== this.value) { ObjectHelper.set(this._targetObject, this._targetProperty, val); this._updateBindings(val); } } /** * Set targetObject * @param {any} val */ set targetObject(val: any) { if (this._targetObject !== val) { this._targetObject = val; } } /** * Add Binding to HTML-Field * @param {HTMLElement} element * @param {string} attribute * @param {string} event * @return {VetproviehBinding} */ addBinding(element: HTMLElement, attribute: string, event: string) { // if (!this._alreadyBound(element, attribute)) { this._attachListenerToEvent(element, event, attribute); this._addToBindings(element, attribute); // } if (this.value) (element as any)[attribute] = this.value; return this; } /** * Is a HTMLElement for a property already bound? * @param {HTMLElement} element * @param {String} attribute * @return {boolean} * @private */ private _alreadyBound(element: HTMLElement, attribute: string): boolean { return this._bindings .findIndex((b: { element: HTMLElement, attribute: string }) => { return this._compareElement(element, b, attribute); }) >= 0; } /** * @private * @param {HTMLElement} a * @param {{ element: HTMLElement, attribute: string }} b * @param {String} attribute * @return {boolean} */ private _compareElement( a: HTMLElement, b: { element: HTMLElement, attribute: string }, attribute: string) : boolean { return b.element.tagName === a.tagName && b.attribute === attribute; } /** * Insert into internal datastructure * @param {HTMLElement} element * @param {string} attribute * @private */ _addToBindings(element: HTMLElement, attribute: string) { this._bindings.push({ element: element, attribute: attribute, }); } /** * Attach EventListener * @param {HTMLElement} element * @param {string} event * @param {string} attribute * @private */ _attachListenerToEvent(element: HTMLElement, event: string, attribute: string) { element.addEventListener(event, () => { this.value = (element as Indexable)[attribute]; }); } /** * Update all set Bindings * @param {any} val * @private */ _updateBindings(val: any) { this._bindings.forEach((binding) => { binding.element[binding.attribute] = val; }); } public static currentBindings: any = {}; /** * Binding Form Elements * @param {HTMLElement|any} target (Target - HTMLElement) * @param {any} data (DataObject) * @param {String} prefix * @param {any} listener */ public static bindFormElements( target: any, data: any, prefix = '', listener: (x: HTMLElement) => void = () => null) { Object.keys(data).forEach((key: string) => { if (ObjectHelper.isObject(data[key]) && !Array.isArray(data[key])) { this.bindFormElements( target, data[key], (prefix != '' ? prefix : '') + key + '.', listener); } else { if (Array.isArray(data[key])) { this.bindArray(data[key], key, target, listener); } const element = target .querySelector('[property="' + prefix + key + '"]'); if (element) { if (target) this.setUniqueIdentifier(target); const binding = VetproviehBinding .bindElement(prefix, key, target['uniqueId'] || target.id, data); this._addBinding(element, binding, listener); } } }); } private static uniqueIdentifier = 0; /** * Setting a unique Identifier * @private * @param {HTMLElement} element */ private static setUniqueIdentifier(element: HTMLElement) { if (!element.id) { element.id = 'id_' + VetproviehBinding.uniqueIdentifier++; } } /** * Binding an Element * @param {String} prefix * @param {String} key * @param {String} targetId * @param {any} data * @return {VetproviehBinding} */ private static bindElement( prefix: string, key: string, targetId: string, data: any): VetproviehBinding { const bKey = targetId + '-' + prefix + key; let binding = VetproviehBinding .currentBindings[bKey] as VetproviehBinding; if (!binding) { binding = new VetproviehBinding(data, key); VetproviehBinding.currentBindings[bKey] = binding; } else { binding.targetObject = data; } return binding; } /** * Bind Array * @param {Array} array * @param {string} key * @param {any} target * @param {any} listener */ private static bindArray( array: any[], key: string, target: any, listener: (x: HTMLElement) => void) { for (let i = 0; i < array.length; i++) { this.bindFormElements(target, array[i], `${key}[${i}].`, listener); } } /** * Attach Binding to Element * @param {any} element * @param {VetproviehBinding} binding * @param {any} listener */ private static _addBinding( element: any, binding: VetproviehBinding, listener: (x: HTMLElement) => void) { switch (element.type) { case 'checkbox': binding.addBinding(element, 'checked', 'change'); break; default: binding.addBinding(element, 'value', 'change'); break; } if (listener) { listener(element); } } }