odac 0.9.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/.editorconfig +21 -0
- package/.github/workflows/auto-pr-description.yml +49 -0
- package/.github/workflows/release.yml +32 -0
- package/.github/workflows/test-coverage.yml +58 -0
- package/.husky/pre-commit +2 -0
- package/.kiro/steering/code-style.md +56 -0
- package/.kiro/steering/product.md +20 -0
- package/.kiro/steering/structure.md +77 -0
- package/.kiro/steering/tech.md +87 -0
- package/.prettierrc +10 -0
- package/.releaserc.js +134 -0
- package/AGENTS.md +84 -0
- package/CHANGELOG.md +181 -0
- package/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +63 -0
- package/LICENSE +661 -0
- package/README.md +57 -0
- package/SECURITY.md +26 -0
- package/bin/candy +10 -0
- package/bin/candypack +10 -0
- package/cli/index.js +3 -0
- package/cli/src/Cli.js +348 -0
- package/cli/src/Connector.js +93 -0
- package/cli/src/Monitor.js +416 -0
- package/core/Candy.js +87 -0
- package/core/Commands.js +239 -0
- package/core/Config.js +1094 -0
- package/core/Lang.js +52 -0
- package/core/Log.js +43 -0
- package/core/Process.js +26 -0
- package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
- package/docs/backend/01-overview/03-development-server.md +79 -0
- package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
- package/docs/backend/03-config/00-configuration-overview.md +214 -0
- package/docs/backend/03-config/01-database-connection.md +60 -0
- package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
- package/docs/backend/03-config/03-request-timeout.md +11 -0
- package/docs/backend/03-config/04-environment-variables.md +227 -0
- package/docs/backend/03-config/05-early-hints.md +352 -0
- package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
- package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
- package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
- package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
- package/docs/backend/04-routing/05-advanced-routing.md +14 -0
- package/docs/backend/04-routing/06-error-pages.md +101 -0
- package/docs/backend/04-routing/07-cron-jobs.md +149 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
- package/docs/backend/05-controllers/03-controller-classes.md +93 -0
- package/docs/backend/05-forms/01-custom-forms.md +395 -0
- package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
- package/docs/backend/07-views/01-the-view-directory.md +73 -0
- package/docs/backend/07-views/02-rendering-a-view.md +179 -0
- package/docs/backend/07-views/03-template-syntax.md +181 -0
- package/docs/backend/07-views/03-variables.md +328 -0
- package/docs/backend/07-views/04-request-data.md +231 -0
- package/docs/backend/07-views/05-conditionals.md +290 -0
- package/docs/backend/07-views/06-loops.md +353 -0
- package/docs/backend/07-views/07-translations.md +358 -0
- package/docs/backend/07-views/08-backend-javascript.md +398 -0
- package/docs/backend/07-views/09-comments.md +297 -0
- package/docs/backend/08-database/01-database-connection.md +99 -0
- package/docs/backend/08-database/02-using-mysql.md +322 -0
- package/docs/backend/09-validation/01-the-validator-service.md +424 -0
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
- package/docs/backend/10-authentication/03-register.md +134 -0
- package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
- package/docs/backend/10-authentication/05-session-management.md +159 -0
- package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
- package/docs/backend/11-mail/01-the-mail-service.md +42 -0
- package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
- package/docs/backend/13-utilities/01-candy-var.md +504 -0
- package/docs/frontend/01-overview/01-introduction.md +146 -0
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
- package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
- package/docs/frontend/03-forms/01-form-handling.md +420 -0
- package/docs/frontend/04-api-requests/01-get-post.md +443 -0
- package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
- package/docs/index.json +452 -0
- package/docs/server/01-installation/01-quick-install.md +19 -0
- package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
- package/docs/server/02-get-started/01-core-concepts.md +7 -0
- package/docs/server/02-get-started/02-basic-commands.md +57 -0
- package/docs/server/02-get-started/03-cli-reference.md +276 -0
- package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
- package/docs/server/03-service/01-start-a-new-service.md +57 -0
- package/docs/server/03-service/02-delete-a-service.md +48 -0
- package/docs/server/04-web/01-create-a-website.md +36 -0
- package/docs/server/04-web/02-list-websites.md +9 -0
- package/docs/server/04-web/03-delete-a-website.md +29 -0
- package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
- package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
- package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
- package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
- package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
- package/docs/server/07-mail/04-change-account-password.md +23 -0
- package/eslint.config.mjs +120 -0
- package/framework/index.js +4 -0
- package/framework/src/Auth.js +309 -0
- package/framework/src/Candy.js +81 -0
- package/framework/src/Config.js +79 -0
- package/framework/src/Env.js +60 -0
- package/framework/src/Lang.js +57 -0
- package/framework/src/Mail.js +83 -0
- package/framework/src/Mysql.js +575 -0
- package/framework/src/Request.js +301 -0
- package/framework/src/Route/Cron.js +128 -0
- package/framework/src/Route/Internal.js +439 -0
- package/framework/src/Route.js +455 -0
- package/framework/src/Server.js +15 -0
- package/framework/src/Stream.js +163 -0
- package/framework/src/Token.js +37 -0
- package/framework/src/Validator.js +271 -0
- package/framework/src/Var.js +211 -0
- package/framework/src/View/EarlyHints.js +190 -0
- package/framework/src/View/Form.js +600 -0
- package/framework/src/View.js +513 -0
- package/framework/web/candy.js +838 -0
- package/jest.config.js +22 -0
- package/locale/de-DE.json +80 -0
- package/locale/en-US.json +79 -0
- package/locale/es-ES.json +80 -0
- package/locale/fr-FR.json +80 -0
- package/locale/pt-BR.json +80 -0
- package/locale/ru-RU.json +80 -0
- package/locale/tr-TR.json +85 -0
- package/locale/zh-CN.json +80 -0
- package/package.json +86 -0
- package/server/index.js +5 -0
- package/server/src/Api.js +88 -0
- package/server/src/DNS.js +940 -0
- package/server/src/Hub.js +535 -0
- package/server/src/Mail.js +571 -0
- package/server/src/SSL.js +180 -0
- package/server/src/Server.js +27 -0
- package/server/src/Service.js +248 -0
- package/server/src/Subdomain.js +64 -0
- package/server/src/Web/Firewall.js +170 -0
- package/server/src/Web/Proxy.js +134 -0
- package/server/src/Web.js +451 -0
- package/server/src/mail/imap.js +1091 -0
- package/server/src/mail/server.js +32 -0
- package/server/src/mail/smtp.js +786 -0
- package/test/cli/Cli.test.js +36 -0
- package/test/core/Candy.test.js +234 -0
- package/test/core/Commands.test.js +538 -0
- package/test/core/Config.test.js +1435 -0
- package/test/core/Lang.test.js +250 -0
- package/test/core/Process.test.js +156 -0
- package/test/framework/Route.test.js +239 -0
- package/test/framework/View/EarlyHints.test.js +282 -0
- package/test/scripts/check-coverage.js +132 -0
- package/test/server/Api.test.js +647 -0
- package/test/server/Client.test.js +338 -0
- package/test/server/DNS.test.js +2050 -0
- package/test/server/DNS.test.js.bak +2084 -0
- package/test/server/Log.test.js +73 -0
- package/test/server/Mail.account.test_.js +460 -0
- package/test/server/Mail.init.test_.js +411 -0
- package/test/server/Mail.test_.js +1340 -0
- package/test/server/SSL.test_.js +1491 -0
- package/test/server/Server.test.js +765 -0
- package/test/server/Service.test_.js +1127 -0
- package/test/server/Subdomain.test.js +440 -0
- package/test/server/Web/Firewall.test.js +175 -0
- package/test/server/Web.test_.js +1562 -0
- package/test/server/__mocks__/acme-client.js +17 -0
- package/test/server/__mocks__/bcrypt.js +50 -0
- package/test/server/__mocks__/child_process.js +389 -0
- package/test/server/__mocks__/crypto.js +432 -0
- package/test/server/__mocks__/fs.js +450 -0
- package/test/server/__mocks__/globalCandy.js +227 -0
- package/test/server/__mocks__/http-proxy.js +105 -0
- package/test/server/__mocks__/http.js +575 -0
- package/test/server/__mocks__/https.js +272 -0
- package/test/server/__mocks__/index.js +249 -0
- package/test/server/__mocks__/mail/server.js +100 -0
- package/test/server/__mocks__/mail/smtp.js +31 -0
- package/test/server/__mocks__/mailparser.js +81 -0
- package/test/server/__mocks__/net.js +369 -0
- package/test/server/__mocks__/node-forge.js +328 -0
- package/test/server/__mocks__/os.js +320 -0
- package/test/server/__mocks__/path.js +291 -0
- package/test/server/__mocks__/selfsigned.js +8 -0
- package/test/server/__mocks__/server/src/mail/server.js +100 -0
- package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
- package/test/server/__mocks__/smtp-server.js +106 -0
- package/test/server/__mocks__/sqlite3.js +394 -0
- package/test/server/__mocks__/testFactories.js +299 -0
- package/test/server/__mocks__/testHelpers.js +363 -0
- package/test/server/__mocks__/tls.js +229 -0
- package/watchdog/index.js +3 -0
- package/watchdog/src/Watchdog.js +156 -0
- package/web/config.json +5 -0
- package/web/controller/page/about.js +27 -0
- package/web/controller/page/index.js +34 -0
- package/web/package.json +18 -0
- package/web/public/assets/css/style.css +1835 -0
- package/web/public/assets/js/app.js +96 -0
- package/web/route/www.js +19 -0
- package/web/skeleton/main.html +22 -0
- package/web/view/content/about.html +65 -0
- package/web/view/content/home.html +205 -0
- package/web/view/footer/main.html +11 -0
- package/web/view/head/main.html +5 -0
- package/web/view/header/main.html +14 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
## 🔀 Conditional Rendering
|
|
2
|
+
|
|
3
|
+
Conditionals allow you to show or hide content based on conditions. This is essential for dynamic user interfaces.
|
|
4
|
+
|
|
5
|
+
### Basic If Statement
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<candy:if condition="user.isAdmin">
|
|
9
|
+
<p>Welcome to the admin panel!</p>
|
|
10
|
+
</candy:if>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The content inside the tag is only rendered if the condition is true.
|
|
14
|
+
|
|
15
|
+
### If-Else Structure
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<candy:if condition="user.isLoggedIn">
|
|
19
|
+
<p>Welcome back, <candy var="user.name" />!</p>
|
|
20
|
+
<candy:else>
|
|
21
|
+
<p>Please log in to continue.</p>
|
|
22
|
+
</candy:if>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### If-ElseIf-Else Structure
|
|
26
|
+
|
|
27
|
+
```html
|
|
28
|
+
<candy:if condition="user.role === 'admin'">
|
|
29
|
+
<div class="admin-panel">
|
|
30
|
+
<p>You have full admin privileges</p>
|
|
31
|
+
</div>
|
|
32
|
+
<candy:elseif condition="user.role === 'moderator'">
|
|
33
|
+
<div class="moderator-panel">
|
|
34
|
+
<p>You have moderator privileges</p>
|
|
35
|
+
</div>
|
|
36
|
+
<candy:elseif condition="user.role === 'editor'">
|
|
37
|
+
<div class="editor-panel">
|
|
38
|
+
<p>You have editor privileges</p>
|
|
39
|
+
</div>
|
|
40
|
+
<candy:else>
|
|
41
|
+
<div class="user-panel">
|
|
42
|
+
<p>You have regular user privileges</p>
|
|
43
|
+
</div>
|
|
44
|
+
</candy:if>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Condition Syntax
|
|
48
|
+
|
|
49
|
+
Conditions use standard JavaScript expressions:
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<!-- Equality -->
|
|
53
|
+
<candy:if condition="status === 'active'">Active</candy:if>
|
|
54
|
+
|
|
55
|
+
<!-- Comparison -->
|
|
56
|
+
<candy:if condition="age >= 18">Adult</candy:if>
|
|
57
|
+
<candy:if condition="price < 100">Affordable</candy:if>
|
|
58
|
+
|
|
59
|
+
<!-- Logical operators -->
|
|
60
|
+
<candy:if condition="user.isVerified && user.isPremium">
|
|
61
|
+
Premium Verified User
|
|
62
|
+
</candy:if>
|
|
63
|
+
|
|
64
|
+
<candy:if condition="role === 'admin' || role === 'moderator'">
|
|
65
|
+
Staff Member
|
|
66
|
+
</candy:if>
|
|
67
|
+
|
|
68
|
+
<!-- Negation -->
|
|
69
|
+
<candy:if condition="!user.isBanned">
|
|
70
|
+
Welcome!
|
|
71
|
+
</candy:if>
|
|
72
|
+
|
|
73
|
+
<!-- Existence check -->
|
|
74
|
+
<candy:if condition="user">
|
|
75
|
+
User exists
|
|
76
|
+
</candy:if>
|
|
77
|
+
|
|
78
|
+
<!-- Array/String length -->
|
|
79
|
+
<candy:if condition="items.length > 0">
|
|
80
|
+
You have items
|
|
81
|
+
</candy:if>
|
|
82
|
+
|
|
83
|
+
<!-- Method calls -->
|
|
84
|
+
<candy:if condition="Candy.Auth.check()">
|
|
85
|
+
Logged in
|
|
86
|
+
</candy:if>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Practical Examples
|
|
90
|
+
|
|
91
|
+
#### User Authentication Status
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<nav>
|
|
95
|
+
<candy:if condition="Candy.Auth.check()">
|
|
96
|
+
<a href="/profile">Profile</a>
|
|
97
|
+
<a href="/settings">Settings</a>
|
|
98
|
+
<a href="/logout">Logout</a>
|
|
99
|
+
<candy:else>
|
|
100
|
+
<a href="/login">Login</a>
|
|
101
|
+
<a href="/register">Register</a>
|
|
102
|
+
</candy:if>
|
|
103
|
+
</nav>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Product Stock Status
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<div class="product">
|
|
110
|
+
<h3><candy var="product.name" /></h3>
|
|
111
|
+
<p class="price">$<candy var="product.price" /></p>
|
|
112
|
+
|
|
113
|
+
<candy:if condition="product.stock > 10">
|
|
114
|
+
<span class="badge in-stock">In Stock</span>
|
|
115
|
+
<button>Add to Cart</button>
|
|
116
|
+
<candy:elseif condition="product.stock > 0">
|
|
117
|
+
<span class="badge low-stock">Only <candy var="product.stock" /> left!</span>
|
|
118
|
+
<button>Add to Cart</button>
|
|
119
|
+
<candy:else>
|
|
120
|
+
<span class="badge out-of-stock">Out of Stock</span>
|
|
121
|
+
<button disabled>Notify Me</button>
|
|
122
|
+
</candy:if>
|
|
123
|
+
</div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### User Role-Based Content
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<div class="dashboard">
|
|
130
|
+
<h1>Dashboard</h1>
|
|
131
|
+
|
|
132
|
+
<candy:if condition="user.role === 'admin'">
|
|
133
|
+
<div class="admin-section">
|
|
134
|
+
<h2>Admin Tools</h2>
|
|
135
|
+
<a href="/admin/users">Manage Users</a>
|
|
136
|
+
<a href="/admin/settings">System Settings</a>
|
|
137
|
+
<a href="/admin/logs">View Logs</a>
|
|
138
|
+
</div>
|
|
139
|
+
</candy:if>
|
|
140
|
+
|
|
141
|
+
<candy:if condition="user.role === 'admin' || user.role === 'moderator'">
|
|
142
|
+
<div class="moderation-section">
|
|
143
|
+
<h2>Moderation</h2>
|
|
144
|
+
<a href="/moderate/posts">Review Posts</a>
|
|
145
|
+
<a href="/moderate/reports">Handle Reports</a>
|
|
146
|
+
</div>
|
|
147
|
+
</candy:if>
|
|
148
|
+
|
|
149
|
+
<div class="user-section">
|
|
150
|
+
<h2>Your Content</h2>
|
|
151
|
+
<a href="/my-posts">My Posts</a>
|
|
152
|
+
<a href="/my-profile">My Profile</a>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Form Validation Messages
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<form>
|
|
161
|
+
<div class="form-group">
|
|
162
|
+
<label>Email</label>
|
|
163
|
+
<input type="email" name="email" value="<candy var="email" />">
|
|
164
|
+
|
|
165
|
+
<candy:if condition="errors && errors.email">
|
|
166
|
+
<span class="error"><candy var="errors.email" /></span>
|
|
167
|
+
</candy:if>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div class="form-group">
|
|
171
|
+
<label>Password</label>
|
|
172
|
+
<input type="password" name="password">
|
|
173
|
+
|
|
174
|
+
<candy:if condition="errors && errors.password">
|
|
175
|
+
<span class="error"><candy var="errors.password" /></span>
|
|
176
|
+
</candy:if>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<candy:if condition="success">
|
|
180
|
+
<div class="success-message">
|
|
181
|
+
<candy var="success" />
|
|
182
|
+
</div>
|
|
183
|
+
</candy:if>
|
|
184
|
+
|
|
185
|
+
<button type="submit">Submit</button>
|
|
186
|
+
</form>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### Conditional CSS Classes
|
|
190
|
+
|
|
191
|
+
```html
|
|
192
|
+
<div class="user-card <candy:if condition="user.isPremium">premium</candy:if> <candy:if condition="user.isVerified">verified</candy:if>">
|
|
193
|
+
<h3><candy var="user.name" /></h3>
|
|
194
|
+
|
|
195
|
+
<candy:if condition="user.isPremium">
|
|
196
|
+
<span class="badge">⭐ Premium</span>
|
|
197
|
+
</candy:if>
|
|
198
|
+
|
|
199
|
+
<candy:if condition="user.isVerified">
|
|
200
|
+
<span class="badge">✓ Verified</span>
|
|
201
|
+
</candy:if>
|
|
202
|
+
</div>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Nested Conditions
|
|
206
|
+
|
|
207
|
+
```html
|
|
208
|
+
<candy:if condition="user">
|
|
209
|
+
<candy:if condition="user.isActive">
|
|
210
|
+
<candy:if condition="user.subscription">
|
|
211
|
+
<candy:if condition="user.subscription.status === 'active'">
|
|
212
|
+
<div class="premium-content">
|
|
213
|
+
<h2>Premium Content</h2>
|
|
214
|
+
<p>Welcome, premium member!</p>
|
|
215
|
+
</div>
|
|
216
|
+
<candy:elseif condition="user.subscription.status === 'expired'">
|
|
217
|
+
<div class="renewal-notice">
|
|
218
|
+
<p>Your subscription has expired. Please renew to continue.</p>
|
|
219
|
+
<a href="/renew">Renew Now</a>
|
|
220
|
+
</div>
|
|
221
|
+
</candy:if>
|
|
222
|
+
<candy:else>
|
|
223
|
+
<div class="upgrade-notice">
|
|
224
|
+
<p>Upgrade to premium for exclusive content!</p>
|
|
225
|
+
<a href="/upgrade">Upgrade Now</a>
|
|
226
|
+
</div>
|
|
227
|
+
</candy:if>
|
|
228
|
+
<candy:else>
|
|
229
|
+
<div class="inactive-notice">
|
|
230
|
+
<p>Your account is inactive. Please contact support.</p>
|
|
231
|
+
</div>
|
|
232
|
+
</candy:if>
|
|
233
|
+
<candy:else>
|
|
234
|
+
<div class="login-notice">
|
|
235
|
+
<p>Please log in to view this content.</p>
|
|
236
|
+
<a href="/login">Login</a>
|
|
237
|
+
</div>
|
|
238
|
+
</candy:if>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### Conditional Attributes
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<!-- Disabled button -->
|
|
245
|
+
<button <candy:if condition="!canSubmit">disabled</candy:if>>
|
|
246
|
+
Submit
|
|
247
|
+
</button>
|
|
248
|
+
|
|
249
|
+
<!-- Selected option -->
|
|
250
|
+
<select name="country">
|
|
251
|
+
<option value="tr" <candy:if condition="country === 'tr'">selected</candy:if>>Turkey</option>
|
|
252
|
+
<option value="us" <candy:if condition="country === 'us'">selected</candy:if>>USA</option>
|
|
253
|
+
<option value="uk" <candy:if condition="country === 'uk'">selected</candy:if>>UK</option>
|
|
254
|
+
</select>
|
|
255
|
+
|
|
256
|
+
<!-- Checked checkbox -->
|
|
257
|
+
<input
|
|
258
|
+
type="checkbox"
|
|
259
|
+
name="terms"
|
|
260
|
+
<candy:if condition="termsAccepted">checked</candy:if>
|
|
261
|
+
>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Best Practices
|
|
265
|
+
|
|
266
|
+
1. **Keep conditions simple**: Complex logic should be in the controller
|
|
267
|
+
2. **Use meaningful variable names**: Make conditions self-documenting
|
|
268
|
+
3. **Avoid deep nesting**: Refactor complex nested conditions
|
|
269
|
+
4. **Handle null/undefined**: Always check if objects exist before accessing properties
|
|
270
|
+
|
|
271
|
+
**Good:**
|
|
272
|
+
```javascript
|
|
273
|
+
// Controller
|
|
274
|
+
Candy.set('canEdit', user.isAdmin || user.id === post.authorId)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
```html
|
|
278
|
+
<!-- View -->
|
|
279
|
+
<candy:if condition="canEdit">
|
|
280
|
+
<button>Edit</button>
|
|
281
|
+
</candy:if>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Avoid:**
|
|
285
|
+
```html
|
|
286
|
+
<!-- Too complex for a view -->
|
|
287
|
+
<candy:if condition="(user && user.role === 'admin') || (user && post && user.id === post.authorId && !post.isLocked)">
|
|
288
|
+
<button>Edit</button>
|
|
289
|
+
</candy:if>
|
|
290
|
+
```
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
## 🔁 Loops and Iteration
|
|
2
|
+
|
|
3
|
+
Loops allow you to repeat content for each item in an array or object. This is essential for displaying lists, tables, and collections.
|
|
4
|
+
|
|
5
|
+
### For Loop
|
|
6
|
+
|
|
7
|
+
The most common way to iterate over arrays and objects:
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<candy:for in="users" key="index" value="user">
|
|
11
|
+
<div class="user-card">
|
|
12
|
+
<h3><candy var="user.name" /></h3>
|
|
13
|
+
<p><candy var="user.email" /></p>
|
|
14
|
+
</div>
|
|
15
|
+
</candy:for>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Parameters:**
|
|
19
|
+
- `in`: The array or object to loop through (required)
|
|
20
|
+
- `key`: Variable name for the index/key (optional, default: "key")
|
|
21
|
+
- `value`: Variable name for the value (optional, default: "value")
|
|
22
|
+
|
|
23
|
+
### Iterating Over Arrays
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
// Controller
|
|
27
|
+
Candy.set('products', [
|
|
28
|
+
{ name: 'Laptop', price: 999 },
|
|
29
|
+
{ name: 'Mouse', price: 29 },
|
|
30
|
+
{ name: 'Keyboard', price: 79 }
|
|
31
|
+
])
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<!-- View -->
|
|
36
|
+
<div class="products">
|
|
37
|
+
<candy:for in="products" key="index" value="product">
|
|
38
|
+
<div class="product">
|
|
39
|
+
<span class="number"><candy var="index + 1" />.</span>
|
|
40
|
+
<h3><candy var="product.name" /></h3>
|
|
41
|
+
<p>$<candy var="product.price" /></p>
|
|
42
|
+
</div>
|
|
43
|
+
</candy:for>
|
|
44
|
+
</div>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Iterating Over Objects
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
// Controller
|
|
51
|
+
Candy.set('settings', {
|
|
52
|
+
theme: 'dark',
|
|
53
|
+
language: 'en',
|
|
54
|
+
notifications: true
|
|
55
|
+
})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<!-- View -->
|
|
60
|
+
<table>
|
|
61
|
+
<candy:for in="settings" key="settingKey" value="settingValue">
|
|
62
|
+
<tr>
|
|
63
|
+
<td><candy var="settingKey" /></td>
|
|
64
|
+
<td><candy var="settingValue" /></td>
|
|
65
|
+
</tr>
|
|
66
|
+
</candy:for>
|
|
67
|
+
</table>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### While Loop
|
|
71
|
+
|
|
72
|
+
Use while loops for conditional iteration:
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<script:candy>
|
|
76
|
+
let counter = 0;
|
|
77
|
+
</script:candy>
|
|
78
|
+
|
|
79
|
+
<candy:while condition="counter < 5">
|
|
80
|
+
<p>Item <candy var="counter + 1" /></p>
|
|
81
|
+
<script:candy>counter++;</script:candy>
|
|
82
|
+
</candy:while>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Note:** Be careful with while loops to avoid infinite loops. The condition must eventually become false.
|
|
86
|
+
|
|
87
|
+
### Loop Control Statements
|
|
88
|
+
|
|
89
|
+
#### Break
|
|
90
|
+
|
|
91
|
+
Exit the loop early:
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<candy:for in="products" value="product">
|
|
95
|
+
<candy:if condition="product.stock === 0">
|
|
96
|
+
<p class="notice">Some products are out of stock</p>
|
|
97
|
+
<candy:break />
|
|
98
|
+
</candy:if>
|
|
99
|
+
<div><candy var="product.name" /></div>
|
|
100
|
+
</candy:for>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Continue
|
|
104
|
+
|
|
105
|
+
Skip to the next iteration:
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<candy:for in="users" value="user">
|
|
109
|
+
<candy:if condition="user.isBlocked">
|
|
110
|
+
<candy:continue />
|
|
111
|
+
</candy:if>
|
|
112
|
+
|
|
113
|
+
<div class="user">
|
|
114
|
+
<h3><candy var="user.name" /></h3>
|
|
115
|
+
<p><candy var="user.email" /></p>
|
|
116
|
+
</div>
|
|
117
|
+
</candy:for>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Practical Examples
|
|
121
|
+
|
|
122
|
+
#### Product List with Numbering
|
|
123
|
+
|
|
124
|
+
```html
|
|
125
|
+
<div class="product-list">
|
|
126
|
+
<h2>Our Products</h2>
|
|
127
|
+
|
|
128
|
+
<candy:for in="products" key="i" value="product">
|
|
129
|
+
<div class="product-item">
|
|
130
|
+
<span class="number">#<candy var="i + 1" /></span>
|
|
131
|
+
<img src="<candy var="product.image" />" alt="<candy var="product.name" />">
|
|
132
|
+
<h3><candy var="product.name" /></h3>
|
|
133
|
+
<p class="price">$<candy var="product.price" /></p>
|
|
134
|
+
|
|
135
|
+
<candy:if condition="product.discount">
|
|
136
|
+
<span class="discount">-<candy var="product.discount" />%</span>
|
|
137
|
+
</candy:if>
|
|
138
|
+
</div>
|
|
139
|
+
</candy:for>
|
|
140
|
+
</div>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Table with Data
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<table class="users-table">
|
|
147
|
+
<thead>
|
|
148
|
+
<tr>
|
|
149
|
+
<th>#</th>
|
|
150
|
+
<th>Name</th>
|
|
151
|
+
<th>Email</th>
|
|
152
|
+
<th>Role</th>
|
|
153
|
+
<th>Status</th>
|
|
154
|
+
</tr>
|
|
155
|
+
</thead>
|
|
156
|
+
<tbody>
|
|
157
|
+
<candy:for in="users" key="index" value="user">
|
|
158
|
+
<tr>
|
|
159
|
+
<td><candy var="index + 1" /></td>
|
|
160
|
+
<td><candy var="user.name" /></td>
|
|
161
|
+
<td><candy var="user.email" /></td>
|
|
162
|
+
<td><candy var="user.role" /></td>
|
|
163
|
+
<td>
|
|
164
|
+
<candy:if condition="user.isActive">
|
|
165
|
+
<span class="badge success">Active</span>
|
|
166
|
+
<candy:else>
|
|
167
|
+
<span class="badge danger">Inactive</span>
|
|
168
|
+
</candy:if>
|
|
169
|
+
</td>
|
|
170
|
+
</tr>
|
|
171
|
+
</candy:for>
|
|
172
|
+
</tbody>
|
|
173
|
+
</table>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Nested Loops
|
|
177
|
+
|
|
178
|
+
```html
|
|
179
|
+
<div class="categories">
|
|
180
|
+
<candy:for in="categories" value="category">
|
|
181
|
+
<div class="category">
|
|
182
|
+
<h2><candy var="category.name" /></h2>
|
|
183
|
+
|
|
184
|
+
<div class="products">
|
|
185
|
+
<candy:for in="category.products" value="product">
|
|
186
|
+
<div class="product">
|
|
187
|
+
<h3><candy var="product.name" /></h3>
|
|
188
|
+
<p>$<candy var="product.price" /></p>
|
|
189
|
+
</div>
|
|
190
|
+
</candy:for>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</candy:for>
|
|
194
|
+
</div>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### Grid Layout
|
|
198
|
+
|
|
199
|
+
```html
|
|
200
|
+
<div class="grid">
|
|
201
|
+
<candy:for in="items" key="i" value="item">
|
|
202
|
+
<div class="grid-item">
|
|
203
|
+
<img src="<candy var="item.image" />" alt="<candy var="item.title" />">
|
|
204
|
+
<h3><candy var="item.title" /></h3>
|
|
205
|
+
<p><candy var="item.description" /></p>
|
|
206
|
+
|
|
207
|
+
<!-- Add row break every 3 items -->
|
|
208
|
+
<candy:if condition="(i + 1) % 3 === 0">
|
|
209
|
+
<div class="row-break"></div>
|
|
210
|
+
</candy:if>
|
|
211
|
+
</div>
|
|
212
|
+
</candy:for>
|
|
213
|
+
</div>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Filtering with Continue
|
|
217
|
+
|
|
218
|
+
```html
|
|
219
|
+
<div class="active-users">
|
|
220
|
+
<h2>Active Users</h2>
|
|
221
|
+
|
|
222
|
+
<candy:for in="users" value="user">
|
|
223
|
+
<!-- Skip inactive users -->
|
|
224
|
+
<candy:if condition="!user.isActive">
|
|
225
|
+
<candy:continue />
|
|
226
|
+
</candy:if>
|
|
227
|
+
|
|
228
|
+
<!-- Skip blocked users -->
|
|
229
|
+
<candy:if condition="user.isBlocked">
|
|
230
|
+
<candy:continue />
|
|
231
|
+
</candy:if>
|
|
232
|
+
|
|
233
|
+
<div class="user-card">
|
|
234
|
+
<h3><candy var="user.name" /></h3>
|
|
235
|
+
<p><candy var="user.email" /></p>
|
|
236
|
+
</div>
|
|
237
|
+
</candy:for>
|
|
238
|
+
</div>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### Empty State Handling
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<div class="products-section">
|
|
245
|
+
<h2>Products</h2>
|
|
246
|
+
|
|
247
|
+
<candy:if condition="products && products.length > 0">
|
|
248
|
+
<div class="products-grid">
|
|
249
|
+
<candy:for in="products" value="product">
|
|
250
|
+
<div class="product-card">
|
|
251
|
+
<h3><candy var="product.name" /></h3>
|
|
252
|
+
<p>$<candy var="product.price" /></p>
|
|
253
|
+
</div>
|
|
254
|
+
</candy:for>
|
|
255
|
+
</div>
|
|
256
|
+
<candy:else>
|
|
257
|
+
<div class="empty-state">
|
|
258
|
+
<p>No products found.</p>
|
|
259
|
+
<a href="/products/add">Add your first product</a>
|
|
260
|
+
</div>
|
|
261
|
+
</candy:if>
|
|
262
|
+
</div>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Alternating Row Colors
|
|
266
|
+
|
|
267
|
+
```html
|
|
268
|
+
<table>
|
|
269
|
+
<candy:for in="items" key="i" value="item">
|
|
270
|
+
<tr class="<candy:if condition="i % 2 === 0">even<candy:else>odd</candy:if>">
|
|
271
|
+
<td><candy var="item.name" /></td>
|
|
272
|
+
<td><candy var="item.value" /></td>
|
|
273
|
+
</tr>
|
|
274
|
+
</candy:for>
|
|
275
|
+
</table>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### Limited Results with Break
|
|
279
|
+
|
|
280
|
+
```html
|
|
281
|
+
<div class="top-products">
|
|
282
|
+
<h2>Top 5 Products</h2>
|
|
283
|
+
|
|
284
|
+
<script:candy>
|
|
285
|
+
let count = 0;
|
|
286
|
+
</script:candy>
|
|
287
|
+
|
|
288
|
+
<candy:for in="products" value="product">
|
|
289
|
+
<candy:if condition="count >= 5">
|
|
290
|
+
<candy:break />
|
|
291
|
+
</candy:if>
|
|
292
|
+
|
|
293
|
+
<div class="product">
|
|
294
|
+
<h3><candy var="product.name" /></h3>
|
|
295
|
+
<p>$<candy var="product.price" /></p>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<script:candy>count++;</script:candy>
|
|
299
|
+
</candy:for>
|
|
300
|
+
</div>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
#### Pagination with While
|
|
304
|
+
|
|
305
|
+
```html
|
|
306
|
+
<script:candy>
|
|
307
|
+
const itemsPerPage = 10;
|
|
308
|
+
const currentPage = parseInt(Candy.Request.get('page')) || 1;
|
|
309
|
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
|
310
|
+
const endIndex = startIndex + itemsPerPage;
|
|
311
|
+
let index = startIndex;
|
|
312
|
+
</script:candy>
|
|
313
|
+
|
|
314
|
+
<div class="items">
|
|
315
|
+
<candy:while condition="index < endIndex && index < items.length">
|
|
316
|
+
<div class="item">
|
|
317
|
+
<candy var="items[index].name" />
|
|
318
|
+
</div>
|
|
319
|
+
<script:candy>index++;</script:candy>
|
|
320
|
+
</candy:while>
|
|
321
|
+
</div>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Best Practices
|
|
325
|
+
|
|
326
|
+
1. **Check for existence**: Always verify the array/object exists before looping
|
|
327
|
+
2. **Use meaningful names**: Choose descriptive variable names for keys and values
|
|
328
|
+
3. **Avoid complex logic**: Keep loop bodies simple, move complex logic to controllers
|
|
329
|
+
4. **Handle empty states**: Always provide feedback when there are no items
|
|
330
|
+
5. **Be careful with while**: Ensure while loops will eventually terminate
|
|
331
|
+
|
|
332
|
+
**Good:**
|
|
333
|
+
```javascript
|
|
334
|
+
// Controller - prepare data
|
|
335
|
+
Candy.set('activeUsers', users.filter(u => u.isActive))
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
```html
|
|
339
|
+
<!-- View - simple loop -->
|
|
340
|
+
<candy:for in="activeUsers" value="user">
|
|
341
|
+
<div><candy var="user.name" /></div>
|
|
342
|
+
</candy:for>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Avoid:**
|
|
346
|
+
```html
|
|
347
|
+
<!-- Too complex for a view -->
|
|
348
|
+
<candy:for in="users" value="user">
|
|
349
|
+
<candy:if condition="user.isActive && !user.isBlocked && user.role !== 'guest'">
|
|
350
|
+
<div><candy var="user.name" /></div>
|
|
351
|
+
</candy:if>
|
|
352
|
+
</candy:for>
|
|
353
|
+
```
|