odac 0.9.0 → 1.0.0
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 +62 -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 +101 -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
|
@@ -7,12 +7,12 @@ Loops allow you to repeat content for each item in an array or object. This is e
|
|
|
7
7
|
The most common way to iterate over arrays and objects:
|
|
8
8
|
|
|
9
9
|
```html
|
|
10
|
-
<
|
|
10
|
+
<odac:for in="users" key="index" value="user">
|
|
11
11
|
<div class="user-card">
|
|
12
|
-
<h3><
|
|
13
|
-
<p><
|
|
12
|
+
<h3><odac var="user.name" /></h3>
|
|
13
|
+
<p><odac var="user.email" /></p>
|
|
14
14
|
</div>
|
|
15
|
-
</
|
|
15
|
+
</odac:for>
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
**Parameters:**
|
|
@@ -24,7 +24,7 @@ The most common way to iterate over arrays and objects:
|
|
|
24
24
|
|
|
25
25
|
```javascript
|
|
26
26
|
// Controller
|
|
27
|
-
|
|
27
|
+
Odac.set('products', [
|
|
28
28
|
{ name: 'Laptop', price: 999 },
|
|
29
29
|
{ name: 'Mouse', price: 29 },
|
|
30
30
|
{ name: 'Keyboard', price: 79 }
|
|
@@ -34,13 +34,13 @@ Candy.set('products', [
|
|
|
34
34
|
```html
|
|
35
35
|
<!-- View -->
|
|
36
36
|
<div class="products">
|
|
37
|
-
<
|
|
37
|
+
<odac:for in="products" key="index" value="product">
|
|
38
38
|
<div class="product">
|
|
39
|
-
<span class="number"><
|
|
40
|
-
<h3><
|
|
41
|
-
<p>$<
|
|
39
|
+
<span class="number"><odac var="index + 1" />.</span>
|
|
40
|
+
<h3><odac var="product.name" /></h3>
|
|
41
|
+
<p>$<odac var="product.price" /></p>
|
|
42
42
|
</div>
|
|
43
|
-
</
|
|
43
|
+
</odac:for>
|
|
44
44
|
</div>
|
|
45
45
|
```
|
|
46
46
|
|
|
@@ -48,7 +48,7 @@ Candy.set('products', [
|
|
|
48
48
|
|
|
49
49
|
```javascript
|
|
50
50
|
// Controller
|
|
51
|
-
|
|
51
|
+
Odac.set('settings', {
|
|
52
52
|
theme: 'dark',
|
|
53
53
|
language: 'en',
|
|
54
54
|
notifications: true
|
|
@@ -58,12 +58,12 @@ Candy.set('settings', {
|
|
|
58
58
|
```html
|
|
59
59
|
<!-- View -->
|
|
60
60
|
<table>
|
|
61
|
-
<
|
|
61
|
+
<odac:for in="settings" key="settingKey" value="settingValue">
|
|
62
62
|
<tr>
|
|
63
|
-
<td><
|
|
64
|
-
<td><
|
|
63
|
+
<td><odac var="settingKey" /></td>
|
|
64
|
+
<td><odac var="settingValue" /></td>
|
|
65
65
|
</tr>
|
|
66
|
-
</
|
|
66
|
+
</odac:for>
|
|
67
67
|
</table>
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -72,14 +72,14 @@ Candy.set('settings', {
|
|
|
72
72
|
Use while loops for conditional iteration:
|
|
73
73
|
|
|
74
74
|
```html
|
|
75
|
-
<script:
|
|
75
|
+
<script:odac>
|
|
76
76
|
let counter = 0;
|
|
77
|
-
</script:
|
|
77
|
+
</script:odac>
|
|
78
78
|
|
|
79
|
-
<
|
|
80
|
-
<p>Item <
|
|
81
|
-
<script:
|
|
82
|
-
</
|
|
79
|
+
<odac:while condition="counter < 5">
|
|
80
|
+
<p>Item <odac var="counter + 1" /></p>
|
|
81
|
+
<script:odac>counter++;</script:odac>
|
|
82
|
+
</odac:while>
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
**Note:** Be careful with while loops to avoid infinite loops. The condition must eventually become false.
|
|
@@ -91,13 +91,13 @@ Use while loops for conditional iteration:
|
|
|
91
91
|
Exit the loop early:
|
|
92
92
|
|
|
93
93
|
```html
|
|
94
|
-
<
|
|
95
|
-
<
|
|
94
|
+
<odac:for in="products" value="product">
|
|
95
|
+
<odac:if condition="product.stock === 0">
|
|
96
96
|
<p class="notice">Some products are out of stock</p>
|
|
97
|
-
<
|
|
98
|
-
</
|
|
99
|
-
<div><
|
|
100
|
-
</
|
|
97
|
+
<odac:break />
|
|
98
|
+
</odac:if>
|
|
99
|
+
<div><odac var="product.name" /></div>
|
|
100
|
+
</odac:for>
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
#### Continue
|
|
@@ -105,16 +105,16 @@ Exit the loop early:
|
|
|
105
105
|
Skip to the next iteration:
|
|
106
106
|
|
|
107
107
|
```html
|
|
108
|
-
<
|
|
109
|
-
<
|
|
110
|
-
<
|
|
111
|
-
</
|
|
108
|
+
<odac:for in="users" value="user">
|
|
109
|
+
<odac:if condition="user.isBlocked">
|
|
110
|
+
<odac:continue />
|
|
111
|
+
</odac:if>
|
|
112
112
|
|
|
113
113
|
<div class="user">
|
|
114
|
-
<h3><
|
|
115
|
-
<p><
|
|
114
|
+
<h3><odac var="user.name" /></h3>
|
|
115
|
+
<p><odac var="user.email" /></p>
|
|
116
116
|
</div>
|
|
117
|
-
</
|
|
117
|
+
</odac:for>
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
### Practical Examples
|
|
@@ -125,18 +125,18 @@ Skip to the next iteration:
|
|
|
125
125
|
<div class="product-list">
|
|
126
126
|
<h2>Our Products</h2>
|
|
127
127
|
|
|
128
|
-
<
|
|
128
|
+
<odac:for in="products" key="i" value="product">
|
|
129
129
|
<div class="product-item">
|
|
130
|
-
<span class="number">#<
|
|
131
|
-
<img src="<
|
|
132
|
-
<h3><
|
|
133
|
-
<p class="price">$<
|
|
130
|
+
<span class="number">#<odac var="i + 1" /></span>
|
|
131
|
+
<img src="<odac var="product.image" />" alt="<odac var="product.name" />">
|
|
132
|
+
<h3><odac var="product.name" /></h3>
|
|
133
|
+
<p class="price">$<odac var="product.price" /></p>
|
|
134
134
|
|
|
135
|
-
<
|
|
136
|
-
<span class="discount">-<
|
|
137
|
-
</
|
|
135
|
+
<odac:if condition="product.discount">
|
|
136
|
+
<span class="discount">-<odac var="product.discount" />%</span>
|
|
137
|
+
</odac:if>
|
|
138
138
|
</div>
|
|
139
|
-
</
|
|
139
|
+
</odac:for>
|
|
140
140
|
</div>
|
|
141
141
|
```
|
|
142
142
|
|
|
@@ -154,21 +154,21 @@ Skip to the next iteration:
|
|
|
154
154
|
</tr>
|
|
155
155
|
</thead>
|
|
156
156
|
<tbody>
|
|
157
|
-
<
|
|
157
|
+
<odac:for in="users" key="index" value="user">
|
|
158
158
|
<tr>
|
|
159
|
-
<td><
|
|
160
|
-
<td><
|
|
161
|
-
<td><
|
|
162
|
-
<td><
|
|
159
|
+
<td><odac var="index + 1" /></td>
|
|
160
|
+
<td><odac var="user.name" /></td>
|
|
161
|
+
<td><odac var="user.email" /></td>
|
|
162
|
+
<td><odac var="user.role" /></td>
|
|
163
163
|
<td>
|
|
164
|
-
<
|
|
164
|
+
<odac:if condition="user.isActive">
|
|
165
165
|
<span class="badge success">Active</span>
|
|
166
|
-
<
|
|
166
|
+
<odac:else>
|
|
167
167
|
<span class="badge danger">Inactive</span>
|
|
168
|
-
</
|
|
168
|
+
</odac:if>
|
|
169
169
|
</td>
|
|
170
170
|
</tr>
|
|
171
|
-
</
|
|
171
|
+
</odac:for>
|
|
172
172
|
</tbody>
|
|
173
173
|
</table>
|
|
174
174
|
```
|
|
@@ -177,20 +177,20 @@ Skip to the next iteration:
|
|
|
177
177
|
|
|
178
178
|
```html
|
|
179
179
|
<div class="categories">
|
|
180
|
-
<
|
|
180
|
+
<odac:for in="categories" value="category">
|
|
181
181
|
<div class="category">
|
|
182
|
-
<h2><
|
|
182
|
+
<h2><odac var="category.name" /></h2>
|
|
183
183
|
|
|
184
184
|
<div class="products">
|
|
185
|
-
<
|
|
185
|
+
<odac:for in="category.products" value="product">
|
|
186
186
|
<div class="product">
|
|
187
|
-
<h3><
|
|
188
|
-
<p>$<
|
|
187
|
+
<h3><odac var="product.name" /></h3>
|
|
188
|
+
<p>$<odac var="product.price" /></p>
|
|
189
189
|
</div>
|
|
190
|
-
</
|
|
190
|
+
</odac:for>
|
|
191
191
|
</div>
|
|
192
192
|
</div>
|
|
193
|
-
</
|
|
193
|
+
</odac:for>
|
|
194
194
|
</div>
|
|
195
195
|
```
|
|
196
196
|
|
|
@@ -198,18 +198,18 @@ Skip to the next iteration:
|
|
|
198
198
|
|
|
199
199
|
```html
|
|
200
200
|
<div class="grid">
|
|
201
|
-
<
|
|
201
|
+
<odac:for in="items" key="i" value="item">
|
|
202
202
|
<div class="grid-item">
|
|
203
|
-
<img src="<
|
|
204
|
-
<h3><
|
|
205
|
-
<p><
|
|
203
|
+
<img src="<odac var="item.image" />" alt="<odac var="item.title" />">
|
|
204
|
+
<h3><odac var="item.title" /></h3>
|
|
205
|
+
<p><odac var="item.description" /></p>
|
|
206
206
|
|
|
207
207
|
<!-- Add row break every 3 items -->
|
|
208
|
-
<
|
|
208
|
+
<odac:if condition="(i + 1) % 3 === 0">
|
|
209
209
|
<div class="row-break"></div>
|
|
210
|
-
</
|
|
210
|
+
</odac:if>
|
|
211
211
|
</div>
|
|
212
|
-
</
|
|
212
|
+
</odac:for>
|
|
213
213
|
</div>
|
|
214
214
|
```
|
|
215
215
|
|
|
@@ -219,22 +219,22 @@ Skip to the next iteration:
|
|
|
219
219
|
<div class="active-users">
|
|
220
220
|
<h2>Active Users</h2>
|
|
221
221
|
|
|
222
|
-
<
|
|
222
|
+
<odac:for in="users" value="user">
|
|
223
223
|
<!-- Skip inactive users -->
|
|
224
|
-
<
|
|
225
|
-
<
|
|
226
|
-
</
|
|
224
|
+
<odac:if condition="!user.isActive">
|
|
225
|
+
<odac:continue />
|
|
226
|
+
</odac:if>
|
|
227
227
|
|
|
228
228
|
<!-- Skip blocked users -->
|
|
229
|
-
<
|
|
230
|
-
<
|
|
231
|
-
</
|
|
229
|
+
<odac:if condition="user.isBlocked">
|
|
230
|
+
<odac:continue />
|
|
231
|
+
</odac:if>
|
|
232
232
|
|
|
233
233
|
<div class="user-card">
|
|
234
|
-
<h3><
|
|
235
|
-
<p><
|
|
234
|
+
<h3><odac var="user.name" /></h3>
|
|
235
|
+
<p><odac var="user.email" /></p>
|
|
236
236
|
</div>
|
|
237
|
-
</
|
|
237
|
+
</odac:for>
|
|
238
238
|
</div>
|
|
239
239
|
```
|
|
240
240
|
|
|
@@ -244,21 +244,21 @@ Skip to the next iteration:
|
|
|
244
244
|
<div class="products-section">
|
|
245
245
|
<h2>Products</h2>
|
|
246
246
|
|
|
247
|
-
<
|
|
247
|
+
<odac:if condition="products && products.length > 0">
|
|
248
248
|
<div class="products-grid">
|
|
249
|
-
<
|
|
249
|
+
<odac:for in="products" value="product">
|
|
250
250
|
<div class="product-card">
|
|
251
|
-
<h3><
|
|
252
|
-
<p>$<
|
|
251
|
+
<h3><odac var="product.name" /></h3>
|
|
252
|
+
<p>$<odac var="product.price" /></p>
|
|
253
253
|
</div>
|
|
254
|
-
</
|
|
254
|
+
</odac:for>
|
|
255
255
|
</div>
|
|
256
|
-
<
|
|
256
|
+
<odac:else>
|
|
257
257
|
<div class="empty-state">
|
|
258
258
|
<p>No products found.</p>
|
|
259
259
|
<a href="/products/add">Add your first product</a>
|
|
260
260
|
</div>
|
|
261
|
-
</
|
|
261
|
+
</odac:if>
|
|
262
262
|
</div>
|
|
263
263
|
```
|
|
264
264
|
|
|
@@ -266,12 +266,12 @@ Skip to the next iteration:
|
|
|
266
266
|
|
|
267
267
|
```html
|
|
268
268
|
<table>
|
|
269
|
-
<
|
|
270
|
-
<tr class="<
|
|
271
|
-
<td><
|
|
272
|
-
<td><
|
|
269
|
+
<odac:for in="items" key="i" value="item">
|
|
270
|
+
<tr class="<odac:if condition="i % 2 === 0">even<odac:else>odd</odac:if>">
|
|
271
|
+
<td><odac var="item.name" /></td>
|
|
272
|
+
<td><odac var="item.value" /></td>
|
|
273
273
|
</tr>
|
|
274
|
-
</
|
|
274
|
+
</odac:for>
|
|
275
275
|
</table>
|
|
276
276
|
```
|
|
277
277
|
|
|
@@ -281,43 +281,43 @@ Skip to the next iteration:
|
|
|
281
281
|
<div class="top-products">
|
|
282
282
|
<h2>Top 5 Products</h2>
|
|
283
283
|
|
|
284
|
-
<script:
|
|
284
|
+
<script:odac>
|
|
285
285
|
let count = 0;
|
|
286
|
-
</script:
|
|
286
|
+
</script:odac>
|
|
287
287
|
|
|
288
|
-
<
|
|
289
|
-
<
|
|
290
|
-
<
|
|
291
|
-
</
|
|
288
|
+
<odac:for in="products" value="product">
|
|
289
|
+
<odac:if condition="count >= 5">
|
|
290
|
+
<odac:break />
|
|
291
|
+
</odac:if>
|
|
292
292
|
|
|
293
293
|
<div class="product">
|
|
294
|
-
<h3><
|
|
295
|
-
<p>$<
|
|
294
|
+
<h3><odac var="product.name" /></h3>
|
|
295
|
+
<p>$<odac var="product.price" /></p>
|
|
296
296
|
</div>
|
|
297
297
|
|
|
298
|
-
<script:
|
|
299
|
-
</
|
|
298
|
+
<script:odac>count++;</script:odac>
|
|
299
|
+
</odac:for>
|
|
300
300
|
</div>
|
|
301
301
|
```
|
|
302
302
|
|
|
303
303
|
#### Pagination with While
|
|
304
304
|
|
|
305
305
|
```html
|
|
306
|
-
<script:
|
|
306
|
+
<script:odac>
|
|
307
307
|
const itemsPerPage = 10;
|
|
308
|
-
const currentPage = parseInt(
|
|
308
|
+
const currentPage = parseInt(Odac.Request.get('page')) || 1;
|
|
309
309
|
const startIndex = (currentPage - 1) * itemsPerPage;
|
|
310
310
|
const endIndex = startIndex + itemsPerPage;
|
|
311
311
|
let index = startIndex;
|
|
312
|
-
</script:
|
|
312
|
+
</script:odac>
|
|
313
313
|
|
|
314
314
|
<div class="items">
|
|
315
|
-
<
|
|
315
|
+
<odac:while condition="index < endIndex && index < items.length">
|
|
316
316
|
<div class="item">
|
|
317
|
-
<
|
|
317
|
+
<odac var="items[index].name" />
|
|
318
318
|
</div>
|
|
319
|
-
<script:
|
|
320
|
-
</
|
|
319
|
+
<script:odac>index++;</script:odac>
|
|
320
|
+
</odac:while>
|
|
321
321
|
</div>
|
|
322
322
|
```
|
|
323
323
|
|
|
@@ -332,22 +332,22 @@ Skip to the next iteration:
|
|
|
332
332
|
**Good:**
|
|
333
333
|
```javascript
|
|
334
334
|
// Controller - prepare data
|
|
335
|
-
|
|
335
|
+
Odac.set('activeUsers', users.filter(u => u.isActive))
|
|
336
336
|
```
|
|
337
337
|
|
|
338
338
|
```html
|
|
339
339
|
<!-- View - simple loop -->
|
|
340
|
-
<
|
|
341
|
-
<div><
|
|
342
|
-
</
|
|
340
|
+
<odac:for in="activeUsers" value="user">
|
|
341
|
+
<div><odac var="user.name" /></div>
|
|
342
|
+
</odac:for>
|
|
343
343
|
```
|
|
344
344
|
|
|
345
345
|
**Avoid:**
|
|
346
346
|
```html
|
|
347
347
|
<!-- Too complex for a view -->
|
|
348
|
-
<
|
|
349
|
-
<
|
|
350
|
-
<div><
|
|
351
|
-
</
|
|
352
|
-
</
|
|
348
|
+
<odac:for in="users" value="user">
|
|
349
|
+
<odac:if condition="user.isActive && !user.isBlocked && user.role !== 'guest'">
|
|
350
|
+
<div><odac var="user.name" /></div>
|
|
351
|
+
</odac:if>
|
|
352
|
+
</odac:for>
|
|
353
353
|
```
|