This page covers limitations of the frontend template engine (compared to full frameworks like Vue or Alpine) and common pitfalls with fixes.Documentation Index
Fetch the complete documentation index at: https://docs.elasticfunnels.io/llms.txt
Use this file to discover all available pages before exploring further.
Quick reference: common pitfalls
| Symptom | Cause | Fix |
|---|---|---|
| Wrong or double currency | Using formatPrice on already-formatted values | Use [[ checkout.total ]], [[ item.price ]] as-is; see Functions |
{{ x }} not replaced | Frontend engine uses [[ ]] only | Use [[ expression ]] for frontend; {{ }} is backend |
| UI doesn’t update after changing data | Path/expression mismatch or data shape issue | Verify the changed efScope path matches your binding expression and item/object shape |
| Single-product block wrong/missing | Using cartItems[0] | Use checkout.product_title, checkout.product_image, etc.; see Checkout |
Flash of raw [[ ... ]] | Content visible before engine runs | Add data-ef-cloak to elements; use CSS to hide [data-ef-cloak] |
| template-else not working | Else not a sibling of template-if | Put template-else / template-else-if as immediate siblings after template-if |
| Checkout validation not showing | Missing error container or wrong id | Use data-checkout-error=“<id>” and data-template-value per Checkout |
| Bump toggle/style not working | Missing data attributes | Use data-bump-code, data-bump-checkbox, data-bump-toggle; see Cart |
| Select options missing on Safari/iOS | template-foreach inside select | Use inline data-each on option directly; see Syntax — Inline data-each |
Limitations
Not a full framework
- No virtual DOM — Updates are reactive and path-scoped, but this is not a component framework with diffed virtual DOM.
- Element-level lifecycle only — You have
@mounted,@unmounted, and@visibleper element (see Bindings & Events), pluswindow.ef.template.watch(...)andwindow.ef.template.computed(...)for scope-driven reactions. There is no full component composition API (no slots, no scoped components, noprops). - No standalone template-else — A
<template-else>must be a sibling of a<template-if>(or follow template-else-if). You cannot have “else only” without an if.
Reactivity model
window.efScopeis reactive — changing nested keys/arrays updates bound UI automatically.- Path-scoped updates still exist — internals and advanced callers can use path-scoped updates for high-frequency flows; most custom code does not need manual notify calls.
Expression evaluation
- Expressions run in a merged context (scope + loop vars) using
with(scope) { return (expression); }. Very complex expressions or ones that rely on strict mode / undeclared variables can be fragile. Prefer simple paths and small expressions. - Reserved names in event handlers:
$event,event,$el,$targetare injected; avoid using them as scope keys if you need to read them from global scope.
Checkout and cart data
checkoutis only set on checkout pages. Any template that usescheckout.*on a non-checkout page may see undefined. Guard with<template-if data-condition="checkout">in shared components.query(URL params) is only available if the app setsefScope.queryat init (e.g. viasyncQueryToScope()). Conditions likequery.couponwill not work otherwise.
Common pitfalls
1. Using formatPrice on already-formatted values
Mistake: [[ formatPrice(checkout.total, checkout.currency) ]] or [[ formatPrice(item.price, item.currency) ]].
Why it’s wrong: checkout.total, item.price, item.total, bump.price, line.price are already formatted currency strings. Passing them to formatPrice can produce wrong or duplicated symbols.
Fix: Use [[ checkout.total ]], [[ item.price ]], [[ item.total ]], [[ bump.price ]], [[ line.price ]] as-is. Use formatPrice only for raw values (e.g. checkout.total_raw, bump.price_raw) when you need to display a number.
2. Using {{ }} in frontend templates
Mistake: Writing {{ variable }} in HTML that is processed by the frontend engine.
Why it’s wrong: {{ }} is reserved for the backend Template Engine. The frontend engine only replaces [[ ]].
Fix: Use [[ expression ]] for frontend token interpolation. For attribute bindings, prefer ef-*/data-ef-* (e.g. ef-text="checkout.total").
Summary
- Limitations: No virtual DOM/component lifecycle; template-else needs a sibling template-if; checkout and query are context-specific.
- Pitfalls: Don’t use formatPrice on formatted values; use [[ ]] not for frontend; keep data paths aligned with bindings; use checkout.* for single-product summary; use data-ef-cloak where needed; keep template-if/else structure and checkout/bump attributes as specified; never use template-foreach inside select — use inline data-each on option instead (Safari/iOS).