Enhance condition rendering: defer processing of %if directives until after initial DOM traversal
This commit is contained in:
parent
4a7737c74f
commit
6f3d40754a
42
src/main.ts
42
src/main.ts
|
@ -208,6 +208,7 @@ export default (options: ComponentOptions) => {
|
||||||
|
|
||||||
// Store nodes and expressions that need to be updated
|
// Store nodes and expressions that need to be updated
|
||||||
const textBindings: Array<{ node: Text, expr: string, originalContent: string }> = []
|
const textBindings: Array<{ node: Text, expr: string, originalContent: string }> = []
|
||||||
|
const ifDirectivesToProcess: Array<{ element: Element, expr: string }> = []
|
||||||
|
|
||||||
// Traverse the DOM tree
|
// Traverse the DOM tree
|
||||||
let currentNode: Node | null
|
let currentNode: Node | null
|
||||||
|
@ -247,66 +248,68 @@ export default (options: ComponentOptions) => {
|
||||||
|
|
||||||
// Handle element nodes (can extend to handle attribute bindings, etc.)
|
// Handle element nodes (can extend to handle attribute bindings, etc.)
|
||||||
else if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
else if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
||||||
// Handle element attribute bindings, such as <img src="{{ imageUrl }}">
|
|
||||||
const element = currentNode as Element
|
const currentElementNode = currentNode as Element // Renamed to avoid conflict with outer 'element'
|
||||||
|
|
||||||
// Traverse all marco attributes
|
// Traverse all marco attributes
|
||||||
|
|
||||||
// Detect :attr="" bindings, such as :src="imageUrl"
|
// Detect :attr="" bindings, such as :src="imageUrl"
|
||||||
Array.from(element.attributes).forEach(attr => {
|
Array.from(currentElementNode.attributes).forEach(attr => {
|
||||||
if (attr.name.startsWith(':')) {
|
if (attr.name.startsWith(':')) {
|
||||||
const attrName = attr.name.substring(1) // Remove ':'
|
const attrName = attr.name.substring(1) // Remove ':'
|
||||||
const expr = attr.value.trim()
|
const expr = attr.value.trim()
|
||||||
|
|
||||||
// Remove the attribute, as it is not a standard HTML attribute
|
// Remove the attribute, as it is not a standard HTML attribute
|
||||||
element.removeAttribute(attr.name)
|
currentElementNode.removeAttribute(attr.name)
|
||||||
|
|
||||||
// Set up attribute binding
|
// Set up attribute binding
|
||||||
this._setupAttributeBinding(element, attrName, expr, attr.value)
|
this._setupAttributeBinding(currentElementNode, attrName, expr, attr.value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Process @event bindings, such as @click="handleClick"
|
// Process @event bindings, such as @click="handleClick"
|
||||||
const eventBindings = Array.from(element.attributes).filter(attr => attr.name.startsWith('@'))
|
const eventBindings = Array.from(currentElementNode.attributes).filter(attr => attr.name.startsWith('@'))
|
||||||
eventBindings.forEach(attr => {
|
eventBindings.forEach(attr => {
|
||||||
const eventName = attr.name.substring(1) // Remove '@'
|
const eventName = attr.name.substring(1) // Remove '@'
|
||||||
const handlerValue = attr.value.trim()
|
const handlerValue = attr.value.trim()
|
||||||
|
|
||||||
// Remove the attribute, as it is not a standard HTML attribute
|
// Remove the attribute, as it is not a standard HTML attribute
|
||||||
element.removeAttribute(attr.name)
|
currentElementNode.removeAttribute(attr.name)
|
||||||
|
|
||||||
// Handle different types of event handlers
|
// Handle different types of event handlers
|
||||||
if (handlerValue.includes('=>')) {
|
if (handlerValue.includes('=>')) {
|
||||||
// Handle arrow function: @click="e => setState('count', count + 1)"
|
// Handle arrow function: @click="e => setState('count', count + 1)"
|
||||||
this._setupArrowFunctionHandler(element, eventName, handlerValue)
|
this._setupArrowFunctionHandler(currentElementNode, eventName, handlerValue)
|
||||||
} else if (handlerValue.includes('(') && handlerValue.includes(')')) {
|
} else if (handlerValue.includes('(') && handlerValue.includes(')')) {
|
||||||
// Handle function call: @click="increment(5)"
|
// Handle function call: @click="increment(5)"
|
||||||
this._setupFunctionCallHandler(element, eventName, handlerValue)
|
this._setupFunctionCallHandler(currentElementNode, eventName, handlerValue)
|
||||||
} else if (typeof (this as any)[handlerValue] === 'function') {
|
} else if (typeof (this as any)[handlerValue] === 'function') {
|
||||||
// Handle method reference: @click="handleClick"
|
// Handle method reference: @click="handleClick"
|
||||||
element.addEventListener(eventName, (this as any)[handlerValue].bind(this))
|
currentElementNode.addEventListener(eventName, (this as any)[handlerValue].bind(this))
|
||||||
} else {
|
} else {
|
||||||
// Handle simple expression: @click="count++" or @input="name = $event.target.value"
|
// Handle simple expression: @click="count++" or @input="name = $event.target.value"
|
||||||
this._setupExpressionHandler(element, eventName, handlerValue)
|
this._setupExpressionHandler(currentElementNode, eventName, handlerValue)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Process %-started marcos, such as %connect="stateName", %if="condition", %for="item in items"
|
// Process %-started marcos, such as %connect="stateName", %if="condition", %for="item in items"
|
||||||
const macroBindings = Array.from(element.attributes).filter(attr => attr.name.startsWith('%'))
|
const macroBindings = Array.from(currentElementNode.attributes).filter(attr => attr.name.startsWith('%'))
|
||||||
macroBindings.forEach(attr => {
|
macroBindings.forEach(attr => {
|
||||||
const macroName = attr.name.substring(1) // Remove '%'
|
const macroName = attr.name.substring(1) // Remove '%'
|
||||||
const expr = attr.value.trim()
|
const expr = attr.value.trim()
|
||||||
|
|
||||||
// Remove the attribute, as it is not a standard HTML attribute
|
// Remove the attribute, as it is not a standard HTML attribute
|
||||||
element.removeAttribute(attr.name)
|
currentElementNode.removeAttribute(attr.name)
|
||||||
|
|
||||||
// Handle different types of macros
|
// Handle different types of macros
|
||||||
if (macroName === 'connect') // Handle state connection: %connect="stateName"
|
if (macroName === 'connect') // Handle state connection: %connect="stateName"
|
||||||
this._setupTwoWayBinding(element, expr)
|
this._setupTwoWayBinding(currentElementNode, expr)
|
||||||
else if (macroName === 'if')
|
else if (macroName === 'if') {
|
||||||
this._setupConditionRendering(element, expr)
|
// Defer %if processing until after the initial traversal
|
||||||
|
ifDirectivesToProcess.push({ element: currentElementNode, expr })
|
||||||
|
}
|
||||||
else if (macroName === 'for')
|
else if (macroName === 'for')
|
||||||
this._setupAttributeBinding(element, 'data-laterano-for', expr, attr.value)
|
this._setupAttributeBinding(currentElementNode, 'data-laterano-for', expr, attr.value) // Placeholder, actual %for needs more complex logic
|
||||||
else
|
else
|
||||||
console.warn(`Unknown macro: %${macroName}`)
|
console.warn(`Unknown macro: %${macroName}`)
|
||||||
})
|
})
|
||||||
|
@ -317,6 +320,11 @@ export default (options: ComponentOptions) => {
|
||||||
|
|
||||||
// Save text binding relationships for updates
|
// Save text binding relationships for updates
|
||||||
this._textBindings = textBindings
|
this._textBindings = textBindings
|
||||||
|
|
||||||
|
// Process all collected %if directives after the main traversal
|
||||||
|
for (const { element: ifElement, expr } of ifDirectivesToProcess) {
|
||||||
|
this._setupConditionRendering(ifElement, expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle two-way data binding (%connect marco)
|
// Handle two-way data binding (%connect marco)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user