diff --git a/lib/entity.js b/lib/entity.js index 48f05d8..0c7124c 100644 --- a/lib/entity.js +++ b/lib/entity.js @@ -4,7 +4,7 @@ import { Events } from './entity/events' import { Attributes } from './entity/attributes' import { State } from './state' -export default class Entity { +export class Entity { constructor(el) { this.element = el this.tagName = el.tagName diff --git a/lib/entity/attributes.js b/lib/entity/attributes.js index 6ef9d13..883fec4 100644 --- a/lib/entity/attributes.js +++ b/lib/entity/attributes.js @@ -44,10 +44,19 @@ export class Attributes { } } + _handleError(attr, error) { + if (!this.base.isExists()) return + console.error( + `Failed to evaluate ${attr} for Entity#${this.base.id}:`, + error + ) + } + async evaluate() { await this.evaluateClass() await this.evaluateText() await this.evaluateValue() + await this.evaluateChecked() for (const attr of this.dynamicAttributes) { if (Attributes.CUSTOM_ATTRIBUTES.includes(attr)) continue @@ -62,7 +71,8 @@ export class Attributes { if (attr === ':parent') this.evaluateParent() else if (attr === ':class') await this.evaluateClass() else if (attr === ':text') await this.evaluateText() - else if ([':value', ':checked'].includes(attr)) await this.evaluateValue() + else if (attr === ':value') await this.evaluateValue() + else if (attr === ':checked') await this.evaluateChecked() else if (attr === ':each') await this.evaluateEach() else { if (!this.dynamicAttributes.includes(attr)) @@ -89,8 +99,7 @@ export class Attributes { this.base.element.setAttribute('class', updatedClassNames) } catch (error) { - if (!this.base.isExists()) return - throw error + this._handleError(':class', error) } } @@ -101,54 +110,55 @@ export class Attributes { try { const newText = await this.base._interpret(textExpr) - if (newText || newText == '') this.base.element.innerText = newText + if (newText || newText == '') this.base.element.textContent = newText } catch (error) { - if (!this.base.isExists()) return - throw error + this._handleError(':text', error) } } async evaluateValue() { + const valueExpr = this.base.element.getAttribute(':value') + if (!valueExpr) return try { - const valueExpr = this.base.element.getAttribute(':value') - - if (valueExpr) { - const newValue = await this.base._interpret(valueExpr) + const newValue = await this.base._interpret(valueExpr) - if (this.base.element.value !== newValue && newValue != null) - this.base.element.value = newValue - } + if (this.base.element.value !== newValue && newValue != null) + this.base.element.value = newValue + } catch (error) { + this._handleError(':value', error) + } + } - const checkedExpr = this.base.element.getAttribute(':checked') + async evaluateChecked() { + const checkedExpr = this.base.element.getAttribute(':checked') + if (!checkedExpr) return - if (checkedExpr) { - const newValue = await this.base._interpret(checkedExpr) + try { + const isChecked = await this.base._interpret(checkedExpr) - if (newValue) this.base.element.checked = newValue - } + if (this.base.element.checked !== isChecked && isChecked != null) + this.base.element.checked = isChecked } catch (error) { - if (!this.base.isExists()) return - throw error + this._handleError(':checked', error) } } async evaluateOtherAttributes() { - try { - for (const attr of this.dynamicAttributes) { - if (Attributes.CUSTOM_ATTRIBUTES.includes(attr)) continue + for (const attr of this.dynamicAttributes) { + if (Attributes.CUSTOM_ATTRIBUTES.includes(attr)) continue - const expr = this.base.element.getAttribute(attr) - if (!expr) return + const expr = this.base.element.getAttribute(attr) + if (!expr) return + try { const newValue = await this.base._interpret(expr) const nativeAttr = attr.slice(1) if (this.base.element[nativeAttr] !== newValue && newValue != null) this.base.element[nativeAttr] = newValue + } catch (error) { + this._handleError(attr, error) } - } catch (error) { - if (!this.base.isExists()) return - throw error } } @@ -159,19 +169,24 @@ export class Attributes { const [args, iterable] = eachExpr.split(' in ') const [variable, indexName] = args.split(',').map((v) => v.trim()) - const items = await this.base._interpret(iterable) - this.childClone ||= this.base.element.innerHTML - let newHTML = '' + try { + const items = await this.base._interpret(iterable) + this.childClone ||= this.base.element.innerHTML - items.forEach((item, index) => { - // TODO: Use the lexer to replace the variables - newHTML += this.childClone - .replaceAll(indexName, index) - .replaceAll(variable, `'${escapeHTML(item)}'`) - }) + let newHTML = '' - // ObserveDOM will be called for updated DOM to initialize the entities - this.base.element.innerHTML = newHTML + items.forEach((item, index) => { + // TODO: Use the lexer to replace the variables + newHTML += this.childClone + .replaceAll(indexName, index) + .replaceAll(variable, `'${escapeHTML(item)}'`) + }) + + // ObserveDOM will be called for updated DOM to initialize the entities + this.base.element.innerHTML = newHTML + } catch (error) { + this._handleError(':each', error) + } } } diff --git a/lib/entity/events.js b/lib/entity/events.js index 10b4244..ab18e9f 100644 --- a/lib/entity/events.js +++ b/lib/entity/events.js @@ -70,6 +70,14 @@ export class Events { ) } + _handleError(attr, error) { + if (!this.base.isExists()) return + console.error( + `Failed to evaluate ${attr} for Entity#${this.base.id}:`, + error + ) + } + apply() { this.dispose() this.evaluate(':load') @@ -240,7 +248,11 @@ export class Events { MiniJS.state.attachVariableHelpers(elVariables, this.base.id) } - await this.base._interpret(value) + try { + await this.base._interpret(value) + } catch (error) { + this._handleError(attr, error) + } } disposeEvent(attr) { diff --git a/lib/main.js b/lib/main.js index e415396..bcc705d 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,4 +1,4 @@ -import Entity from './entity' +import { Entity } from './entity' import { Lexer } from './generators/lexer' import { observeDOM } from './generators/observer' import { State } from './state'