Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: :checked attribute when set to false #12

Merged
merged 4 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
93 changes: 54 additions & 39 deletions lib/entity/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
jorenrui marked this conversation as resolved.
Show resolved Hide resolved

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
Expand All @@ -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))
Expand All @@ -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)
}
}

Expand All @@ -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
}
}

Expand All @@ -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)
}
jorenrui marked this conversation as resolved.
Show resolved Hide resolved
}
}
14 changes: 13 additions & 1 deletion lib/entity/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}
jorenrui marked this conversation as resolved.
Show resolved Hide resolved

apply() {
this.dispose()
this.evaluate(':load')
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion lib/main.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
Loading