💻 Backend JavaScript (Server-Side Execution)
Backend JavaScript allows you to execute JavaScript code during template rendering on the server. This code runs before the HTML is sent to the browser.
Basic Usage
<script:odac>
// This runs on the SERVER during template rendering
let total = 0;
for (let item of cart) {
total += item.price * item.quantity;
}
</script:odac>
<p>Total: $<odac var="total" /></p>
Key Characteristics
- ✅ Runs on the server during template rendering
- ✅ Has access to all backend variables and Odac object
- ✅ Perfect for calculations, data manipulation, filtering
- ✅ Full IDE syntax highlighting and autocomplete
- ❌ Does NOT run in the browser
- ❌ Cannot access browser APIs (window, document, localStorage, etc.)
When to Use Backend JavaScript
Use backend JavaScript for:
- Calculations: Totals, averages, statistics
- Data transformation: Filtering, sorting, mapping arrays
- Complex logic: Logic that's too complex for inline conditions
- Variable preparation: Creating temporary variables for display
Accessing Variables
You have access to all variables set in the controller:
// Controller
module.exports = async function(Odac) {
Odac.set('products', [
{ name: 'Laptop', price: 999, quantity: 2 },
{ name: 'Mouse', price: 29, quantity: 5 }
])
Odac.View.skeleton('main').set('content', 'cart')
}
<!-- View -->
<script:odac>
let total = 0;
let itemCount = 0;
for (let product of products) {
total += product.price * product.quantity;
itemCount += product.quantity;
}
const avgPrice = total / itemCount;
</script:odac>
<div class="cart-summary">
<p>Total Items: <odac var="itemCount" /></p>
<p>Total Price: $<odac var="total" /></p>
<p>Average Price: $<odac var="avgPrice.toFixed(2)" /></p>
</div>
Accessing the Odac Object
Full access to the Odac object and all its methods:
<script:odac>
const isLoggedIn = Odac.Auth.check();
const currentUser = isLoggedIn ? Odac.Auth.user() : null;
const requestMethod = Odac.Request.method;
const currentUrl = Odac.Request.url;
</script:odac>
<odac:if condition="isLoggedIn">
<p>Welcome, <odac var="currentUser.name" />!</p>
</odac:if>
Practical Examples
Shopping Cart Calculations
<script:odac>
let subtotal = 0;
let totalItems = 0;
for (let item of cart) {
subtotal += item.price * item.quantity;
totalItems += item.quantity;
}
const tax = subtotal * 0.18; // 18% tax
const shipping = subtotal > 100 ? 0 : 10;
const total = subtotal + tax + shipping;
</script:odac>
<div class="cart-summary">
<h3>Order Summary</h3>
<p>Items (<odac var="totalItems" />): $<odac var="subtotal.toFixed(2)" /></p>
<p>Tax (18%): $<odac var="tax.toFixed(2)" /></p>
<p>Shipping:
<odac:if condition="shipping === 0">
<span class="free">FREE</span>
<odac:else>
$<odac var="shipping.toFixed(2)" />
</odac:if>
</p>
<hr>
<p class="total">Total: $<odac var="total.toFixed(2)" /></p>
</div>
Filtering and Sorting
<script:odac>
// Filter active products
const activeProducts = products.filter(p => p.isActive && p.stock > 0);
// Sort by price
activeProducts.sort((a, b) => a.price - b.price);
// Get featured products
const featured = activeProducts.filter(p => p.featured).slice(0, 3);
// Calculate statistics
const avgPrice = activeProducts.reduce((sum, p) => sum + p.price, 0) / activeProducts.length;
const maxPrice = Math.max(...activeProducts.map(p => p.price));
const minPrice = Math.min(...activeProducts.map(p => p.price));
</script:odac>
<div class="products-section">
<h2>Featured Products</h2>
<p>Showing <odac var="featured.length" /> of <odac var="activeProducts.length" /> products</p>
<p>Price range: $<odac var="minPrice" /> - $<odac var="maxPrice" /></p>
<odac:for in="featured" value="product">
<div class="product">
<h3><odac var="product.name" /></h3>
<p>$<odac var="product.price" /></p>
<odac:if condition="product.price < avgPrice">
<span class="badge">Great Deal!</span>
</odac:if>
</div>
</odac:for>
</div>
Date and Time Formatting
<script:odac>
const now = new Date();
const postDate = new Date(post.createdAt);
// Calculate time difference
const diffMs = now - postDate;
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffMinutes = Math.floor(diffMs / (1000 * 60));
let timeAgo;
if (diffDays > 0) {
timeAgo = diffDays + ' days ago';
} else if (diffHours > 0) {
timeAgo = diffHours + ' hours ago';
} else if (diffMinutes > 0) {
timeAgo = diffMinutes + ' minutes ago';
} else {
timeAgo = 'Just now';
}
const formattedDate = postDate.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
</script:odac>
<div class="post">
<h2><odac var="post.title" /></h2>
<p class="meta">
Posted <odac var="timeAgo" /> (<odac var="formattedDate" />)
</p>
</div>
Grouping Data
<script:odac>
// Group products by category
const grouped = {};
for (let product of products) {
if (!grouped[product.category]) {
grouped[product.category] = [];
}
grouped[product.category].push(product);
}
// Sort categories
const categories = Object.keys(grouped).sort();
</script:odac>
<div class="products-by-category">
<odac:for in="categories" value="category">
<div class="category-section">
<h2><odac var="category" /></h2>
<p><odac var="grouped[category].length" /> products</p>
<odac:for in="grouped[category]" value="product">
<div class="product">
<h3><odac var="product.name" /></h3>
<p>$<odac var="product.price" /></p>
</div>
</odac:for>
</div>
</odac:for>
</div>
Pagination Logic
<script:odac>
const itemsPerPage = 10;
const currentPage = parseInt(Odac.Request.get('page')) || 1;
const totalItems = products.length;
const totalPages = Math.ceil(totalItems / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = Math.min(startIndex + itemsPerPage, totalItems);
const currentItems = products.slice(startIndex, endIndex);
const hasPrevious = currentPage > 1;
const hasNext = currentPage < totalPages;
</script:odac>
<div class="products">
<odac:for in="currentItems" value="product">
<div class="product">
<h3><odac var="product.name" /></h3>
</div>
</odac:for>
</div>
<div class="pagination">
<odac:if condition="hasPrevious">
<a href="?page=<odac var="currentPage - 1" />">Previous</a>
</odac:if>
<span>Page <odac var="currentPage" /> of <odac var="totalPages" /></span>
<odac:if condition="hasNext">
<a href="?page=<odac var="currentPage + 1" />">Next</a>
</odac:if>
</div>
Complex Conditional Logic
<script:odac>
const user = Odac.Auth.check() ? Odac.Auth.user() : null;
const canEdit = user && (
user.role === 'admin' ||
user.id === post.authorId
);
const canDelete = user && user.role === 'admin';
const canComment = user && !user.isBanned && post.commentsEnabled;
const showActions = canEdit || canDelete || canComment;
</script:odac>
<div class="post">
<h2><odac var="post.title" /></h2>
<p><odac var="post.content" /></p>
<odac:if condition="showActions">
<div class="actions">
<odac:if condition="canEdit">
<a href="/posts/<odac var="post.id" />/edit">Edit</a>
</odac:if>
<odac:if condition="canDelete">
<a href="/posts/<odac var="post.id" />/delete">Delete</a>
</odac:if>
<odac:if condition="canComment">
<a href="#comments">Add Comment</a>
</odac:if>
</div>
</odac:if>
</div>
Multiple Script Blocks
You can use multiple <script:odac> blocks in the same view:
<script:odac>
let total = 0;
</script:odac>
<odac:for in="items" value="item">
<div><odac var="item.name" /></div>
<script:odac>
total += item.price;
</script:odac>
</odac:for>
<p>Total: $<odac var="total" /></p>
Comparison with Client-Side JavaScript
Backend JavaScript (<script:odac>):
<script:odac>
// Runs on SERVER during rendering
const total = products.reduce((sum, p) => sum + p.price, 0);
</script:odac>
<p>Total: $<odac var="total" /></p>
Client-Side JavaScript (<script>):
<script>
// Runs in BROWSER after page loads
document.addEventListener('DOMContentLoaded', function() {
console.log('Page loaded');
// Can access browser APIs
localStorage.setItem('visited', 'true');
// Can manipulate DOM
document.querySelector('.button').addEventListener('click', function() {
alert('Clicked!');
});
});
</script>
Best Practices
- Keep it simple: Complex logic should be in controllers
- Use for calculations: Perfect for totals, averages, filtering
- Avoid heavy operations: Don't do database queries or API calls
- Use meaningful variable names: Make code self-documenting
- Comment when necessary: Explain complex calculations
Good:
<script:odac>
const discountedPrice = product.price * (1 - product.discount / 100);
const savings = product.price - discountedPrice;
</script:odac>
Avoid:
<script:odac>
// Don't do this - should be in controller
const users = await Odac.DB.users.get();
const apiData = await fetch('https://api.example.com/data');
</script:odac>
Common Use Cases
- ✅ Calculate totals and subtotals
- ✅ Filter and sort arrays
- ✅ Format dates and numbers
- ✅ Group and aggregate data
- ✅ Create temporary display variables
- ✅ Simple conditional logic
- ❌ Database queries (use controller)
- ❌ API calls (use controller)
- ❌ File operations (use controller)
- ❌ Heavy computations (use controller)