odac 0.9.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/auto-pr-description.yml +0 -2
- package/.github/workflows/codeql.yml +46 -0
- package/.github/workflows/release.yml +13 -6
- package/.github/workflows/test-coverage.yml +10 -9
- package/.releaserc.js +9 -6
- package/CHANGELOG.md +72 -150
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +8 -8
- package/LICENSE +21 -661
- package/README.md +12 -12
- package/SECURITY.md +4 -4
- package/bin/odac.js +81 -0
- package/{framework/web/candy.js → client/odac.js} +310 -44
- package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
- package/docs/backend/01-overview/03-development-server.md +11 -11
- package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
- package/docs/backend/03-config/00-configuration-overview.md +6 -6
- package/docs/backend/03-config/01-database-connection.md +1 -1
- package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
- package/docs/backend/03-config/04-environment-variables.md +20 -20
- package/docs/backend/03-config/05-early-hints.md +4 -4
- package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
- package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
- package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
- package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
- package/docs/backend/04-routing/05-advanced-routing.md +3 -3
- package/docs/backend/04-routing/06-error-pages.md +17 -17
- package/docs/backend/04-routing/07-cron-jobs.md +13 -13
- package/docs/backend/04-routing/08-middleware.md +214 -0
- package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
- package/docs/backend/04-routing/09-websocket-examples.md +381 -0
- package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
- package/docs/backend/04-routing/09-websocket.md +298 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
- package/docs/backend/05-controllers/03-controller-classes.md +19 -19
- package/docs/backend/05-forms/01-custom-forms.md +114 -114
- package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
- package/docs/backend/07-views/01-the-view-directory.md +1 -1
- package/docs/backend/07-views/02-rendering-a-view.md +22 -22
- package/docs/backend/07-views/03-template-syntax.md +52 -52
- package/docs/backend/07-views/03-variables.md +84 -84
- package/docs/backend/07-views/04-request-data.md +57 -57
- package/docs/backend/07-views/05-conditionals.md +78 -78
- package/docs/backend/07-views/06-loops.md +114 -114
- package/docs/backend/07-views/07-translations.md +66 -66
- package/docs/backend/07-views/08-backend-javascript.md +103 -103
- package/docs/backend/07-views/09-comments.md +71 -71
- package/docs/backend/08-database/01-database-connection.md +8 -8
- package/docs/backend/08-database/02-using-mysql.md +49 -49
- package/docs/backend/09-validation/01-the-validator-service.md +38 -38
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
- package/docs/backend/10-authentication/03-register.md +12 -12
- package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
- package/docs/backend/10-authentication/05-session-management.md +10 -10
- package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
- package/docs/backend/11-mail/01-the-mail-service.md +5 -5
- package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
- package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
- package/docs/frontend/01-overview/01-introduction.md +30 -30
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
- package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
- package/docs/frontend/03-forms/01-form-handling.md +32 -32
- package/docs/frontend/04-api-requests/01-get-post.md +33 -33
- package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
- package/docs/frontend/06-websocket/00-overview.md +76 -0
- package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
- package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
- package/docs/index.json +49 -11
- package/eslint.config.mjs +6 -6
- package/{framework/index.js → index.js} +1 -1
- package/package.json +14 -39
- package/{framework/src → src}/Auth.js +59 -59
- package/{framework/src → src}/Config.js +3 -3
- package/{framework/src → src}/Lang.js +7 -7
- package/{framework/src → src}/Mail.js +5 -5
- package/{framework/src → src}/Mysql.js +42 -42
- package/src/Odac.js +112 -0
- package/{framework/src → src}/Request.js +38 -36
- package/{framework/src → src}/Route/Internal.js +116 -116
- package/src/Route/Middleware.js +75 -0
- package/src/Route.js +621 -0
- package/src/Server.js +22 -0
- package/{framework/src → src}/Stream.js +11 -3
- package/{framework/src → src}/Validator.js +21 -21
- package/{framework/src → src}/Var.js +5 -5
- package/{framework/src → src}/View/EarlyHints.js +1 -1
- package/{framework/src → src}/View/Form.js +69 -69
- package/{framework/src → src}/View.js +78 -81
- package/src/WebSocket.js +403 -0
- package/template/config.json +5 -0
- package/{web → template}/controller/page/about.js +6 -6
- package/{web → template}/controller/page/index.js +9 -9
- package/{web → template}/package.json +4 -5
- package/{web → template}/public/assets/css/style.css +4 -4
- package/{web → template}/public/assets/js/app.js +6 -6
- package/{web → template}/route/www.js +6 -6
- package/{web → template}/skeleton/main.html +1 -1
- package/{web → template}/view/content/about.html +5 -5
- package/{web → template}/view/content/home.html +12 -12
- package/template/view/footer/main.html +11 -0
- package/{web → template}/view/head/main.html +1 -1
- package/{web → template}/view/header/main.html +2 -2
- package/test/core/Candy.test.js +58 -58
- package/test/core/Commands.test.js +7 -7
- package/test/core/Config.test.js +82 -85
- package/test/core/Lang.test.js +2 -2
- package/test/core/Process.test.js +6 -6
- package/test/framework/Route.test.js +56 -37
- package/test/framework/View/EarlyHints.test.js +2 -2
- package/test/framework/WebSocket.test.js +100 -0
- package/test/framework/middleware.test.js +85 -0
- package/test/server/Api.test.js +31 -31
- package/test/server/DNS.test.js +11 -11
- package/test/server/Hub.test.js +497 -0
- package/test/server/Mail.account.test_.js +3 -3
- package/test/server/Mail.init.test_.js +10 -10
- package/test/server/Mail.test_.js +20 -20
- package/test/server/SSL.test_.js +54 -54
- package/test/server/Server.test.js +39 -39
- package/test/server/Service.test_.js +7 -7
- package/test/server/Subdomain.test.js +7 -7
- package/test/server/Web/Firewall.test.js +87 -87
- package/test/server/Web/Proxy.test.js +397 -0
- package/test/server/{Web.test_.js → Web.test.js} +137 -205
- package/test/server/__mocks__/fs.js +2 -2
- package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
- package/test/server/__mocks__/index.js +6 -6
- package/test/server/__mocks__/testFactories.js +1 -1
- package/test/server/__mocks__/testHelpers.js +7 -7
- package/.husky/pre-commit +0 -2
- package/.kiro/steering/code-style.md +0 -56
- package/.kiro/steering/product.md +0 -20
- package/.kiro/steering/structure.md +0 -77
- package/.kiro/steering/tech.md +0 -87
- package/AGENTS.md +0 -84
- package/bin/candy +0 -10
- package/bin/candypack +0 -10
- package/cli/index.js +0 -3
- package/cli/src/Cli.js +0 -348
- package/cli/src/Connector.js +0 -93
- package/cli/src/Monitor.js +0 -416
- package/core/Candy.js +0 -87
- package/core/Commands.js +0 -239
- package/core/Config.js +0 -1094
- package/core/Lang.js +0 -52
- package/core/Log.js +0 -43
- package/core/Process.js +0 -26
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
- package/docs/server/01-installation/01-quick-install.md +0 -19
- package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
- package/docs/server/02-get-started/01-core-concepts.md +0 -7
- package/docs/server/02-get-started/02-basic-commands.md +0 -57
- package/docs/server/02-get-started/03-cli-reference.md +0 -276
- package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
- package/docs/server/03-service/01-start-a-new-service.md +0 -57
- package/docs/server/03-service/02-delete-a-service.md +0 -48
- package/docs/server/04-web/01-create-a-website.md +0 -36
- package/docs/server/04-web/02-list-websites.md +0 -9
- package/docs/server/04-web/03-delete-a-website.md +0 -29
- package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
- package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
- package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
- package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
- package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
- package/docs/server/07-mail/04-change-account-password.md +0 -23
- package/framework/src/Candy.js +0 -81
- package/framework/src/Route.js +0 -455
- package/framework/src/Server.js +0 -15
- package/locale/de-DE.json +0 -80
- package/locale/en-US.json +0 -79
- package/locale/es-ES.json +0 -80
- package/locale/fr-FR.json +0 -80
- package/locale/pt-BR.json +0 -80
- package/locale/ru-RU.json +0 -80
- package/locale/tr-TR.json +0 -85
- package/locale/zh-CN.json +0 -80
- package/server/index.js +0 -5
- package/server/src/Api.js +0 -88
- package/server/src/DNS.js +0 -940
- package/server/src/Hub.js +0 -535
- package/server/src/Mail.js +0 -571
- package/server/src/SSL.js +0 -180
- package/server/src/Server.js +0 -27
- package/server/src/Service.js +0 -248
- package/server/src/Subdomain.js +0 -64
- package/server/src/Web/Firewall.js +0 -170
- package/server/src/Web/Proxy.js +0 -134
- package/server/src/Web.js +0 -451
- package/server/src/mail/imap.js +0 -1091
- package/server/src/mail/server.js +0 -32
- package/server/src/mail/smtp.js +0 -786
- package/test/server/Client.test.js +0 -338
- package/test/server/__mocks__/http-proxy.js +0 -105
- package/watchdog/index.js +0 -3
- package/watchdog/src/Watchdog.js +0 -156
- package/web/config.json +0 -5
- package/web/view/footer/main.html +0 -11
- /package/{framework/src → src}/Env.js +0 -0
- /package/{framework/src → src}/Route/Cron.js +0 -0
- /package/{framework/src → src}/Token.js +0 -0
|
@@ -5,21 +5,21 @@ Backend JavaScript allows you to execute JavaScript code during template renderi
|
|
|
5
5
|
### Basic Usage
|
|
6
6
|
|
|
7
7
|
```html
|
|
8
|
-
<script:
|
|
8
|
+
<script:odac>
|
|
9
9
|
// This runs on the SERVER during template rendering
|
|
10
10
|
let total = 0;
|
|
11
11
|
for (let item of cart) {
|
|
12
12
|
total += item.price * item.quantity;
|
|
13
13
|
}
|
|
14
|
-
</script:
|
|
14
|
+
</script:odac>
|
|
15
15
|
|
|
16
|
-
<p>Total: $<
|
|
16
|
+
<p>Total: $<odac var="total" /></p>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
### Key Characteristics
|
|
20
20
|
|
|
21
21
|
- ✅ Runs on the **server** during template rendering
|
|
22
|
-
- ✅ Has access to all backend variables and
|
|
22
|
+
- ✅ Has access to all backend variables and Odac object
|
|
23
23
|
- ✅ Perfect for calculations, data manipulation, filtering
|
|
24
24
|
- ✅ Full IDE syntax highlighting and autocomplete
|
|
25
25
|
- ❌ Does NOT run in the browser
|
|
@@ -39,19 +39,19 @@ You have access to all variables set in the controller:
|
|
|
39
39
|
|
|
40
40
|
```javascript
|
|
41
41
|
// Controller
|
|
42
|
-
module.exports = async function(
|
|
43
|
-
|
|
42
|
+
module.exports = async function(Odac) {
|
|
43
|
+
Odac.set('products', [
|
|
44
44
|
{ name: 'Laptop', price: 999, quantity: 2 },
|
|
45
45
|
{ name: 'Mouse', price: 29, quantity: 5 }
|
|
46
46
|
])
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
Odac.View.skeleton('main').set('content', 'cart')
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
```html
|
|
53
53
|
<!-- View -->
|
|
54
|
-
<script:
|
|
54
|
+
<script:odac>
|
|
55
55
|
let total = 0;
|
|
56
56
|
let itemCount = 0;
|
|
57
57
|
|
|
@@ -61,30 +61,30 @@ module.exports = async function(Candy) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const avgPrice = total / itemCount;
|
|
64
|
-
</script:
|
|
64
|
+
</script:odac>
|
|
65
65
|
|
|
66
66
|
<div class="cart-summary">
|
|
67
|
-
<p>Total Items: <
|
|
68
|
-
<p>Total Price: $<
|
|
69
|
-
<p>Average Price: $<
|
|
67
|
+
<p>Total Items: <odac var="itemCount" /></p>
|
|
68
|
+
<p>Total Price: $<odac var="total" /></p>
|
|
69
|
+
<p>Average Price: $<odac var="avgPrice.toFixed(2)" /></p>
|
|
70
70
|
</div>
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
### Accessing the
|
|
73
|
+
### Accessing the Odac Object
|
|
74
74
|
|
|
75
|
-
Full access to the
|
|
75
|
+
Full access to the Odac object and all its methods:
|
|
76
76
|
|
|
77
77
|
```html
|
|
78
|
-
<script:
|
|
79
|
-
const isLoggedIn =
|
|
80
|
-
const currentUser = isLoggedIn ?
|
|
81
|
-
const requestMethod =
|
|
82
|
-
const currentUrl =
|
|
83
|
-
</script:
|
|
84
|
-
|
|
85
|
-
<
|
|
86
|
-
<p>Welcome, <
|
|
87
|
-
</
|
|
78
|
+
<script:odac>
|
|
79
|
+
const isLoggedIn = Odac.Auth.check();
|
|
80
|
+
const currentUser = isLoggedIn ? Odac.Auth.user() : null;
|
|
81
|
+
const requestMethod = Odac.Request.method;
|
|
82
|
+
const currentUrl = Odac.Request.url;
|
|
83
|
+
</script:odac>
|
|
84
|
+
|
|
85
|
+
<odac:if condition="isLoggedIn">
|
|
86
|
+
<p>Welcome, <odac var="currentUser.name" />!</p>
|
|
87
|
+
</odac:if>
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
### Practical Examples
|
|
@@ -92,7 +92,7 @@ Full access to the Candy object and all its methods:
|
|
|
92
92
|
#### Shopping Cart Calculations
|
|
93
93
|
|
|
94
94
|
```html
|
|
95
|
-
<script:
|
|
95
|
+
<script:odac>
|
|
96
96
|
let subtotal = 0;
|
|
97
97
|
let totalItems = 0;
|
|
98
98
|
|
|
@@ -104,28 +104,28 @@ Full access to the Candy object and all its methods:
|
|
|
104
104
|
const tax = subtotal * 0.18; // 18% tax
|
|
105
105
|
const shipping = subtotal > 100 ? 0 : 10;
|
|
106
106
|
const total = subtotal + tax + shipping;
|
|
107
|
-
</script:
|
|
107
|
+
</script:odac>
|
|
108
108
|
|
|
109
109
|
<div class="cart-summary">
|
|
110
110
|
<h3>Order Summary</h3>
|
|
111
|
-
<p>Items (<
|
|
112
|
-
<p>Tax (18%): $<
|
|
111
|
+
<p>Items (<odac var="totalItems" />): $<odac var="subtotal.toFixed(2)" /></p>
|
|
112
|
+
<p>Tax (18%): $<odac var="tax.toFixed(2)" /></p>
|
|
113
113
|
<p>Shipping:
|
|
114
|
-
<
|
|
114
|
+
<odac:if condition="shipping === 0">
|
|
115
115
|
<span class="free">FREE</span>
|
|
116
|
-
<
|
|
117
|
-
$<
|
|
118
|
-
</
|
|
116
|
+
<odac:else>
|
|
117
|
+
$<odac var="shipping.toFixed(2)" />
|
|
118
|
+
</odac:if>
|
|
119
119
|
</p>
|
|
120
120
|
<hr>
|
|
121
|
-
<p class="total">Total: $<
|
|
121
|
+
<p class="total">Total: $<odac var="total.toFixed(2)" /></p>
|
|
122
122
|
</div>
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
#### Filtering and Sorting
|
|
126
126
|
|
|
127
127
|
```html
|
|
128
|
-
<script:
|
|
128
|
+
<script:odac>
|
|
129
129
|
// Filter active products
|
|
130
130
|
const activeProducts = products.filter(p => p.isActive && p.stock > 0);
|
|
131
131
|
|
|
@@ -139,30 +139,30 @@ Full access to the Candy object and all its methods:
|
|
|
139
139
|
const avgPrice = activeProducts.reduce((sum, p) => sum + p.price, 0) / activeProducts.length;
|
|
140
140
|
const maxPrice = Math.max(...activeProducts.map(p => p.price));
|
|
141
141
|
const minPrice = Math.min(...activeProducts.map(p => p.price));
|
|
142
|
-
</script:
|
|
142
|
+
</script:odac>
|
|
143
143
|
|
|
144
144
|
<div class="products-section">
|
|
145
145
|
<h2>Featured Products</h2>
|
|
146
|
-
<p>Showing <
|
|
147
|
-
<p>Price range: $<
|
|
146
|
+
<p>Showing <odac var="featured.length" /> of <odac var="activeProducts.length" /> products</p>
|
|
147
|
+
<p>Price range: $<odac var="minPrice" /> - $<odac var="maxPrice" /></p>
|
|
148
148
|
|
|
149
|
-
<
|
|
149
|
+
<odac:for in="featured" value="product">
|
|
150
150
|
<div class="product">
|
|
151
|
-
<h3><
|
|
152
|
-
<p>$<
|
|
151
|
+
<h3><odac var="product.name" /></h3>
|
|
152
|
+
<p>$<odac var="product.price" /></p>
|
|
153
153
|
|
|
154
|
-
<
|
|
154
|
+
<odac:if condition="product.price < avgPrice">
|
|
155
155
|
<span class="badge">Great Deal!</span>
|
|
156
|
-
</
|
|
156
|
+
</odac:if>
|
|
157
157
|
</div>
|
|
158
|
-
</
|
|
158
|
+
</odac:for>
|
|
159
159
|
</div>
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
#### Date and Time Formatting
|
|
163
163
|
|
|
164
164
|
```html
|
|
165
|
-
<script:
|
|
165
|
+
<script:odac>
|
|
166
166
|
const now = new Date();
|
|
167
167
|
const postDate = new Date(post.createdAt);
|
|
168
168
|
|
|
@@ -188,12 +188,12 @@ Full access to the Candy object and all its methods:
|
|
|
188
188
|
month: 'long',
|
|
189
189
|
day: 'numeric'
|
|
190
190
|
});
|
|
191
|
-
</script:
|
|
191
|
+
</script:odac>
|
|
192
192
|
|
|
193
193
|
<div class="post">
|
|
194
|
-
<h2><
|
|
194
|
+
<h2><odac var="post.title" /></h2>
|
|
195
195
|
<p class="meta">
|
|
196
|
-
Posted <
|
|
196
|
+
Posted <odac var="timeAgo" /> (<odac var="formattedDate" />)
|
|
197
197
|
</p>
|
|
198
198
|
</div>
|
|
199
199
|
```
|
|
@@ -201,7 +201,7 @@ Full access to the Candy object and all its methods:
|
|
|
201
201
|
#### Grouping Data
|
|
202
202
|
|
|
203
203
|
```html
|
|
204
|
-
<script:
|
|
204
|
+
<script:odac>
|
|
205
205
|
// Group products by category
|
|
206
206
|
const grouped = {};
|
|
207
207
|
for (let product of products) {
|
|
@@ -213,31 +213,31 @@ Full access to the Candy object and all its methods:
|
|
|
213
213
|
|
|
214
214
|
// Sort categories
|
|
215
215
|
const categories = Object.keys(grouped).sort();
|
|
216
|
-
</script:
|
|
216
|
+
</script:odac>
|
|
217
217
|
|
|
218
218
|
<div class="products-by-category">
|
|
219
|
-
<
|
|
219
|
+
<odac:for in="categories" value="category">
|
|
220
220
|
<div class="category-section">
|
|
221
|
-
<h2><
|
|
222
|
-
<p><
|
|
221
|
+
<h2><odac var="category" /></h2>
|
|
222
|
+
<p><odac var="grouped[category].length" /> products</p>
|
|
223
223
|
|
|
224
|
-
<
|
|
224
|
+
<odac:for in="grouped[category]" value="product">
|
|
225
225
|
<div class="product">
|
|
226
|
-
<h3><
|
|
227
|
-
<p>$<
|
|
226
|
+
<h3><odac var="product.name" /></h3>
|
|
227
|
+
<p>$<odac var="product.price" /></p>
|
|
228
228
|
</div>
|
|
229
|
-
</
|
|
229
|
+
</odac:for>
|
|
230
230
|
</div>
|
|
231
|
-
</
|
|
231
|
+
</odac:for>
|
|
232
232
|
</div>
|
|
233
233
|
```
|
|
234
234
|
|
|
235
235
|
#### Pagination Logic
|
|
236
236
|
|
|
237
237
|
```html
|
|
238
|
-
<script:
|
|
238
|
+
<script:odac>
|
|
239
239
|
const itemsPerPage = 10;
|
|
240
|
-
const currentPage = parseInt(
|
|
240
|
+
const currentPage = parseInt(Odac.Request.get('page')) || 1;
|
|
241
241
|
const totalItems = products.length;
|
|
242
242
|
const totalPages = Math.ceil(totalItems / itemsPerPage);
|
|
243
243
|
|
|
@@ -247,34 +247,34 @@ Full access to the Candy object and all its methods:
|
|
|
247
247
|
|
|
248
248
|
const hasPrevious = currentPage > 1;
|
|
249
249
|
const hasNext = currentPage < totalPages;
|
|
250
|
-
</script:
|
|
250
|
+
</script:odac>
|
|
251
251
|
|
|
252
252
|
<div class="products">
|
|
253
|
-
<
|
|
253
|
+
<odac:for in="currentItems" value="product">
|
|
254
254
|
<div class="product">
|
|
255
|
-
<h3><
|
|
255
|
+
<h3><odac var="product.name" /></h3>
|
|
256
256
|
</div>
|
|
257
|
-
</
|
|
257
|
+
</odac:for>
|
|
258
258
|
</div>
|
|
259
259
|
|
|
260
260
|
<div class="pagination">
|
|
261
|
-
<
|
|
262
|
-
<a href="?page=<
|
|
263
|
-
</
|
|
261
|
+
<odac:if condition="hasPrevious">
|
|
262
|
+
<a href="?page=<odac var="currentPage - 1" />">Previous</a>
|
|
263
|
+
</odac:if>
|
|
264
264
|
|
|
265
|
-
<span>Page <
|
|
265
|
+
<span>Page <odac var="currentPage" /> of <odac var="totalPages" /></span>
|
|
266
266
|
|
|
267
|
-
<
|
|
268
|
-
<a href="?page=<
|
|
269
|
-
</
|
|
267
|
+
<odac:if condition="hasNext">
|
|
268
|
+
<a href="?page=<odac var="currentPage + 1" />">Next</a>
|
|
269
|
+
</odac:if>
|
|
270
270
|
</div>
|
|
271
271
|
```
|
|
272
272
|
|
|
273
273
|
#### Complex Conditional Logic
|
|
274
274
|
|
|
275
275
|
```html
|
|
276
|
-
<script:
|
|
277
|
-
const user =
|
|
276
|
+
<script:odac>
|
|
277
|
+
const user = Odac.Auth.check() ? Odac.Auth.user() : null;
|
|
278
278
|
|
|
279
279
|
const canEdit = user && (
|
|
280
280
|
user.role === 'admin' ||
|
|
@@ -286,59 +286,59 @@ Full access to the Candy object and all its methods:
|
|
|
286
286
|
const canComment = user && !user.isBanned && post.commentsEnabled;
|
|
287
287
|
|
|
288
288
|
const showActions = canEdit || canDelete || canComment;
|
|
289
|
-
</script:
|
|
289
|
+
</script:odac>
|
|
290
290
|
|
|
291
291
|
<div class="post">
|
|
292
|
-
<h2><
|
|
293
|
-
<p><
|
|
292
|
+
<h2><odac var="post.title" /></h2>
|
|
293
|
+
<p><odac var="post.content" /></p>
|
|
294
294
|
|
|
295
|
-
<
|
|
295
|
+
<odac:if condition="showActions">
|
|
296
296
|
<div class="actions">
|
|
297
|
-
<
|
|
298
|
-
<a href="/posts/<
|
|
299
|
-
</
|
|
297
|
+
<odac:if condition="canEdit">
|
|
298
|
+
<a href="/posts/<odac var="post.id" />/edit">Edit</a>
|
|
299
|
+
</odac:if>
|
|
300
300
|
|
|
301
|
-
<
|
|
302
|
-
<a href="/posts/<
|
|
303
|
-
</
|
|
301
|
+
<odac:if condition="canDelete">
|
|
302
|
+
<a href="/posts/<odac var="post.id" />/delete">Delete</a>
|
|
303
|
+
</odac:if>
|
|
304
304
|
|
|
305
|
-
<
|
|
305
|
+
<odac:if condition="canComment">
|
|
306
306
|
<a href="#comments">Add Comment</a>
|
|
307
|
-
</
|
|
307
|
+
</odac:if>
|
|
308
308
|
</div>
|
|
309
|
-
</
|
|
309
|
+
</odac:if>
|
|
310
310
|
</div>
|
|
311
311
|
```
|
|
312
312
|
|
|
313
313
|
### Multiple Script Blocks
|
|
314
314
|
|
|
315
|
-
You can use multiple `<script:
|
|
315
|
+
You can use multiple `<script:odac>` blocks in the same view:
|
|
316
316
|
|
|
317
317
|
```html
|
|
318
|
-
<script:
|
|
318
|
+
<script:odac>
|
|
319
319
|
let total = 0;
|
|
320
|
-
</script:
|
|
320
|
+
</script:odac>
|
|
321
321
|
|
|
322
|
-
<
|
|
323
|
-
<div><
|
|
322
|
+
<odac:for in="items" value="item">
|
|
323
|
+
<div><odac var="item.name" /></div>
|
|
324
324
|
|
|
325
|
-
<script:
|
|
325
|
+
<script:odac>
|
|
326
326
|
total += item.price;
|
|
327
|
-
</script:
|
|
328
|
-
</
|
|
327
|
+
</script:odac>
|
|
328
|
+
</odac:for>
|
|
329
329
|
|
|
330
|
-
<p>Total: $<
|
|
330
|
+
<p>Total: $<odac var="total" /></p>
|
|
331
331
|
```
|
|
332
332
|
|
|
333
333
|
### Comparison with Client-Side JavaScript
|
|
334
334
|
|
|
335
|
-
**Backend JavaScript (`<script:
|
|
335
|
+
**Backend JavaScript (`<script:odac>`):**
|
|
336
336
|
```html
|
|
337
|
-
<script:
|
|
337
|
+
<script:odac>
|
|
338
338
|
// Runs on SERVER during rendering
|
|
339
339
|
const total = products.reduce((sum, p) => sum + p.price, 0);
|
|
340
|
-
</script:
|
|
341
|
-
<p>Total: $<
|
|
340
|
+
</script:odac>
|
|
341
|
+
<p>Total: $<odac var="total" /></p>
|
|
342
342
|
```
|
|
343
343
|
|
|
344
344
|
**Client-Side JavaScript (`<script>`):**
|
|
@@ -369,19 +369,19 @@ You can use multiple `<script:candy>` blocks in the same view:
|
|
|
369
369
|
|
|
370
370
|
**Good:**
|
|
371
371
|
```html
|
|
372
|
-
<script:
|
|
372
|
+
<script:odac>
|
|
373
373
|
const discountedPrice = product.price * (1 - product.discount / 100);
|
|
374
374
|
const savings = product.price - discountedPrice;
|
|
375
|
-
</script:
|
|
375
|
+
</script:odac>
|
|
376
376
|
```
|
|
377
377
|
|
|
378
378
|
**Avoid:**
|
|
379
379
|
```html
|
|
380
|
-
<script:
|
|
380
|
+
<script:odac>
|
|
381
381
|
// Don't do this - should be in controller
|
|
382
|
-
const users = await
|
|
382
|
+
const users = await Odac.Mysql.query('SELECT * FROM users');
|
|
383
383
|
const apiData = await fetch('https://api.example.com/data');
|
|
384
|
-
</script:
|
|
384
|
+
</script:odac>
|
|
385
385
|
```
|
|
386
386
|
|
|
387
387
|
### Common Use Cases
|