@a.nemreen/dga-dynamic-form 0.1.7 → 0.1.9
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/README.md +173 -138
- package/fesm2022/a.nemreen-dga-dynamic-form.mjs +249 -11
- package/package.json +6 -2
- package/types/a.nemreen-dga-dynamic-form.d.ts +93 -40
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Schema-driven dynamic forms for Angular, rendered with [`@a.nemreen/dga-ui`](https://www.npmjs.com/package/@a.nemreen/dga-ui) controls.
|
|
4
4
|
|
|
5
|
-
Version
|
|
5
|
+
**Version 0.1.9** — locale-aware labels and visibility, plus adapter/token updates.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -25,16 +25,86 @@ export const appConfig = {
|
|
|
25
25
|
httpSubmit: true,
|
|
26
26
|
httpLookup: true,
|
|
27
27
|
dgaToast: true,
|
|
28
|
-
|
|
29
|
-
// allowedOrigins: ['https://api.example.com'],
|
|
28
|
+
allowedOrigins: ['https://api.example.com'],
|
|
30
29
|
}),
|
|
31
30
|
],
|
|
32
31
|
};
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
**Security
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
**Security:** Treat CMS schemas as untrusted unless `allowedOrigins` is set. HTTP adapters reject non-`http(s)` URLs; same-origin paths (`/api/...`) are always allowed. Validate uploads server-side.
|
|
35
|
+
|
|
36
|
+
### reCAPTCHA (production HTTP submits)
|
|
37
|
+
|
|
38
|
+
Use **reCAPTCHA v3** (invisible). There is **no** `type: 'captcha'` field in the form schema.
|
|
39
|
+
|
|
40
|
+
| Audience | What happens |
|
|
41
|
+
|----------|----------------|
|
|
42
|
+
| **End user** | No checkbox. Submit works like a normal form; Google scores in the background. |
|
|
43
|
+
| **Developer** | Wire `ng-recaptcha` + override `DGA_CAPTCHA_ADAPTER` in `app.config`. |
|
|
44
|
+
| **Backend** | Read `X-Recaptcha-Token` and verify with Google's secret key. |
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install ng-recaptcha
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// environments/environment.ts
|
|
52
|
+
export const environment = {
|
|
53
|
+
recaptchaSiteKey: '6Lc...your-v3-site-key...',
|
|
54
|
+
};
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`provideDgaDynamicForm` registers a **no-op** `DGA_CAPTCHA_ADAPTER` (token is always `null`). Override it after `provideDgaDynamicForm`. On submit the package calls `getToken('submit')` and sends header `X-Recaptcha-Token`.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { importProvidersFrom } from '@angular/core';
|
|
61
|
+
import {
|
|
62
|
+
RECAPTCHA_V3_SITE_KEY,
|
|
63
|
+
RecaptchaV3Module,
|
|
64
|
+
ReCaptchaV3Service,
|
|
65
|
+
} from 'ng-recaptcha';
|
|
66
|
+
import { catchError, of } from 'rxjs';
|
|
67
|
+
import {
|
|
68
|
+
DGA_CAPTCHA_ADAPTER,
|
|
69
|
+
provideDgaDynamicForm,
|
|
70
|
+
} from '@a.nemreen/dga-dynamic-form';
|
|
71
|
+
|
|
72
|
+
export const appConfig = {
|
|
73
|
+
providers: [
|
|
74
|
+
provideHttpClient(),
|
|
75
|
+
importProvidersFrom(RecaptchaV3Module),
|
|
76
|
+
{ provide: RECAPTCHA_V3_SITE_KEY, useValue: environment.recaptchaSiteKey },
|
|
77
|
+
provideDgaDynamicForm({ httpSubmit: true }),
|
|
78
|
+
{
|
|
79
|
+
provide: DGA_CAPTCHA_ADAPTER,
|
|
80
|
+
useFactory: (recaptcha: ReCaptchaV3Service) => ({
|
|
81
|
+
getToken: (action?: string) => {
|
|
82
|
+
const googleAction = action === 'submit' ? 'form_submit' : action ?? 'form_submit';
|
|
83
|
+
return recaptcha.execute(googleAction).pipe(catchError(() => of(null)));
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
deps: [ReCaptchaV3Service],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Form config — set `endpoint` (do **not** use `emitOnly` for live POSTs):
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
readonly config: DgaDynamicFormConfig = {
|
|
96
|
+
endpoint: 'https://api.example.com/contact',
|
|
97
|
+
method: 'POST',
|
|
98
|
+
fields: [/* no captcha field */],
|
|
99
|
+
};
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Backend:** verify token via `https://www.google.com/recaptcha/api/siteverify` with secret key; check `action === 'form_submit'` and score threshold.
|
|
103
|
+
|
|
104
|
+
**Troubleshooting:** token null → provide site key + `RecaptchaV3Module`; captcha skipped → form uses `emitOnly`; script blocked → CSP must allow `www.google.com` and `www.gstatic.com`. Use `RecaptchaV3Module` only (not v2 `RecaptchaModule` checkbox).
|
|
105
|
+
|
|
106
|
+
Full guide: demo `/dynamic-form` → reCAPTCHA section.
|
|
107
|
+
|
|
38
108
|
```css
|
|
39
109
|
@source "../node_modules/@a.nemreen/dga-dynamic-form/**/*.{mjs,js}";
|
|
40
110
|
@source "../node_modules/@a.nemreen/dga-ui/**/*.{mjs,js}";
|
|
@@ -51,31 +121,46 @@ readonly config: DgaDynamicFormConfig = {
|
|
|
51
121
|
submitButtonLabel: { en: 'Send', ar: 'إرسال' },
|
|
52
122
|
fields: [
|
|
53
123
|
{
|
|
54
|
-
name: '
|
|
55
|
-
type: '
|
|
56
|
-
label: { en: '
|
|
124
|
+
name: 'password',
|
|
125
|
+
type: 'password',
|
|
126
|
+
label: { en: 'Password', ar: 'كلمة المرور' },
|
|
57
127
|
required: true,
|
|
58
|
-
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'confirmPassword',
|
|
131
|
+
type: 'password',
|
|
132
|
+
label: { en: 'Confirm', ar: 'تأكيد' },
|
|
133
|
+
required: true,
|
|
134
|
+
validation: {
|
|
135
|
+
errorMessages: {
|
|
136
|
+
passwordMismatch: { en: 'Passwords must match', ar: 'يجب أن تتطابق كلمات المرور' },
|
|
137
|
+
},
|
|
138
|
+
},
|
|
59
139
|
},
|
|
60
140
|
],
|
|
61
141
|
};
|
|
62
142
|
```
|
|
63
143
|
|
|
64
144
|
```html
|
|
65
|
-
<dga-dynamic-form
|
|
145
|
+
<dga-dynamic-form
|
|
146
|
+
[config]="config"
|
|
147
|
+
[locale]="'ar'"
|
|
148
|
+
(formSubmitted)="onSubmit($event)"
|
|
149
|
+
(fileDownload)="onDownload($event)"
|
|
150
|
+
/>
|
|
66
151
|
```
|
|
67
152
|
|
|
153
|
+
When both `password` and `confirmPassword` fields exist, `passwordMismatch` validation is applied automatically.
|
|
154
|
+
|
|
68
155
|
---
|
|
69
156
|
|
|
70
157
|
## Config reference (`DgaDynamicFormConfig`)
|
|
71
158
|
|
|
72
|
-
Passed as `[config]` on `<dga-dynamic-form>`.
|
|
73
|
-
|
|
74
159
|
| Key | Type | Default | Description |
|
|
75
160
|
|-----|------|---------|-------------|
|
|
76
|
-
| `endpoint` | `string?` | — | Submit URL. Omit
|
|
77
|
-
| `method` | `
|
|
78
|
-
| `emitOnly` | `boolean?` | `false` | Skip HTTP; emit
|
|
161
|
+
| `endpoint` | `string?` | — | Submit URL. Omit or use `emitOnly` to only emit `formSubmitted`. |
|
|
162
|
+
| `method` | `DgaHttpMethod` | `'POST'` | HTTP method for submit adapter. |
|
|
163
|
+
| `emitOnly` | `boolean?` | `false` | Skip HTTP; emit payload on `formSubmitted`. |
|
|
79
164
|
| `fields` | `DgaFormField[]` | **required** | Field list. Can be `[]` when using `wizard.steps` only. |
|
|
80
165
|
| `description` | `DgaFormLabel?` | — | Bilingual text above the form. |
|
|
81
166
|
| `submitButtonLabel` | `DgaFormLabel?` | Submit / إرسال | Primary button label. |
|
|
@@ -83,47 +168,31 @@ Passed as `[config]` on `<dga-dynamic-form>`.
|
|
|
83
168
|
| `saveButtonLabel` | `DgaFormLabel?` | — | Draft save button label. |
|
|
84
169
|
| `successMessage` | `DgaFormLabel?` | — | Toast on success. |
|
|
85
170
|
| `errorMessage` | `DgaFormLabel?` | — | Toast on failure. |
|
|
86
|
-
| `fieldMapping` | `Record<string, string>?` | — | Rename field keys in
|
|
171
|
+
| `fieldMapping` | `Record<string, string>?` | — | Rename field keys in submit payload. |
|
|
87
172
|
| `payloadTransformer` | `(data) => object \| FormData` | — | Final reshape before submit. |
|
|
88
173
|
| `formId` | `string?` | — | localStorage namespace for drafts. |
|
|
89
174
|
| `enableLocalStorageSave` | `boolean?` | `false` | Enable Save draft + restore on init. |
|
|
90
175
|
| `idempotencyKey` | `string?` | — | Sent as `Idempotency-Key` header. |
|
|
176
|
+
| `consentBaseUrl` | `string?` | — | CMS base for `consentServiceName` fields → GET `{base}/consents/{name}`. |
|
|
91
177
|
| `wizard` | `DgaWizardConfig?` | — | Multi-step mode (see below). |
|
|
92
178
|
|
|
93
|
-
`DgaFormLabel` is
|
|
94
|
-
|
|
95
|
-
### Example with endpoint
|
|
96
|
-
|
|
97
|
-
```ts
|
|
98
|
-
const EDIT_STUDY: DgaDynamicFormConfig = {
|
|
99
|
-
endpoint: `${apiUrl}/consulting-studies/{id}`,
|
|
100
|
-
method: 'PUT',
|
|
101
|
-
successMessage: { en: 'Saved', ar: 'تم الحفظ' },
|
|
102
|
-
errorMessage: { en: 'Failed', ar: 'فشل' },
|
|
103
|
-
fields: [ /* ... */ ],
|
|
104
|
-
};
|
|
105
|
-
```
|
|
179
|
+
`DgaFormLabel` is a map of locale code → string (typically `{ en, ar }`, optional extra keys).
|
|
106
180
|
|
|
107
181
|
---
|
|
108
182
|
|
|
109
|
-
## Wizard (`config.wizard`
|
|
110
|
-
|
|
111
|
-
When `wizard.enabled` is true, fields are taken from `wizard.steps[].fields` (root `fields` can be empty).
|
|
183
|
+
## Wizard (`config.wizard`)
|
|
112
184
|
|
|
113
185
|
| Key | Type | Default | Description |
|
|
114
186
|
|-----|------|---------|-------------|
|
|
115
187
|
| `enabled` | `boolean` | — | Turn on stepper UI. |
|
|
116
188
|
| `steps` | `DgaFormStep[]` | — | Ordered steps. |
|
|
117
189
|
| `validateOnStepChange` | `boolean?` | `true` | Block Next if current step invalid. |
|
|
118
|
-
| `allowSkipSteps` | `boolean?` | `false` | Allow jumping ahead
|
|
190
|
+
| `allowSkipSteps` | `boolean?` | `false` | Allow jumping ahead in stepper. |
|
|
119
191
|
| `showSaveButton` | `boolean?` | — | Show draft save in wizard chrome. |
|
|
120
|
-
| `autoSave` | `boolean?` | — |
|
|
121
|
-
| `saveEndpoint` | `string?` | — |
|
|
122
|
-
| `saveMethod` | `DgaHttpMethod?` |
|
|
123
|
-
| `nextButtonText` | `DgaFormLabel?` |
|
|
124
|
-
| `previousButtonText` | `DgaFormLabel?` | Previous / السابق | |
|
|
125
|
-
| `submitButtonText` | `DgaFormLabel?` | Finish / إنهاء | Final step. |
|
|
126
|
-
| `saveButtonText` | `DgaFormLabel?` | Save / حفظ | |
|
|
192
|
+
| `autoSave` | `boolean?` | — | When true + `saveEndpoint`, HTTP draft save on Save. |
|
|
193
|
+
| `saveEndpoint` | `string?` | — | Draft HTTP URL. |
|
|
194
|
+
| `saveMethod` | `DgaHttpMethod?` | `'POST'` | Method for `saveEndpoint`. |
|
|
195
|
+
| `nextButtonText` / `previousButtonText` / `submitButtonText` / `saveButtonText` | `DgaFormLabel?` | — | Wizard chrome labels. |
|
|
127
196
|
|
|
128
197
|
### Step (`DgaFormStep`)
|
|
129
198
|
|
|
@@ -133,113 +202,93 @@ When `wizard.enabled` is true, fields are taken from `wizard.steps[].fields` (ro
|
|
|
133
202
|
| `title` | `DgaFormLabel` | Stepper title. |
|
|
134
203
|
| `description` | `DgaFormLabel?` | Stepper subtitle. |
|
|
135
204
|
| `fields` | `DgaFormField[]` | Fields in this step. |
|
|
136
|
-
| `optional` | `boolean?` |
|
|
205
|
+
| `optional` | `boolean?` | Skip validation for this step. |
|
|
206
|
+
| `initialData` | `Record<string, unknown>?` | Pre-fill when entering the step. |
|
|
137
207
|
|
|
138
208
|
---
|
|
139
209
|
|
|
140
210
|
## Field (`DgaFormField`)
|
|
141
211
|
|
|
142
|
-
### Core
|
|
143
|
-
|
|
144
|
-
| Key | Type | Description |
|
|
145
|
-
|-----|------|-------------|
|
|
146
|
-
| `name` | `string` | FormControl name / payload key. |
|
|
147
|
-
| `type` | `DgaFormFieldType` | See built-in types below. |
|
|
148
|
-
| `label` | `DgaFormLabel` | Bilingual label. |
|
|
149
|
-
| `required` | `boolean?` | Always-required. |
|
|
150
|
-
| `placeholder` | `DgaFormLabel?` | |
|
|
151
|
-
| `hint` | `DgaFormLabel?` | Helper under the field. |
|
|
152
|
-
| `value` | `unknown?` | Initial value. |
|
|
153
|
-
| `disabled` | `boolean?` | |
|
|
154
|
-
| `readonly` | `boolean?` | Text-like inputs. |
|
|
155
|
-
| `hidden` | `boolean?` | Hide from layout. |
|
|
156
|
-
| `excludeFromPayload` | `boolean?` | Keep in UI; drop from submit body. |
|
|
157
|
-
| `columns` | `{ sm?, md?, lg?, xl? }` | 12-column grid spans (default 12). |
|
|
158
|
-
| `maxLength` | `number?` | |
|
|
159
|
-
| `rows` | `number?` | Textarea rows. |
|
|
160
|
-
| `inputRestriction` | `'numbers' \| 'english' \| 'arabic'` | Filter keystrokes. |
|
|
161
|
-
|
|
162
212
|
### Built-in `type` values
|
|
163
213
|
|
|
164
|
-
`text` · `email` · `textarea` · `number` · `otp` · `phone` · `select` · `multiselect` · `checkbox` · `radio` · `date` · `chips` · `toggle` · `file` · `hidden`
|
|
214
|
+
`text` · `email` · `password` · `textarea` · `number` · `otp` · `phone` · `select` · `multiselect` · `checkbox` · `radio` · `date` · `chips` · `toggle` · `file` · `multifile` · `multifilestaged` · `hidden`
|
|
165
215
|
|
|
166
|
-
|
|
216
|
+
Fields named `password` or `confirmPassword` with `type: 'text'` are rendered as password inputs.
|
|
167
217
|
|
|
168
|
-
|
|
169
|
-
|-----|------|-------------|
|
|
170
|
-
| `options` | `DgaFormFieldOption[]?` | Static options. |
|
|
171
|
-
| `groupedOptions` | `{ groupLabel, options }[]?` | **Planned** — flattened for visibility only today. |
|
|
172
|
-
| `optionsLayout` | `'stack' \| 'grid'` | Radio/checkbox layout (default `stack`). |
|
|
218
|
+
### Visibility & conditional required
|
|
173
219
|
|
|
174
|
-
|
|
220
|
+
| Key | Description |
|
|
221
|
+
|-----|-------------|
|
|
222
|
+
| `dependsOn` | Other field that controls visibility. |
|
|
223
|
+
| `showOnValues` | Show when `dependsOn` value is in list. |
|
|
224
|
+
| `showOnTitleEn` / `showOnTitleAr` | Show when selected option label matches. |
|
|
225
|
+
| `showWhenOptionProperty` | Show when selected option has `property === value`. |
|
|
226
|
+
| `extraField` | Show when selected option has `requiresTextInput: true`. |
|
|
227
|
+
| `extraFieldTrigger` | Trigger field for `extraField` (defaults to `dependsOn`). |
|
|
228
|
+
| `requiredWhen` | `{ field, value?, values?, optionProperty?, optionPropertyValue? }` |
|
|
175
229
|
|
|
176
|
-
###
|
|
230
|
+
### Lookup (remote options)
|
|
177
231
|
|
|
178
|
-
| Key |
|
|
179
|
-
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
232
|
+
| Key | Description |
|
|
233
|
+
|-----|-------------|
|
|
234
|
+
| `lookupDomain` | Base URL for lookup adapter. |
|
|
235
|
+
| `lookupName` | Resource → `{domain}/lookup/{name}`. |
|
|
236
|
+
| `lookupFilter` | Client-side filter on loaded options. |
|
|
237
|
+
| `useParentId` | Pass `dependsOn` value as `parentId`; reloads when parent changes. |
|
|
238
|
+
| `searchLookup` | Searchable select; remote search via `DGA_LOOKUP_ADAPTER`. |
|
|
239
|
+
| `searchParamName` | Query param (default `q`). |
|
|
240
|
+
| `minSearchLength` | Min chars before search (default `3` when `searchLookup`). |
|
|
184
241
|
|
|
185
|
-
|
|
186
|
-
requiredWhen: {
|
|
187
|
-
field: 'topic',
|
|
188
|
-
value?: 'other', // single match
|
|
189
|
-
values?: ['a', 'b'], // any-of match
|
|
190
|
-
optionProperty?: 'requiresTextInput',
|
|
191
|
-
optionPropertyValue?: true,
|
|
192
|
-
}
|
|
193
|
-
```
|
|
242
|
+
HTTP adapter maps `Id/Title`, `Code/Name`, and `{UserName,Email,DisplayName}` shapes.
|
|
194
243
|
|
|
195
|
-
###
|
|
244
|
+
### Files
|
|
196
245
|
|
|
197
|
-
| Key |
|
|
198
|
-
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
|
|
246
|
+
| Key | Types | Description |
|
|
247
|
+
|-----|-------|-------------|
|
|
248
|
+
| `acceptedFileTypes` | file, multifile, multifilestaged | e.g. `.pdf,.png`. |
|
|
249
|
+
| `maxFileSize` | all file types | Max bytes per file. |
|
|
250
|
+
| `maxFiles` | all file types | Max file count. |
|
|
251
|
+
| `uploadEndpoint` | multifilestaged | POST staging URL → `{ fileId, fileName? }`. |
|
|
252
|
+
| `deleteEndpoint` | multifilestaged | DELETE/POST removal URL (`:id` / `:fileId`). |
|
|
253
|
+
| `deleteMethod` | multifilestaged | `'DELETE' \| 'POST'`. |
|
|
254
|
+
|
|
255
|
+
`multifilestaged` value: `DgaStagedFileRef[]`. Edit mode: `DgaExistingAttachment[]` on file fields.
|
|
206
256
|
|
|
207
257
|
### Type-specific
|
|
208
258
|
|
|
209
259
|
| Key | Types | Description |
|
|
210
260
|
|-----|-------|-------------|
|
|
211
|
-
| `minDate` / `maxDate` |
|
|
212
|
-
| `monthPicker` |
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
261
|
+
| `minDate` / `maxDate` | date | ISO bounds on picker. |
|
|
262
|
+
| `monthPicker` | date | Month-only mode. |
|
|
263
|
+
| `maxChips` | chips | Max chip count validator. |
|
|
264
|
+
| `allowDuplicates` | chips | Allow duplicate chip values. |
|
|
265
|
+
| `alwaysOpen` | select | `openMode="always"` on `dga-select`. |
|
|
266
|
+
| `consentServiceName` | checkbox | Fetch label from CMS (`consentBaseUrl`). |
|
|
267
|
+
| `excludeFromDraft` / `sensitive` | any | Strip from localStorage drafts. |
|
|
218
268
|
|
|
219
269
|
### Validation (`field.validation`)
|
|
220
270
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
| `required` | Same as `field.required`. |
|
|
227
|
-
| `errorMessages.*` | Bilingual messages for `required`, `pattern`, `minLength`, `maxLength`, `email`, `min`, `max`, `minDate`, `maxDate`, `maxChips`, `invalidFormat`. |
|
|
271
|
+
Supports `pattern`, `minLength`, `maxLength`, `min`, `max`, `minDate`, `maxDate`, `required`.
|
|
272
|
+
|
|
273
|
+
**Error message keys:** `required`, `pattern`, `minLength`, `maxLength`, `email`, `min`, `max`, `minDate`, `maxDate`, `maxChips`, `invalidFormat`, `invalidPrefix`, `invalidLength`, `passwordMismatch`, `endDateAfterStartDate`, `uploadInProgress`, `maxFiles`.
|
|
274
|
+
|
|
275
|
+
**Auto cross-field rules:** `password` + `confirmPassword` → match validator; known date pairs (`StartDate`/`EndDate`, `UseCaseStartDate`/`UseCaseEndDate`, `startDate`/`endDate`) → end-after-start.
|
|
228
276
|
|
|
229
277
|
---
|
|
230
278
|
|
|
231
279
|
## Component API
|
|
232
280
|
|
|
233
|
-
| Member |
|
|
234
|
-
|
|
235
|
-
| `config` |
|
|
236
|
-
| `locale` |
|
|
237
|
-
| `formSubmitted` |
|
|
238
|
-
| `formError` |
|
|
239
|
-
| `clearButtonClick` |
|
|
240
|
-
| `draftSaved` |
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
281
|
+
| Member | Description |
|
|
282
|
+
|--------|-------------|
|
|
283
|
+
| `[config]` | `DgaDynamicFormConfig` (required). |
|
|
284
|
+
| `[locale]` | `'ar' \| 'en'` (default from `<html lang>`). |
|
|
285
|
+
| `(formSubmitted)` | HTTP response or payload when `emitOnly` / no endpoint. |
|
|
286
|
+
| `(formError)` | Submit failure. |
|
|
287
|
+
| `(clearButtonClick)` | After Clear. |
|
|
288
|
+
| `(draftSaved)` | After localStorage save. |
|
|
289
|
+
| `(fileDownload)` | Existing attachment download (`DgaExistingAttachment`). |
|
|
290
|
+
| `patchFormValues(values)` | Patch controls; config `value` changes also patch at runtime. |
|
|
291
|
+
| `getForm()` | Returns `FormGroup`. |
|
|
243
292
|
|
|
244
293
|
---
|
|
245
294
|
|
|
@@ -248,31 +297,17 @@ requiredWhen: {
|
|
|
248
297
|
| Token | Role |
|
|
249
298
|
|-------|------|
|
|
250
299
|
| `DGA_SUBMIT_ADAPTER` | `submit({ endpoint, method, body, headers })` |
|
|
251
|
-
| `DGA_LOOKUP_ADAPTER` | `lookup({ domain, name, parentId?, search? })` |
|
|
300
|
+
| `DGA_LOOKUP_ADAPTER` | `lookup({ domain, name, parentId?, search?, searchParamName? })` |
|
|
252
301
|
| `DGA_FORM_TOAST_ADAPTER` | `show({ title?, message?, variant? })` |
|
|
253
302
|
| `DGA_CAPTCHA_ADAPTER` | `getToken(action?) → Observable<string \| null>` |
|
|
254
303
|
| `DGA_FORM_I18N_ADAPTER` | `resolveLabel(label, locale)` |
|
|
304
|
+
| `DGA_HTTP_SECURITY` | `{ allowedOrigins? }` for schema-driven URLs |
|
|
255
305
|
| `DGA_DYNAMIC_FORM_FIELD_REGISTRY` | `{ type, component }[]` custom field renderers |
|
|
256
306
|
|
|
257
|
-
|
|
258
|
-
provideDgaDynamicForm({
|
|
259
|
-
httpSubmit: true,
|
|
260
|
-
httpLookup: true,
|
|
261
|
-
dgaToast: true,
|
|
262
|
-
});
|
|
263
|
-
```
|
|
307
|
+
### Exported utilities
|
|
264
308
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
```ts
|
|
268
|
-
{
|
|
269
|
-
provide: DGA_DYNAMIC_FORM_FIELD_REGISTRY,
|
|
270
|
-
useValue: [{ type: 'nationalId', component: NationalIdField }],
|
|
271
|
-
}
|
|
272
|
-
```
|
|
309
|
+
`dgaBuildFormGroup`, `dgaIsFieldVisible`, `dgaIsRequiredWhen`, `dgaApplyCrossFieldValidators`, `dgaSaudiMobileValidator`, `dgaPasswordMatchValidator`, `dgaAssertSafeHttpUrl`, `createHttpLookupAdapter`, `createHttpSubmitAdapter`.
|
|
273
310
|
|
|
274
311
|
## License
|
|
275
312
|
|
|
276
313
|
MIT — see [LICENSE](./LICENSE). Copyright (c) 2026 Ahmed Nemreen.
|
|
277
|
-
|
|
278
|
-
Use it in any project. **Do not rebrand and sell it as your own unique product.**
|