Templating Engine v9.3.4
Novaxjs2 v9.3.4 includes a powerful templating system with two rendering modes: HTML templates with custom syntax and JavaScript templates for maximum flexibility and control.
Setting Up Views
// Configure view engine (HTML mode)
app.setViewEngine('novax', {
viewsPath: './views', // Directory containing templates
viewsType: 'html', // 'html' or 'js'
helpers: { // Custom helper functions
formatDate: (date) => new Date(date).toLocaleDateString(),
uppercase: (str) => str.toUpperCase()
}
});
// Or for JavaScript templates:
app.setViewEngine('novax', {
viewsType: 'js', // Uses JavaScript files for templates
viewsPath: './templates'
});
// Add custom helpers programmatically
app.addHelper('reverse', (str) => str.split('').reverse().join(''));
app.addHelper('currency', (amount) => `$${amount.toFixed(2)}`);
// Add multiple helpers at once
app.addHelpers({
truncate: (str, length) => str.length > length ? str.substring(0, length) + '...' : str,
pluralize: (count, singular, plural) => count === 1 ? singular : plural
});
HTML Templates (New Syntax)
HTML templates use the new @ syntax for expressions and special tags for logic.
Basic Syntax
<!-- views/home.html -->
<!DOCTYPE html>
<html>
<head>
<title>@title</title>
</head>
<body>
<h1>@heading</h1>
<p>Current time: @{new Date().toLocaleTimeString()}</p>
<p>Formatted date: @formatDate(createdAt)</p>
</body>
</html> Conditional Logic
<!-- If/Else statements -->
@if(user.isAdmin)
<div class="admin-panel">Admin controls</div>
@else
<p>Regular user view</p>
@end
<!-- Complex conditions -->
@if((user.age >= 18) && (user.status === 'active'))
<p>Adult active user</p>
@end
<!-- Else if -->
@if(score >= 90)
<p>Grade: A</p>
@elif(score >= 80)
<p>Grade: B</p>
@else
<p>Grade: C</p>
@end Loops
<!-- Basic loop -->
<ul>
@each(items)
<li>@name - $@price</li>
@end
</ul>
<!-- Accessing array index -->
@each(items)
<div class="item-@index">
<h3>@name</h3>
<p>Position: @index of @{this.length}</p>
</div>
@end
<!-- Accessing array items by index -->
@each(items)
<p>Next item: @{this[index+1]?.name}</p>
@end Nested Data & Object Access
<!-- Nested object access -->
<p>Shipping to: @user.address.city, @user.address.country</p>
<!-- Array access -->
<p>First item: @{items[0].name}</p>
<!-- JSON output -->
<script>
const userData = @{JSON.stringify(user)};
</script>
<!-- Using helpers -->
<p>Price: @currency(product.price)</p>
<p>@truncate(description, 100)</p>
<p>@pluralize(items.length, 'item', 'items')</p> Variable Declaration
<!-- Declare variables -->
@var fullName = user.firstName + ' ' + user.lastName
@var discount = user.isPremium ? 0.2 : 0.1
<p>Welcome, @fullName!</p>
<p>Your discount: @{discount * 100}%</p>
<!-- Object destructuring -->
@var {city, country} = user.address
<p>Location: @city, @country</p>
<!-- Array destructuring -->
@var [firstItem, secondItem] = items
<p>First: @firstItem.name, Second: @secondItem.name</p> Template Includes
<!-- Include other templates -->
<!DOCTYPE html>
<html>
<head>
<title>@title</title>
</head>
<body>
@include('header.html', { user: user })
<main>
@include('content.html', {
content: pageContent,
sidebar: true
})
</main>
@include('footer.html')
</body>
</html> JavaScript Templates (CommonJS style)
For more complex logic, you can use JavaScript templates that export a function or string:
// views/home.js
module.exports = function(data) {
// Complex logic here
const welcomeMessage = data.user.isAdmin
? 'Welcome Administrator'
: `Welcome ${data.user.name}`;
// Helper function within template
function renderItems(items) {
return items.map(item => `
<div class="item">
<h3>${item.name}</h3>
<p>Price: $${item.price.toFixed(2)}</p>
<p>${item.description}</p>
</div>
`).join('');
}
return `
<!DOCTYPE html>
<html>
<head>
<title>${data.title}</title>
<style>
body { font-family: Arial, sans-serif; }
.item { border: 1px solid #ccc; padding: 1rem; margin: 1rem 0; }
</style>
</head>
<body>
<h1>${welcomeMessage}</h1>
${renderItems(data.items)}
<footer>
<p>Generated on ${new Date().toLocaleDateString()}</p>
</footer>
</body>
</html>
`;
}; Rendering Templates
// Basic rendering
app.get('/', async (req, res) => {
const data = {
title: 'Home Page',
user: {
name: 'John Doe',
isAdmin: false,
address: {
city: 'New York',
country: 'USA'
}
},
items: [
{ name: 'Product 1', price: 19.99, description: 'A great product' },
{ name: 'Product 2', price: 29.99, description: 'Another great product' }
],
createdAt: new Date()
};
const html = await app.render('home', data);
return html;
});
// With error handling
app.get('/profile', async (req, res) => {
try {
const userData = await getUserData(req.user.id);
return await app.render('profile', userData);
} catch (err) {
return await app.render('error', {
message: 'Failed to load profile',
error: err.message
});
}
});
// Using with other response methods
app.get('/api/user/:id', async (req, res) => {
const user = await getUserById(req.params.id);
if (!user) {
return res.status(404).render('404', { id: req.params.id });
}
return res.render('user-profile', { user });
}); Template Reference (New Syntax)
| Feature | Syntax | Description |
|---|---|---|
| Variables | @variable | Output variable value |
| Expressions | @{expression} | JavaScript expressions |
| Conditionals | @if(condition)...@end | Conditional rendering |
| Else/Elif | @else@elif(condition) | Else clauses for conditionals |
| Loops | @each(array)...@end | Iterate over arrays with index available |
| Array Access | @{this[index]} | Access array items by index in loops |
| JSON Output | @{JSON.stringify(data)} | Convert objects to JSON |
| Helpers | @helperName(arg) | Use custom helper functions |
| Variable Declaration | @var name = value | Declare template variables |
| Template Includes | @include('template.html', data) | Include other templates |
| JavaScript | module.exports = function(data) {...} | Full JavaScript templates when using viewsType: 'js' |
Advanced Features
// Using third-party view engines
const pug = require('pug');
app.setViewEngine(pug, {
viewsPath: './views',
viewsType: 'pug',
engineOptions: {
pretty: true
}
});
// Async helpers
app.addHelper('fetchData', async (url) => {
const response = await fetch(url);
return await response.json();
});
// Template inheritance (using JavaScript templates)
// base-template.js
module.exports = function(data, content) {
return `
<!DOCTYPE html>
<html>
<head>
<title>${data.title}</title>
</head>
<body>
<header>...</header>
<main>${content}</main>
<footer>...</footer>
</body>
</html>
`;
};
// page.js
module.exports = async function(data) {
const baseTemplate = require('./base-template');
const pageContent = `<h1>${data.title}</h1><p>${data.content}</p>`;
return baseTemplate(data, pageContent);
}; What's New in v9.3.4 Templating
- New
@syntax for cleaner template expressions - Variable declaration with
@var - Template includes with
@include - Object and array destructuring support
- Improved performance for template rendering
- Enhanced error handling in template compilation
- Better support for async helpers
- Improved documentation and examples