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
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
# Advanced AJAX Navigation
|
|
2
2
|
|
|
3
|
-
Advanced techniques and patterns for AJAX navigation in
|
|
3
|
+
Advanced techniques and patterns for AJAX navigation in Odac.
|
|
4
4
|
|
|
5
5
|
**Note:** All examples assume you have properly configured your skeleton template and controller. See the [Quick Start guide](01-quick-start.md#skeleton-structure-required) for setup instructions.
|
|
6
6
|
|
|
7
7
|
## Programmatic Navigation
|
|
8
8
|
|
|
9
|
-
### Using
|
|
9
|
+
### Using odac.load()
|
|
10
10
|
|
|
11
11
|
Navigate programmatically from your code:
|
|
12
12
|
|
|
13
13
|
```javascript
|
|
14
14
|
// Basic usage
|
|
15
|
-
|
|
15
|
+
odac.load('/about')
|
|
16
16
|
|
|
17
17
|
// With callback
|
|
18
|
-
|
|
18
|
+
odac.load('/about', function(page, variables) {
|
|
19
19
|
console.log('Loaded:', page)
|
|
20
20
|
console.log('Data:', variables)
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
// Without updating history
|
|
24
|
-
|
|
24
|
+
odac.load('/about', callback, false)
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
### Use Cases
|
|
28
28
|
|
|
29
29
|
**Redirect after form submission:**
|
|
30
30
|
```javascript
|
|
31
|
-
|
|
31
|
+
odac.form('#my-form', function(data) {
|
|
32
32
|
if (data.result.success) {
|
|
33
|
-
|
|
33
|
+
odac.load('/success')
|
|
34
34
|
}
|
|
35
35
|
})
|
|
36
36
|
```
|
|
@@ -38,16 +38,16 @@ Candy.form('#my-form', function(data) {
|
|
|
38
38
|
**Conditional navigation:**
|
|
39
39
|
```javascript
|
|
40
40
|
if (user.isLoggedIn) {
|
|
41
|
-
|
|
41
|
+
odac.load('/dashboard')
|
|
42
42
|
} else {
|
|
43
|
-
|
|
43
|
+
odac.load('/login')
|
|
44
44
|
}
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
**Timed navigation:**
|
|
48
48
|
```javascript
|
|
49
49
|
setTimeout(() => {
|
|
50
|
-
|
|
50
|
+
odac.load('/next-page')
|
|
51
51
|
}, 3000)
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -58,7 +58,7 @@ setTimeout(() => {
|
|
|
58
58
|
Update different parts of your page simultaneously:
|
|
59
59
|
|
|
60
60
|
```javascript
|
|
61
|
-
|
|
61
|
+
Odac.action({
|
|
62
62
|
navigate: {
|
|
63
63
|
update: {
|
|
64
64
|
content: 'main',
|
|
@@ -75,9 +75,9 @@ Candy.action({
|
|
|
75
75
|
Define all parts in your controller:
|
|
76
76
|
|
|
77
77
|
```javascript
|
|
78
|
-
module.exports = function(
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
module.exports = function(Odac) {
|
|
79
|
+
odac.View.skeleton('main')
|
|
80
|
+
odac.View.set({
|
|
81
81
|
header: 'main',
|
|
82
82
|
content: 'dashboard',
|
|
83
83
|
sidebar: 'dashboard',
|
|
@@ -100,7 +100,7 @@ let appState = {
|
|
|
100
100
|
filters: {}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
Odac.action({
|
|
104
104
|
navigate: {
|
|
105
105
|
update: 'main',
|
|
106
106
|
on: function(page, variables) {
|
|
@@ -121,7 +121,7 @@ Candy.action({
|
|
|
121
121
|
Persist state across page reloads:
|
|
122
122
|
|
|
123
123
|
```javascript
|
|
124
|
-
|
|
124
|
+
Odac.action({
|
|
125
125
|
navigate: {
|
|
126
126
|
update: 'main',
|
|
127
127
|
on: function(page, variables) {
|
|
@@ -148,7 +148,7 @@ Candy.action({
|
|
|
148
148
|
Add custom animations:
|
|
149
149
|
|
|
150
150
|
```javascript
|
|
151
|
-
|
|
151
|
+
Odac.action({
|
|
152
152
|
navigate: {
|
|
153
153
|
update: 'main',
|
|
154
154
|
on: function(page, variables) {
|
|
@@ -170,7 +170,7 @@ Candy.action({
|
|
|
170
170
|
Smooth page transitions:
|
|
171
171
|
|
|
172
172
|
```javascript
|
|
173
|
-
|
|
173
|
+
Odac.action({
|
|
174
174
|
navigate: {
|
|
175
175
|
update: 'main',
|
|
176
176
|
on: function(page, variables) {
|
|
@@ -209,7 +209,7 @@ main.page-enter-active {
|
|
|
209
209
|
Automatically scroll to top on navigation:
|
|
210
210
|
|
|
211
211
|
```javascript
|
|
212
|
-
|
|
212
|
+
Odac.action({
|
|
213
213
|
navigate: {
|
|
214
214
|
update: 'main',
|
|
215
215
|
on: function(page, variables) {
|
|
@@ -229,7 +229,7 @@ Remember scroll position:
|
|
|
229
229
|
```javascript
|
|
230
230
|
let scrollPositions = {}
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
Odac.action({
|
|
233
233
|
navigate: {
|
|
234
234
|
update: 'main',
|
|
235
235
|
on: function(page, variables) {
|
|
@@ -256,7 +256,7 @@ Candy.action({
|
|
|
256
256
|
Scroll to specific element after navigation:
|
|
257
257
|
|
|
258
258
|
```javascript
|
|
259
|
-
|
|
259
|
+
Odac.action({
|
|
260
260
|
navigate: {
|
|
261
261
|
update: 'main',
|
|
262
262
|
on: function(page, variables) {
|
|
@@ -282,10 +282,10 @@ Candy.action({
|
|
|
282
282
|
Gracefully handle navigation errors:
|
|
283
283
|
|
|
284
284
|
```javascript
|
|
285
|
-
// Override
|
|
286
|
-
const originalLoad =
|
|
285
|
+
// Override odac.load to add error handling
|
|
286
|
+
const originalLoad = odac.load.bind(Odac)
|
|
287
287
|
|
|
288
|
-
|
|
288
|
+
odac.load = function(url, callback, push) {
|
|
289
289
|
try {
|
|
290
290
|
originalLoad(url, function(page, variables) {
|
|
291
291
|
if (callback) callback(page, variables)
|
|
@@ -308,7 +308,7 @@ function loadWithRetry(url, maxRetries = 3) {
|
|
|
308
308
|
|
|
309
309
|
function attempt() {
|
|
310
310
|
attempts++
|
|
311
|
-
|
|
311
|
+
odac.load(url,
|
|
312
312
|
(page, vars) => {
|
|
313
313
|
console.log('Success after', attempts, 'attempts')
|
|
314
314
|
},
|
|
@@ -333,7 +333,7 @@ Prevent rapid navigation:
|
|
|
333
333
|
```javascript
|
|
334
334
|
let navigationTimeout
|
|
335
335
|
|
|
336
|
-
|
|
336
|
+
Odac.action({
|
|
337
337
|
click: {
|
|
338
338
|
'a[href^="/"]': function(e) {
|
|
339
339
|
clearTimeout(navigationTimeout)
|
|
@@ -353,7 +353,7 @@ Prefetch pages on hover:
|
|
|
353
353
|
```javascript
|
|
354
354
|
let prefetchCache = {}
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
Odac.action({
|
|
357
357
|
mouseover: {
|
|
358
358
|
'a[href^="/"]': function() {
|
|
359
359
|
const url = this.getAttribute('href')
|
|
@@ -362,7 +362,7 @@ Candy.action({
|
|
|
362
362
|
// Prefetch the page
|
|
363
363
|
fetch(url, {
|
|
364
364
|
headers: {
|
|
365
|
-
'X-
|
|
365
|
+
'X-Odac': 'prefetch'
|
|
366
366
|
}
|
|
367
367
|
}).then(response => response.text())
|
|
368
368
|
.then(html => {
|
|
@@ -381,7 +381,7 @@ Candy.action({
|
|
|
381
381
|
Track page views:
|
|
382
382
|
|
|
383
383
|
```javascript
|
|
384
|
-
|
|
384
|
+
Odac.action({
|
|
385
385
|
navigate: {
|
|
386
386
|
update: 'main',
|
|
387
387
|
on: function(page, variables) {
|
|
@@ -407,7 +407,7 @@ Candy.action({
|
|
|
407
407
|
Redux/Vuex integration:
|
|
408
408
|
|
|
409
409
|
```javascript
|
|
410
|
-
|
|
410
|
+
Odac.action({
|
|
411
411
|
navigate: {
|
|
412
412
|
update: 'main',
|
|
413
413
|
on: function(page, variables) {
|
|
@@ -430,7 +430,7 @@ Test navigation logic:
|
|
|
430
430
|
```javascript
|
|
431
431
|
describe('Navigation', () => {
|
|
432
432
|
it('should update active nav on page change', () => {
|
|
433
|
-
|
|
433
|
+
Odac.action({
|
|
434
434
|
navigate: {
|
|
435
435
|
update: 'main',
|
|
436
436
|
on: (page) => {
|
|
@@ -440,7 +440,7 @@ describe('Navigation', () => {
|
|
|
440
440
|
})
|
|
441
441
|
|
|
442
442
|
// Simulate navigation
|
|
443
|
-
|
|
443
|
+
odac.load('/about')
|
|
444
444
|
|
|
445
445
|
// Assert
|
|
446
446
|
expect(document.querySelector('.nav-link.active').href)
|
|
@@ -463,7 +463,7 @@ describe('Navigation', () => {
|
|
|
463
463
|
### Dashboard Navigation
|
|
464
464
|
|
|
465
465
|
```javascript
|
|
466
|
-
|
|
466
|
+
Odac.action({
|
|
467
467
|
navigate: {
|
|
468
468
|
links: '.sidebar a, .breadcrumb a',
|
|
469
469
|
update: {
|
|
@@ -482,7 +482,7 @@ Candy.action({
|
|
|
482
482
|
### E-commerce
|
|
483
483
|
|
|
484
484
|
```javascript
|
|
485
|
-
|
|
485
|
+
Odac.action({
|
|
486
486
|
navigate: {
|
|
487
487
|
update: {
|
|
488
488
|
content: 'main',
|
|
@@ -499,7 +499,7 @@ Candy.action({
|
|
|
499
499
|
### Blog
|
|
500
500
|
|
|
501
501
|
```javascript
|
|
502
|
-
|
|
502
|
+
Odac.action({
|
|
503
503
|
navigate: {
|
|
504
504
|
update: 'main',
|
|
505
505
|
on: (page, vars) => {
|
|
@@ -516,4 +516,4 @@ Candy.action({
|
|
|
516
516
|
|
|
517
517
|
- Learn about [Form Handling](../03-forms/01-form-handling.md)
|
|
518
518
|
- Explore [API Requests](../04-api-requests/01-get-post.md)
|
|
519
|
-
- Check [
|
|
519
|
+
- Check [odac.js Overview](../01-overview/01-introduction.md)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Form Handling with
|
|
1
|
+
# Form Handling with odac.js
|
|
2
2
|
|
|
3
|
-
Learn how to handle forms with automatic AJAX submission, CSRF protection, and validation in
|
|
3
|
+
Learn how to handle forms with automatic AJAX submission, CSRF protection, and validation in Odac.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -14,14 +14,14 @@ Learn how to handle forms with automatic AJAX submission, CSRF protection, and v
|
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
```javascript
|
|
17
|
-
|
|
17
|
+
odac.form('#contact-form', function(data) {
|
|
18
18
|
if (data.result.success) {
|
|
19
19
|
alert('Form submitted successfully!')
|
|
20
20
|
}
|
|
21
21
|
})
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
That's it!
|
|
24
|
+
That's it! odac.js handles:
|
|
25
25
|
- ✅ AJAX submission
|
|
26
26
|
- ✅ CSRF token (automatic)
|
|
27
27
|
- ✅ Validation errors (auto-generated)
|
|
@@ -35,7 +35,7 @@ That's it! candy.js handles:
|
|
|
35
35
|
### Basic Usage
|
|
36
36
|
|
|
37
37
|
```javascript
|
|
38
|
-
|
|
38
|
+
odac.form('#my-form', function(data) {
|
|
39
39
|
console.log('Response:', data)
|
|
40
40
|
})
|
|
41
41
|
```
|
|
@@ -43,7 +43,7 @@ Candy.form('#my-form', function(data) {
|
|
|
43
43
|
### With Options
|
|
44
44
|
|
|
45
45
|
```javascript
|
|
46
|
-
|
|
46
|
+
odac.form({
|
|
47
47
|
form: '#my-form',
|
|
48
48
|
messages: true, // Show error/success messages
|
|
49
49
|
loading: function(percent) {
|
|
@@ -59,7 +59,7 @@ Candy.form({
|
|
|
59
59
|
### Redirect After Submit
|
|
60
60
|
|
|
61
61
|
```javascript
|
|
62
|
-
|
|
62
|
+
odac.form('#my-form', '/success-page')
|
|
63
63
|
// Redirects to /success-page on success
|
|
64
64
|
```
|
|
65
65
|
|
|
@@ -75,17 +75,17 @@ If you want to control where errors appear, add error elements manually:
|
|
|
75
75
|
|
|
76
76
|
```html
|
|
77
77
|
<input name="email" type="email">
|
|
78
|
-
<span
|
|
78
|
+
<span odac-form-error="email"></span>
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
**If not present:** The system automatically creates `<span
|
|
81
|
+
**If not present:** The system automatically creates `<span odac-form-error="email">` after the input field.
|
|
82
82
|
|
|
83
83
|
**If present:** The system uses your existing element and updates its content.
|
|
84
84
|
|
|
85
85
|
### Custom Error Display
|
|
86
86
|
|
|
87
87
|
```javascript
|
|
88
|
-
|
|
88
|
+
odac.form('#my-form', function(data) {
|
|
89
89
|
if (!data.result.success) {
|
|
90
90
|
// Custom error handling
|
|
91
91
|
Object.entries(data.errors).forEach(([field, message]) => {
|
|
@@ -99,7 +99,7 @@ Candy.form('#my-form', function(data) {
|
|
|
99
99
|
|
|
100
100
|
```css
|
|
101
101
|
/* Error message */
|
|
102
|
-
[
|
|
102
|
+
[odac-form-error] {
|
|
103
103
|
color: #ef4444;
|
|
104
104
|
font-size: 0.875rem;
|
|
105
105
|
margin-top: 0.25rem;
|
|
@@ -107,7 +107,7 @@ Candy.form('#my-form', function(data) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/* Invalid input */
|
|
110
|
-
input.
|
|
110
|
+
input._odac_error {
|
|
111
111
|
border-color: #ef4444;
|
|
112
112
|
}
|
|
113
113
|
```
|
|
@@ -126,18 +126,18 @@ If you want to control where success messages appear, add a success element manu
|
|
|
126
126
|
<form id="my-form" action="/api/submit" method="POST">
|
|
127
127
|
<!-- form fields -->
|
|
128
128
|
<button type="submit">Submit</button>
|
|
129
|
-
<div
|
|
129
|
+
<div odac-form-success></div>
|
|
130
130
|
</form>
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
**If not present:** The system automatically creates `<span
|
|
133
|
+
**If not present:** The system automatically creates `<span odac-form-success>` at the end of the form.
|
|
134
134
|
|
|
135
135
|
**If present:** The system uses your existing element and updates its content.
|
|
136
136
|
|
|
137
137
|
### Custom Success Message
|
|
138
138
|
|
|
139
139
|
```javascript
|
|
140
|
-
|
|
140
|
+
odac.form('#my-form', function(data) {
|
|
141
141
|
if (data.result.success) {
|
|
142
142
|
document.querySelector('#custom-message').innerHTML =
|
|
143
143
|
'Thank you! Your form has been submitted.'
|
|
@@ -157,7 +157,7 @@ Candy.form('#my-form', function(data) {
|
|
|
157
157
|
```
|
|
158
158
|
|
|
159
159
|
```javascript
|
|
160
|
-
|
|
160
|
+
odac.form('#upload-form', function(data) {
|
|
161
161
|
if (data.result.success) {
|
|
162
162
|
console.log('File uploaded:', data.result.filename)
|
|
163
163
|
}
|
|
@@ -167,7 +167,7 @@ Candy.form('#upload-form', function(data) {
|
|
|
167
167
|
### Upload Progress
|
|
168
168
|
|
|
169
169
|
```javascript
|
|
170
|
-
|
|
170
|
+
odac.form({
|
|
171
171
|
form: '#upload-form',
|
|
172
172
|
loading: function(percent) {
|
|
173
173
|
document.querySelector('#progress').style.width = percent + '%'
|
|
@@ -189,7 +189,7 @@ Candy.form({
|
|
|
189
189
|
### Disable Messages
|
|
190
190
|
|
|
191
191
|
```javascript
|
|
192
|
-
|
|
192
|
+
odac.form({
|
|
193
193
|
form: '#my-form',
|
|
194
194
|
messages: false // Don't show automatic messages
|
|
195
195
|
}, function(data) {
|
|
@@ -200,7 +200,7 @@ Candy.form({
|
|
|
200
200
|
### Disable Specific Messages
|
|
201
201
|
|
|
202
202
|
```javascript
|
|
203
|
-
|
|
203
|
+
odac.form({
|
|
204
204
|
form: '#my-form',
|
|
205
205
|
messages: ['error'] // Only show errors, not success
|
|
206
206
|
}, function(data) {
|
|
@@ -211,7 +211,7 @@ Candy.form({
|
|
|
211
211
|
### Form Reset
|
|
212
212
|
|
|
213
213
|
```javascript
|
|
214
|
-
|
|
214
|
+
odac.form('#my-form', function(data) {
|
|
215
215
|
if (data.result.success) {
|
|
216
216
|
// Reset the form
|
|
217
217
|
document.querySelector('#my-form').reset()
|
|
@@ -229,7 +229,7 @@ document.querySelector('#my-form').addEventListener('submit', function(e) {
|
|
|
229
229
|
}
|
|
230
230
|
})
|
|
231
231
|
|
|
232
|
-
|
|
232
|
+
odac.form('#my-form', function(data) {
|
|
233
233
|
console.log('Submitted!')
|
|
234
234
|
})
|
|
235
235
|
```
|
|
@@ -240,9 +240,9 @@ Candy.form('#my-form', function(data) {
|
|
|
240
240
|
|
|
241
241
|
```javascript
|
|
242
242
|
// controller/post/contact.js
|
|
243
|
-
module.exports = async function(
|
|
244
|
-
const email = await
|
|
245
|
-
const message = await
|
|
243
|
+
module.exports = async function(Odac) {
|
|
244
|
+
const email = await odac.Request.request('email')
|
|
245
|
+
const message = await odac.Request.request('message')
|
|
246
246
|
|
|
247
247
|
// Validation
|
|
248
248
|
const errors = {}
|
|
@@ -272,7 +272,7 @@ module.exports = async function(Candy) {
|
|
|
272
272
|
|
|
273
273
|
```javascript
|
|
274
274
|
// route/www.js
|
|
275
|
-
|
|
275
|
+
odac.Route.post('/api/contact', 'contact')
|
|
276
276
|
```
|
|
277
277
|
|
|
278
278
|
## Validation
|
|
@@ -292,8 +292,8 @@ Use HTML5 validation:
|
|
|
292
292
|
Always validate on the server:
|
|
293
293
|
|
|
294
294
|
```javascript
|
|
295
|
-
module.exports = async function(
|
|
296
|
-
const email = await
|
|
295
|
+
module.exports = async function(Odac) {
|
|
296
|
+
const email = await odac.Request.request('email')
|
|
297
297
|
|
|
298
298
|
const errors = {}
|
|
299
299
|
|
|
@@ -336,7 +336,7 @@ module.exports = async function(Candy) {
|
|
|
336
336
|
**Note:** Error and success elements are auto-generated. Add them manually only if you need custom positioning or styling.
|
|
337
337
|
|
|
338
338
|
```javascript
|
|
339
|
-
|
|
339
|
+
odac.form('#contact-form', function(data) {
|
|
340
340
|
if (data.result.success) {
|
|
341
341
|
document.querySelector('#contact-form').reset()
|
|
342
342
|
}
|
|
@@ -354,7 +354,7 @@ Candy.form('#contact-form', function(data) {
|
|
|
354
354
|
```
|
|
355
355
|
|
|
356
356
|
```javascript
|
|
357
|
-
|
|
357
|
+
odac.form('#login-form', function(data) {
|
|
358
358
|
if (data.result.success) {
|
|
359
359
|
// Redirect to dashboard
|
|
360
360
|
window.location.href = '/dashboard'
|
|
@@ -379,7 +379,7 @@ Candy.form('#login-form', function(data) {
|
|
|
379
379
|
```html
|
|
380
380
|
<div class="form-group">
|
|
381
381
|
<input name="email" type="email" required>
|
|
382
|
-
<span
|
|
382
|
+
<span odac-form-error="email" class="error-message"></span>
|
|
383
383
|
</div>
|
|
384
384
|
```
|
|
385
385
|
|
|
@@ -396,7 +396,7 @@ Candy.form('#login-form', function(data) {
|
|
|
396
396
|
|
|
397
397
|
### Form Not Submitting
|
|
398
398
|
|
|
399
|
-
- Check that `
|
|
399
|
+
- Check that `Odac.form()` is called after DOM is ready
|
|
400
400
|
- Verify the form selector is correct
|
|
401
401
|
- Check browser console for errors
|
|
402
402
|
|
|
@@ -405,7 +405,7 @@ Candy.form('#login-form', function(data) {
|
|
|
405
405
|
- Errors are automatically created - no manual elements needed
|
|
406
406
|
- Check that server returns errors in correct format: `{result: {success: false}, errors: {fieldName: 'message'}}`
|
|
407
407
|
- Verify `messages` option is not set to `false`
|
|
408
|
-
- If using custom error elements, ensure `
|
|
408
|
+
- If using custom error elements, ensure `odac-form-error` attributes match field names exactly
|
|
409
409
|
|
|
410
410
|
### CSRF Token Errors
|
|
411
411
|
|