@ngx-formbar/core 1.0.0 → 2.0.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -34
- package/fesm2022/ngx-formbar-core.mjs +225 -1658
- package/fesm2022/ngx-formbar-core.mjs.map +1 -1
- package/package.json +5 -12
- package/types/ngx-formbar-core.d.ts +525 -0
- package/LICENSE +0 -21
- package/index.d.ts +0 -5
- package/lib/components/form/ngxfb-form.component.d.ts +0 -28
- package/lib/composables/computed-value.d.ts +0 -8
- package/lib/composables/disabled.state.d.ts +0 -36
- package/lib/composables/dynamic-label.d.ts +0 -9
- package/lib/composables/dynamic-title.d.ts +0 -9
- package/lib/composables/hidden.state.d.ts +0 -68
- package/lib/composables/readonly.state.d.ts +0 -19
- package/lib/composables/testId.d.ts +0 -16
- package/lib/composables/update-strategy.d.ts +0 -20
- package/lib/composables/validators.d.ts +0 -22
- package/lib/config/config.d.ts +0 -7
- package/lib/config/provide-formbar.d.ts +0 -38
- package/lib/directives/ngxfb-abstract-control.directive.d.ts +0 -53
- package/lib/directives/ngxfb-block.directive.d.ts +0 -124
- package/lib/directives/ngxfb-control.directive.d.ts +0 -203
- package/lib/directives/ngxfb-group.directive.d.ts +0 -253
- package/lib/helper/control-container-view-providers.d.ts +0 -33
- package/lib/index.d.ts +0 -23
- package/lib/services/component-registration.service.d.ts +0 -8
- package/lib/services/configuration.service.d.ts +0 -8
- package/lib/services/expression.service.d.ts +0 -148
- package/lib/services/form.service.d.ts +0 -10
- package/lib/services/validator-registration.service.d.ts +0 -10
- package/lib/tokens/component-registrations.d.ts +0 -2
- package/lib/tokens/component-resolver.d.ts +0 -3
- package/lib/tokens/default-update-strategy.d.ts +0 -3
- package/lib/tokens/global-config.d.ts +0 -5
- package/lib/tokens/validator-registrations.d.ts +0 -8
- package/lib/tokens/validator-resolver.d.ts +0 -3
- package/lib/types/component-resolver.type.d.ts +0 -4
- package/lib/types/content.type.d.ts +0 -137
- package/lib/types/expression.type.d.ts +0 -2
- package/lib/types/form.type.d.ts +0 -4
- package/lib/types/functions.type.d.ts +0 -4
- package/lib/types/global-configuration.type.d.ts +0 -4
- package/lib/types/provide.type.d.ts +0 -42
- package/lib/types/registration.type.d.ts +0 -18
- package/lib/types/validation.type.d.ts +0 -59
- package/lib/types/validator-resolver.type.d.ts +0 -6
- package/public-api.d.ts +0 -1
- package/schematics/block/files/__componentName@dasherize__.component.html.template +0 -1
- package/schematics/block/files/__componentName@dasherize__.component.ts.template +0 -29
- package/schematics/block/files/__interfaceName@dasherize__.type.ts.template +0 -6
- package/schematics/block/index.d.ts +0 -3
- package/schematics/block/index.js +0 -11
- package/schematics/block/index.js.map +0 -1
- package/schematics/block/schema.json +0 -62
- package/schematics/collection.json +0 -31
- package/schematics/control/files/__componentName@dasherize__.component.html.template +0 -0
- package/schematics/control/files/__componentName@dasherize__.component.ts.template +0 -29
- package/schematics/control/files/__interfaceName@dasherize__.type.ts.template +0 -6
- package/schematics/control/index.d.ts +0 -3
- package/schematics/control/index.js +0 -11
- package/schematics/control/index.js.map +0 -1
- package/schematics/control/schema.json +0 -61
- package/schematics/group/files/__componentName@dasherize__.component.html.template +0 -5
- package/schematics/group/files/__componentName@dasherize__.component.ts.template +0 -29
- package/schematics/group/files/__interfaceName@dasherize__.type.ts.template +0 -5
- package/schematics/group/index.d.ts +0 -3
- package/schematics/group/index.js +0 -11
- package/schematics/group/index.js.map +0 -1
- package/schematics/group/schema.json +0 -62
- package/schematics/ng-add/files/config-registrations/async-validator-registrations.ts.template +0 -4
- package/schematics/ng-add/files/config-registrations/component-registrations.ts.template +0 -4
- package/schematics/ng-add/files/config-registrations/index.ts.template +0 -3
- package/schematics/ng-add/files/config-registrations/validator-registrations.ts.template +0 -4
- package/schematics/ng-add/files/helper/block.host-directive.ts.template +0 -6
- package/schematics/ng-add/files/helper/control.host-directive.ts.template +0 -6
- package/schematics/ng-add/files/helper/group.host-directive.ts.template +0 -6
- package/schematics/ng-add/files/helper/index.ts.template +0 -4
- package/schematics/ng-add/files/helper/view-provider.ts.template +0 -9
- package/schematics/ng-add/files/provider-config/config/__providerConfigFileName__.ts.template +0 -9
- package/schematics/ng-add/files/provider-config/inline/__providerConfigFileName__.ts.template +0 -8
- package/schematics/ng-add/files/provider-config/token/__providerConfigFileName__.ts.template +0 -4
- package/schematics/ng-add/files/schematics-config/__schematicConfigFileName__.json.template +0 -1
- package/schematics/ng-add/files/token-registrations/async-validator-registrations.ts.template +0 -8
- package/schematics/ng-add/files/token-registrations/component-registrations.ts.template +0 -8
- package/schematics/ng-add/files/token-registrations/index.ts.template +0 -3
- package/schematics/ng-add/files/token-registrations/validator-registrations.ts.template +0 -8
- package/schematics/ng-add/helper.d.ts +0 -11
- package/schematics/ng-add/helper.js +0 -198
- package/schematics/ng-add/helper.js.map +0 -1
- package/schematics/ng-add/index.d.ts +0 -3
- package/schematics/ng-add/index.js +0 -68
- package/schematics/ng-add/index.js.map +0 -1
- package/schematics/ng-add/rules/create-config-registration-files.rule.d.ts +0 -3
- package/schematics/ng-add/rules/create-config-registration-files.rule.js +0 -32
- package/schematics/ng-add/rules/create-config-registration-files.rule.js.map +0 -1
- package/schematics/ng-add/rules/create-formbar-registration-config.rule.d.ts +0 -3
- package/schematics/ng-add/rules/create-formbar-registration-config.rule.js +0 -30
- package/schematics/ng-add/rules/create-formbar-registration-config.rule.js.map +0 -1
- package/schematics/ng-add/rules/create-helper-files.rule.d.ts +0 -6
- package/schematics/ng-add/rules/create-helper-files.rule.js +0 -22
- package/schematics/ng-add/rules/create-helper-files.rule.js.map +0 -1
- package/schematics/ng-add/rules/create-schematics-config.rule.d.ts +0 -3
- package/schematics/ng-add/rules/create-schematics-config.rule.js +0 -42
- package/schematics/ng-add/rules/create-schematics-config.rule.js.map +0 -1
- package/schematics/ng-add/rules/create-token-registration-files.rule.d.ts +0 -3
- package/schematics/ng-add/rules/create-token-registration-files.rule.js +0 -32
- package/schematics/ng-add/rules/create-token-registration-files.rule.js.map +0 -1
- package/schematics/ng-add/rules/include-templates.rule.d.ts +0 -3
- package/schematics/ng-add/rules/include-templates.rule.js +0 -11
- package/schematics/ng-add/rules/include-templates.rule.js.map +0 -1
- package/schematics/ng-add/rules/install-dependencies.rule.d.ts +0 -2
- package/schematics/ng-add/rules/install-dependencies.rule.js +0 -12
- package/schematics/ng-add/rules/install-dependencies.rule.js.map +0 -1
- package/schematics/ng-add/rules/update-app-config.rule.d.ts +0 -3
- package/schematics/ng-add/rules/update-app-config.rule.js +0 -49
- package/schematics/ng-add/rules/update-app-config.rule.js.map +0 -1
- package/schematics/ng-add/rules/update-schematics-config.rule.d.ts +0 -6
- package/schematics/ng-add/rules/update-schematics-config.rule.js +0 -28
- package/schematics/ng-add/rules/update-schematics-config.rule.js.map +0 -1
- package/schematics/ng-add/schema.d.ts +0 -23
- package/schematics/ng-add/schema.js +0 -3
- package/schematics/ng-add/schema.js.map +0 -1
- package/schematics/ng-add/schema.json +0 -81
- package/schematics/register/component-info.type.d.ts +0 -11
- package/schematics/register/component-info.type.js +0 -3
- package/schematics/register/component-info.type.js.map +0 -1
- package/schematics/register/discover-components.d.ts +0 -19
- package/schematics/register/discover-components.js +0 -267
- package/schematics/register/discover-components.js.map +0 -1
- package/schematics/register/index.d.ts +0 -3
- package/schematics/register/index.js +0 -49
- package/schematics/register/index.js.map +0 -1
- package/schematics/register/register-components.d.ts +0 -3
- package/schematics/register/register-components.js +0 -38
- package/schematics/register/register-components.js.map +0 -1
- package/schematics/register/schema.d.ts +0 -14
- package/schematics/register/schema.js +0 -3
- package/schematics/register/schema.js.map +0 -1
- package/schematics/register/schema.json +0 -44
- package/schematics/shared/ast/decorators.d.ts +0 -9
- package/schematics/shared/ast/decorators.js +0 -182
- package/schematics/shared/ast/decorators.js.map +0 -1
- package/schematics/shared/ast/imports.d.ts +0 -3
- package/schematics/shared/ast/imports.js +0 -93
- package/schematics/shared/ast/imports.js.map +0 -1
- package/schematics/shared/ast/parse.d.ts +0 -3
- package/schematics/shared/ast/parse.js +0 -17
- package/schematics/shared/ast/parse.js.map +0 -1
- package/schematics/shared/ast/registrations.d.ts +0 -22
- package/schematics/shared/ast/registrations.js +0 -654
- package/schematics/shared/ast/registrations.js.map +0 -1
- package/schematics/shared/ast/types.d.ts +0 -3
- package/schematics/shared/ast/types.js +0 -58
- package/schematics/shared/ast/types.js.map +0 -1
- package/schematics/shared/file.d.ts +0 -4
- package/schematics/shared/file.js +0 -60
- package/schematics/shared/file.js.map +0 -1
- package/schematics/shared/helper.d.ts +0 -2
- package/schematics/shared/helper.js +0 -29
- package/schematics/shared/helper.js.map +0 -1
- package/schematics/shared/rules/create-component.rule.d.ts +0 -3
- package/schematics/shared/rules/create-component.rule.js +0 -15
- package/schematics/shared/rules/create-component.rule.js.map +0 -1
- package/schematics/shared/rules/register-control.rule.d.ts +0 -3
- package/schematics/shared/rules/register-control.rule.js +0 -30
- package/schematics/shared/rules/register-control.rule.js.map +0 -1
- package/schematics/shared/rules/register-type-map.rule.d.ts +0 -3
- package/schematics/shared/rules/register-type-map.rule.js +0 -46
- package/schematics/shared/rules/register-type-map.rule.js.map +0 -1
- package/schematics/shared/rules/register-type-token.rule.d.ts +0 -3
- package/schematics/shared/rules/register-type-token.rule.js +0 -49
- package/schematics/shared/rules/register-type-token.rule.js.map +0 -1
- package/schematics/shared/rules/scaffold-and-register.rule.d.ts +0 -3
- package/schematics/shared/rules/scaffold-and-register.rule.js +0 -134
- package/schematics/shared/rules/scaffold-and-register.rule.js.map +0 -1
- package/schematics/shared/schema.d.ts +0 -32
- package/schematics/shared/schema.js +0 -3
- package/schematics/shared/schema.js.map +0 -1
- package/schematics/tests/generators.spec.d.ts +0 -1
- package/schematics/tests/generators.spec.js +0 -450
- package/schematics/tests/generators.spec.js.map +0 -1
- package/schematics/tests/helper.d.ts +0 -20
- package/schematics/tests/helper.js +0 -275
- package/schematics/tests/helper.js.map +0 -1
- package/schematics/tests/ng-add.spec.d.ts +0 -1
- package/schematics/tests/ng-add.spec.js +0 -380
- package/schematics/tests/ng-add.spec.js.map +0 -1
- package/schematics/tests/register.spec.d.ts +0 -1
- package/schematics/tests/register.spec.js +0 -340
- package/schematics/tests/register.spec.js.map +0 -1
- package/schematics/tests/workspace-setup.d.ts +0 -21
- package/schematics/tests/workspace-setup.js +0 -256
- package/schematics/tests/workspace-setup.js.map +0 -1
- package/shared/ast.d.ts +0 -10
- package/shared/ast.js +0 -93
- package/shared/ast.js.map +0 -1
- package/shared/constants.d.ts +0 -16
- package/shared/constants.js +0 -20
- package/shared/constants.js.map +0 -1
- package/shared/shared-config.type.d.ts +0 -20
- package/shared/shared-config.type.js +0 -3
- package/shared/shared-config.type.js.map +0 -1
|
@@ -1,244 +1,56 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, Injectable,
|
|
3
|
-
import { ControlContainer, Validators, FormGroup, FormControl } from '@angular/forms';
|
|
4
|
-
import { toSignal } from '@angular/core/rxjs-interop';
|
|
2
|
+
import { InjectionToken, inject, Injectable, signal, computed, effect, untracked } from '@angular/core';
|
|
5
3
|
import * as acorn from 'acorn';
|
|
6
4
|
|
|
7
|
-
class FormService {
|
|
8
|
-
controlContainer = inject(ControlContainer);
|
|
9
|
-
formGroup = this.controlContainer.control;
|
|
10
|
-
formValue = toSignal(this.formGroup.valueChanges);
|
|
11
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FormService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
12
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FormService });
|
|
13
|
-
}
|
|
14
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FormService, decorators: [{
|
|
15
|
-
type: Injectable
|
|
16
|
-
}] });
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Provides the parent ControlContainer to child components
|
|
20
|
-
*
|
|
21
|
-
* This provider configuration allows child form components to access their parent's
|
|
22
|
-
* ControlContainer through dependency injection. This is particularly useful for
|
|
23
|
-
* creating nested form components that inherit the parent form context without
|
|
24
|
-
* explicitly passing FormGroup instances.
|
|
25
|
-
*
|
|
26
|
-
* @remarks
|
|
27
|
-
* The `skipSelf` option ensures the provider looks for the ControlContainer in parent
|
|
28
|
-
* components rather than trying to resolve it from the component where this is used.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```typescript
|
|
32
|
-
* @Component({
|
|
33
|
-
* selector: 'app-child-form',
|
|
34
|
-
* template: '...',
|
|
35
|
-
* viewProviders: [controlContainerViewProviders]
|
|
36
|
-
* })
|
|
37
|
-
* export class ChildFormComponent {
|
|
38
|
-
* // Now this component can access the parent form
|
|
39
|
-
* private controlContainer = inject(ControlContainer);
|
|
40
|
-
*
|
|
41
|
-
* get parentFormGroup() {
|
|
42
|
-
* return this.parentContainer.control as FormGroup | null;
|
|
43
|
-
* }
|
|
44
|
-
* }
|
|
45
|
-
*/
|
|
46
|
-
const controlContainerViewProviders = [
|
|
47
|
-
{
|
|
48
|
-
provide: ControlContainer,
|
|
49
|
-
useFactory: () => inject(ControlContainer, { skipSelf: true }),
|
|
50
|
-
},
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
const NGX_FW_COMPONENT_RESOLVER = new InjectionToken('NGX_FW_COMPONENT_RESOLVER');
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Structural directive that renders the appropriate component based on the control's type.
|
|
57
|
-
*
|
|
58
|
-
* This directive acts as a dynamic renderer for form controls, blocks, and groups.
|
|
59
|
-
* It works by:
|
|
60
|
-
* 1. Receiving a content configuration and name
|
|
61
|
-
* 2. Looking up the registered component for the content's type
|
|
62
|
-
* 3. Creating an instance of that component and binding the content and name to it
|
|
63
|
-
*
|
|
64
|
-
* This allows forms to be composed declaratively through configuration objects
|
|
65
|
-
* rather than explicit templates.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```html
|
|
69
|
-
* <!-- Used with ngFor to render a list of controls -->
|
|
70
|
-
* @for (control of controls(); track control[0]) {
|
|
71
|
-
* <ng-template *ngxfbAbstractControl="control" />
|
|
72
|
-
* }
|
|
73
|
-
*
|
|
74
|
-
* <!-- Used directly with a specific control -->
|
|
75
|
-
* <ng-template *ngxfbAbstractControl="['name', nameControlConfig]" />
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
class NgxfbAbstractControlDirective {
|
|
79
|
-
viewContainerRef = inject(ViewContainerRef);
|
|
80
|
-
/**
|
|
81
|
-
* Service for component registration
|
|
82
|
-
* Provides access to component type mappings
|
|
83
|
-
*/
|
|
84
|
-
contentRegistrationService = inject(NGX_FW_COMPONENT_RESOLVER);
|
|
85
|
-
/**
|
|
86
|
-
* Required input for control configuration
|
|
87
|
-
* Defines properties like type, validation, and other control-specific settings
|
|
88
|
-
*/
|
|
89
|
-
content = input.required({
|
|
90
|
-
alias: 'ngxfbAbstractControl',
|
|
91
|
-
});
|
|
92
|
-
controlName = computed(() => this.content()[0]);
|
|
93
|
-
controlConfig = computed(() => this.content()[1]);
|
|
94
|
-
/**
|
|
95
|
-
* Registration map of component types
|
|
96
|
-
* Maps control types to component implementations
|
|
97
|
-
*/
|
|
98
|
-
registrations = this.contentRegistrationService.registrations;
|
|
99
|
-
/**
|
|
100
|
-
* Computed component type based on content.type
|
|
101
|
-
* Looks up the component implementation from registrations map
|
|
102
|
-
*/
|
|
103
|
-
component = computed(() => {
|
|
104
|
-
const registrations = this.registrations();
|
|
105
|
-
const content = this.controlConfig();
|
|
106
|
-
const component = registrations.get(content.type);
|
|
107
|
-
return component ?? null;
|
|
108
|
-
});
|
|
109
|
-
constructor() {
|
|
110
|
-
effect(() => {
|
|
111
|
-
const component = this.component();
|
|
112
|
-
this.viewContainerRef.clear();
|
|
113
|
-
if (component) {
|
|
114
|
-
const componentRef = this.viewContainerRef.createComponent(component);
|
|
115
|
-
componentRef.setInput('content', this.controlConfig());
|
|
116
|
-
componentRef.setInput('name', this.controlName());
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbAbstractControlDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
121
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.15", type: NgxfbAbstractControlDirective, isStandalone: true, selector: "[ngxfbAbstractControl]", inputs: { content: { classPropertyName: "content", publicName: "ngxfbAbstractControl", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
|
|
122
|
-
}
|
|
123
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbAbstractControlDirective, decorators: [{
|
|
124
|
-
type: Directive,
|
|
125
|
-
args: [{
|
|
126
|
-
selector: '[ngxfbAbstractControl]',
|
|
127
|
-
}]
|
|
128
|
-
}], ctorParameters: () => [] });
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Ngx Formbar Form Component
|
|
132
|
-
*
|
|
133
|
-
* This component serves as the main container for Ngx Formbar forms:
|
|
134
|
-
* - Takes a form configuration
|
|
135
|
-
* - Establishes the form context through FormService provider
|
|
136
|
-
* - Renders each content item using NgxfbAbstractControlDirective
|
|
137
|
-
* - Handles component registration and dependency injection
|
|
138
|
-
*
|
|
139
|
-
* The component acts as the root element for declarative form creation,
|
|
140
|
-
* processing the form content configuration and rendering the appropriate
|
|
141
|
-
* components for each control defined in the configuration.
|
|
142
|
-
*/
|
|
143
|
-
class NgxfbFormComponent {
|
|
144
|
-
/**
|
|
145
|
-
* Required input containing form configuration
|
|
146
|
-
*/
|
|
147
|
-
formConfig = input.required();
|
|
148
|
-
/**
|
|
149
|
-
* Computed value containing form content
|
|
150
|
-
*/
|
|
151
|
-
formContent = computed(() => Object.entries(this.formConfig().content));
|
|
152
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
153
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: NgxfbFormComponent, isStandalone: true, selector: "ngxfb-form", inputs: { formConfig: { classPropertyName: "formConfig", publicName: "formConfig", isSignal: true, isRequired: true, transformFunction: null } }, providers: [FormService], ngImport: i0, template: "@for (content of formContent(); track content[0]) {\n <ng-template *ngxfbAbstractControl=\"content\" />\n}\n", dependencies: [{ kind: "directive", type: NgxfbAbstractControlDirective, selector: "[ngxfbAbstractControl]", inputs: ["ngxfbAbstractControl"] }], viewProviders: [controlContainerViewProviders] });
|
|
154
|
-
}
|
|
155
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbFormComponent, decorators: [{
|
|
156
|
-
type: Component,
|
|
157
|
-
args: [{ selector: 'ngxfb-form', imports: [NgxfbAbstractControlDirective], providers: [FormService], viewProviders: [controlContainerViewProviders], template: "@for (content of formContent(); track content[0]) {\n <ng-template *ngxfbAbstractControl=\"content\" />\n}\n" }]
|
|
158
|
-
}] });
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Type helper to make it easier to use formbar.config.ts
|
|
162
|
-
* accepts a direct {@link FormbarConfig} object
|
|
163
|
-
*/
|
|
164
|
-
function defineFormbarConfig(config) {
|
|
165
|
-
return config;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
5
|
const NGX_FW_COMPONENT_REGISTRATIONS = new InjectionToken('NGX_FW_COMPONENT_REGISTRATIONS', {
|
|
169
6
|
providedIn: 'root',
|
|
170
7
|
factory: () => new Map(),
|
|
171
8
|
});
|
|
172
9
|
|
|
173
|
-
|
|
174
|
-
_registrations = signal(inject(NGX_FW_COMPONENT_REGISTRATIONS));
|
|
175
|
-
registrations = this._registrations.asReadonly();
|
|
176
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ComponentRegistrationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
177
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ComponentRegistrationService, providedIn: 'root' });
|
|
178
|
-
}
|
|
179
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ComponentRegistrationService, decorators: [{
|
|
180
|
-
type: Injectable,
|
|
181
|
-
args: [{
|
|
182
|
-
providedIn: 'root',
|
|
183
|
-
}]
|
|
184
|
-
}] });
|
|
10
|
+
const NGX_FW_COMPONENT_RESOLVER = new InjectionToken('NGX_FW_COMPONENT_RESOLVER');
|
|
185
11
|
|
|
186
|
-
const
|
|
187
|
-
providedIn: 'root',
|
|
188
|
-
factory: () => new Map([
|
|
189
|
-
['required', [Validators.required]],
|
|
190
|
-
['requiredTrue', [Validators.requiredTrue]],
|
|
191
|
-
['email', [Validators.email]],
|
|
192
|
-
['nullValidator', [Validators.nullValidator]],
|
|
193
|
-
]),
|
|
194
|
-
});
|
|
195
|
-
const NGX_FW_DEFAULT_ASYNC_VALIDATOR_REGISTRATIONS = new InjectionToken('NGX_FW_DEFAULT_ASYNC_VALIDATOR_REGISTRATIONS', {
|
|
12
|
+
const NGX_FW_DEFAULT_CONFIG = new InjectionToken('NGX_FW_DEFAULT_CONFIG', {
|
|
196
13
|
providedIn: 'root',
|
|
197
|
-
factory: () =>
|
|
14
|
+
factory: () => ({}),
|
|
198
15
|
});
|
|
199
|
-
const
|
|
200
|
-
const NGX_FW_ASYNC_VALIDATOR_REGISTRATIONS = new InjectionToken('NGX_FW_ASYNC_VALIDATOR_REGISTRATIONS');
|
|
201
|
-
const NGX_FW_VALIDATOR_REGISTRATIONS_RESOLVED = new InjectionToken('NGX_FW_VALIDATOR_REGISTRATIONS_RESOLVED', {
|
|
16
|
+
const NGX_FW_CONFIG = new InjectionToken('NGX_FW_CONFIG', {
|
|
202
17
|
providedIn: 'root',
|
|
203
|
-
factory: () =>
|
|
204
|
-
const base = inject(NGX_FW_DEFAULT_VALIDATOR_REGISTRATIONS);
|
|
205
|
-
const extras = inject(NGX_FW_VALIDATOR_REGISTRATIONS, { optional: true }) ?? [];
|
|
206
|
-
return mergeMapsLastWins(base, ...extras);
|
|
207
|
-
},
|
|
18
|
+
factory: () => [],
|
|
208
19
|
});
|
|
209
|
-
const
|
|
20
|
+
const NGX_FW_CONFIG_RESOLVED = new InjectionToken('NGX_FW_CONFIG_RESOLVED', {
|
|
210
21
|
providedIn: 'root',
|
|
211
22
|
factory: () => {
|
|
212
|
-
const base = inject(
|
|
213
|
-
const extras = inject(
|
|
214
|
-
return
|
|
23
|
+
const base = inject(NGX_FW_DEFAULT_CONFIG);
|
|
24
|
+
const extras = inject(NGX_FW_CONFIG, { optional: true }) ?? [];
|
|
25
|
+
return mergeDeep(base, ...extras);
|
|
215
26
|
},
|
|
216
27
|
});
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
out
|
|
28
|
+
function mergeDeep(base, ...partials) {
|
|
29
|
+
const out = { ...base };
|
|
30
|
+
for (const p of partials) {
|
|
31
|
+
for (const k of Object.keys(p)) {
|
|
32
|
+
const src = p[k];
|
|
33
|
+
const dst = out[k];
|
|
34
|
+
const bothObjects = typeof dst === 'object' &&
|
|
35
|
+
dst !== null &&
|
|
36
|
+
typeof src === 'object' &&
|
|
37
|
+
src !== null &&
|
|
38
|
+
!Array.isArray(dst) &&
|
|
39
|
+
!Array.isArray(src);
|
|
40
|
+
if (bothObjects) {
|
|
41
|
+
out[k] = mergeDeep(dst, src);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
out[k] = src;
|
|
223
45
|
}
|
|
224
46
|
}
|
|
225
47
|
return out;
|
|
226
48
|
}
|
|
227
49
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
asyncRegistrations = this._asyncRegistrations.asReadonly();
|
|
233
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ValidatorRegistrationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
234
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ValidatorRegistrationService, providedIn: 'root' });
|
|
235
|
-
}
|
|
236
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ValidatorRegistrationService, decorators: [{
|
|
237
|
-
type: Injectable,
|
|
238
|
-
args: [{
|
|
239
|
-
providedIn: 'root',
|
|
240
|
-
}]
|
|
241
|
-
}] });
|
|
50
|
+
const NGX_FW_DEFAULT_UPDATE_STRATEGY = new InjectionToken('NGX_FW_DEFAULT_UPDATE_STRATEGY', {
|
|
51
|
+
providedIn: 'root',
|
|
52
|
+
factory: () => 'change',
|
|
53
|
+
});
|
|
242
54
|
|
|
243
55
|
/**
|
|
244
56
|
* Set of node types that are not supported in expressions for security or complexity reasons
|
|
@@ -876,505 +688,249 @@ class ExpressionService {
|
|
|
876
688
|
throw new TypeError('Block-bodied arrow functions are not supported');
|
|
877
689
|
};
|
|
878
690
|
}
|
|
879
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
880
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
691
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ExpressionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
692
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ExpressionService, providedIn: 'root' });
|
|
881
693
|
}
|
|
882
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
694
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ExpressionService, decorators: [{
|
|
883
695
|
type: Injectable,
|
|
884
696
|
args: [{
|
|
885
697
|
providedIn: 'root',
|
|
886
698
|
}]
|
|
887
699
|
}] });
|
|
888
700
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
});
|
|
893
|
-
|
|
894
|
-
|
|
701
|
+
class ComponentRegistrationService {
|
|
702
|
+
_registrations = signal(inject(NGX_FW_COMPONENT_REGISTRATIONS), ...(ngDevMode ? [{ debugName: "_registrations" }] : []));
|
|
703
|
+
registrations = this._registrations.asReadonly();
|
|
704
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ComponentRegistrationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
705
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ComponentRegistrationService, providedIn: 'root' });
|
|
706
|
+
}
|
|
707
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ComponentRegistrationService, decorators: [{
|
|
708
|
+
type: Injectable,
|
|
709
|
+
args: [{
|
|
710
|
+
providedIn: 'root',
|
|
711
|
+
}]
|
|
712
|
+
}] });
|
|
895
713
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
const NGX_FW_CONFIG = new InjectionToken('NGX_FW_CONFIG', {
|
|
901
|
-
providedIn: 'root',
|
|
902
|
-
factory: () => [],
|
|
903
|
-
});
|
|
904
|
-
const NGX_FW_CONFIG_RESOLVED = new InjectionToken('NGX_FW_CONFIG_RESOLVED', {
|
|
905
|
-
providedIn: 'root',
|
|
906
|
-
factory: () => {
|
|
907
|
-
const base = inject(NGX_FW_DEFAULT_CONFIG);
|
|
908
|
-
const extras = inject(NGX_FW_CONFIG, { optional: true }) ?? [];
|
|
909
|
-
return mergeDeep(base, ...extras);
|
|
910
|
-
},
|
|
911
|
-
});
|
|
912
|
-
function mergeDeep(base, ...partials) {
|
|
913
|
-
const out = { ...base };
|
|
914
|
-
for (const p of partials) {
|
|
915
|
-
for (const k of Object.keys(p)) {
|
|
916
|
-
const src = p[k];
|
|
917
|
-
const dst = out[k];
|
|
918
|
-
const bothObjects = typeof dst === 'object' &&
|
|
919
|
-
dst !== null &&
|
|
920
|
-
typeof src === 'object' &&
|
|
921
|
-
src !== null &&
|
|
922
|
-
!Array.isArray(dst) &&
|
|
923
|
-
!Array.isArray(src);
|
|
924
|
-
if (bothObjects) {
|
|
925
|
-
out[k] = mergeDeep(dst, src);
|
|
926
|
-
continue;
|
|
927
|
-
}
|
|
928
|
-
out[k] = src;
|
|
929
|
-
}
|
|
714
|
+
class NgxFbConfigurationService {
|
|
715
|
+
_config = inject(NGX_FW_CONFIG_RESOLVED);
|
|
716
|
+
get testIdBuilder() {
|
|
717
|
+
return this._config.testIdBuilderFn;
|
|
930
718
|
}
|
|
931
|
-
|
|
719
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: NgxFbConfigurationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
720
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: NgxFbConfigurationService, providedIn: 'root' });
|
|
932
721
|
}
|
|
722
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: NgxFbConfigurationService, decorators: [{
|
|
723
|
+
type: Injectable,
|
|
724
|
+
args: [{
|
|
725
|
+
providedIn: 'root',
|
|
726
|
+
}]
|
|
727
|
+
}] });
|
|
933
728
|
|
|
934
729
|
/**
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
* This function is used in app.config.ts to register form components, validators,
|
|
938
|
-
* and set global configuration for the formbar library.
|
|
939
|
-
*
|
|
940
|
-
* @param config Configuration object for ngx-formbar:
|
|
941
|
-
* - componentRegistrations: Optional mapping of control types to component implementations
|
|
942
|
-
* - validatorRegistrations: Optional mapping of validator names to validator functions
|
|
943
|
-
* (Angular's required, requiredTrue, email, and nullValidator are registered by default)
|
|
944
|
-
* - asyncValidatorRegistrations: Optional mapping of async validator names to async validator functions
|
|
945
|
-
* - updateOn: Optional default update strategy for all form controls
|
|
946
|
-
* - globalConfig: Optional global configuration settings
|
|
947
|
-
*
|
|
948
|
-
* @returns Angular environment providers to be included in application configuration
|
|
730
|
+
* Creates a static component registration entry.
|
|
731
|
+
* Use for eagerly imported components that don't need lazy loading.
|
|
949
732
|
*
|
|
950
733
|
* @example
|
|
951
734
|
* ```ts
|
|
952
|
-
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
* text: TextInputComponent,
|
|
958
|
-
* select: SelectComponent,
|
|
959
|
-
* },
|
|
960
|
-
* validatorRegistrations: {
|
|
961
|
-
* customValidator: [myCustomValidator]
|
|
962
|
-
* }
|
|
963
|
-
* })
|
|
964
|
-
* ]
|
|
735
|
+
* import { staticComponent } from '@ngx-formbar/core';
|
|
736
|
+
* import { TextComponent } from './text.component';
|
|
737
|
+
*
|
|
738
|
+
* const registrations = {
|
|
739
|
+
* text: staticComponent(TextComponent),
|
|
965
740
|
* };
|
|
966
741
|
* ```
|
|
967
742
|
*/
|
|
968
|
-
function
|
|
969
|
-
|
|
970
|
-
const { componentRegistrations, validatorRegistrations, asyncValidatorRegistrations, updateOn, globalConfig, } = config;
|
|
971
|
-
const providers = [
|
|
972
|
-
{
|
|
973
|
-
provide: NGX_FW_COMPONENT_RESOLVER,
|
|
974
|
-
useClass: ComponentRegistrationService,
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
provide: NGX_VALIDATOR_RESOLVER,
|
|
978
|
-
useClass: ValidatorRegistrationService,
|
|
979
|
-
},
|
|
980
|
-
ExpressionService,
|
|
981
|
-
];
|
|
982
|
-
if (componentRegistrations !== undefined) {
|
|
983
|
-
providers.push({
|
|
984
|
-
provide: NGX_FW_COMPONENT_REGISTRATIONS,
|
|
985
|
-
useValue: toComponentRegistrationMap(componentRegistrations),
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
|
-
if (validatorRegistrations !== undefined) {
|
|
989
|
-
providers.push({
|
|
990
|
-
provide: NGX_FW_VALIDATOR_REGISTRATIONS,
|
|
991
|
-
useFactory: () => toValidatorRegistrationMap(validatorRegistrations),
|
|
992
|
-
multi: true,
|
|
993
|
-
});
|
|
994
|
-
}
|
|
995
|
-
if (asyncValidatorRegistrations !== undefined) {
|
|
996
|
-
providers.push({
|
|
997
|
-
provide: NGX_FW_ASYNC_VALIDATOR_REGISTRATIONS,
|
|
998
|
-
useFactory: () => toAsyncValidatorRegistrationMap(asyncValidatorRegistrations),
|
|
999
|
-
multi: true,
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
if (updateOn !== undefined) {
|
|
1003
|
-
providers.push({
|
|
1004
|
-
provide: NGX_FW_DEFAULT_UPDATE_STRATEGY,
|
|
1005
|
-
useValue: updateOn,
|
|
1006
|
-
});
|
|
1007
|
-
}
|
|
1008
|
-
if (globalConfig !== undefined) {
|
|
1009
|
-
providers.push({
|
|
1010
|
-
provide: NGX_FW_CONFIG,
|
|
1011
|
-
useValue: globalConfig,
|
|
1012
|
-
multi: true,
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
return makeEnvironmentProviders(providers);
|
|
1016
|
-
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Converts component registration array to a lookup map
|
|
1019
|
-
*
|
|
1020
|
-
* @param componentRegistrations Array of component registration configurations
|
|
1021
|
-
* @returns Map of component types to component implementations
|
|
1022
|
-
*/
|
|
1023
|
-
function toComponentRegistrationMap(componentRegistrations) {
|
|
1024
|
-
return new Map(Object.entries(componentRegistrations));
|
|
1025
|
-
}
|
|
1026
|
-
/**
|
|
1027
|
-
* Converts validator configuration to a map of validator functions
|
|
1028
|
-
* Resolves validator keys to actual validator functions
|
|
1029
|
-
*
|
|
1030
|
-
* @param config Configuration object for validators
|
|
1031
|
-
* @returns Map of validator keys to validator function arrays
|
|
1032
|
-
*/
|
|
1033
|
-
function toValidatorRegistrationMap(config) {
|
|
1034
|
-
return toValidatorMap(config);
|
|
743
|
+
function staticComponent(type) {
|
|
744
|
+
return { component: type };
|
|
1035
745
|
}
|
|
1036
746
|
/**
|
|
1037
|
-
*
|
|
747
|
+
* Creates a lazy component registration entry.
|
|
748
|
+
* Use for components that should be loaded on demand.
|
|
1038
749
|
*
|
|
1039
|
-
* @
|
|
1040
|
-
*
|
|
1041
|
-
|
|
1042
|
-
function toAsyncValidatorRegistrationMap(config) {
|
|
1043
|
-
return toValidatorMap(config);
|
|
1044
|
-
}
|
|
1045
|
-
/**
|
|
1046
|
-
* Resolves validator references to actual validator functions
|
|
1047
|
-
* Supports recursive validator references and memoization
|
|
750
|
+
* @example
|
|
751
|
+
* ```ts
|
|
752
|
+
* import { loadComponent } from '@ngx-formbar/core';
|
|
1048
753
|
*
|
|
1049
|
-
*
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
*
|
|
1053
|
-
* @returns Flattened array of validator functions
|
|
754
|
+
* const registrations = {
|
|
755
|
+
* text: loadComponent(() => import('./text.component').then(m => m.TextComponent)),
|
|
756
|
+
* };
|
|
757
|
+
* ```
|
|
1054
758
|
*/
|
|
1055
|
-
function
|
|
1056
|
-
return
|
|
1057
|
-
switch (typeof v) {
|
|
1058
|
-
case 'string': {
|
|
1059
|
-
const cached = memo.get(v);
|
|
1060
|
-
if (cached)
|
|
1061
|
-
return cached;
|
|
1062
|
-
if (visiting.has(v))
|
|
1063
|
-
throw new Error(`Cyclic validator reference: ${[...visiting, v].join(' -> ')}`);
|
|
1064
|
-
if (!Object.prototype.hasOwnProperty.call(registrations, v))
|
|
1065
|
-
throw new Error(`Unknown validator key: "${v}"`);
|
|
1066
|
-
const spec = registrations[v];
|
|
1067
|
-
visiting.add(v);
|
|
1068
|
-
const resolved = toValidatorFn(spec, registrations, memo, visiting);
|
|
1069
|
-
visiting.delete(v);
|
|
1070
|
-
memo.set(v, resolved);
|
|
1071
|
-
return resolved;
|
|
1072
|
-
}
|
|
1073
|
-
default:
|
|
1074
|
-
return v;
|
|
1075
|
-
}
|
|
1076
|
-
});
|
|
759
|
+
function loadComponent(load) {
|
|
760
|
+
return { loadComponent: load };
|
|
1077
761
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
762
|
+
|
|
763
|
+
function toSafeString(value) {
|
|
764
|
+
if (value === null) {
|
|
765
|
+
return 'null';
|
|
766
|
+
}
|
|
767
|
+
switch (typeof value) {
|
|
768
|
+
case 'undefined':
|
|
769
|
+
return 'undefined';
|
|
770
|
+
case 'string':
|
|
771
|
+
return value;
|
|
772
|
+
case 'boolean':
|
|
773
|
+
return value ? 'true' : 'false';
|
|
774
|
+
case 'number':
|
|
775
|
+
case 'symbol':
|
|
776
|
+
case 'bigint':
|
|
777
|
+
case 'function':
|
|
778
|
+
return value.toString();
|
|
779
|
+
default:
|
|
780
|
+
return JSON.stringify(value);
|
|
1094
781
|
}
|
|
1095
|
-
return out;
|
|
1096
782
|
}
|
|
1097
783
|
|
|
784
|
+
function isExpressionFn(value) {
|
|
785
|
+
return typeof value === 'function';
|
|
786
|
+
}
|
|
1098
787
|
/**
|
|
1099
|
-
*
|
|
788
|
+
* Resolves an expression option into a computed signal value
|
|
1100
789
|
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
1105
|
-
* 3. If no readonly property is defined, the control inherits the readonly state
|
|
1106
|
-
* from its parent group
|
|
790
|
+
* Handles three expression types:
|
|
791
|
+
* - **string**: Parsed to AST and evaluated against the form context
|
|
792
|
+
* - **function**: Called with the form context as argument
|
|
793
|
+
* - **static value / undefined**: Returned as-is
|
|
1107
794
|
*
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
795
|
+
* Internally caches the parsed AST in a separate computed signal
|
|
796
|
+
* so it is only recalculated when the option itself changes.
|
|
1110
797
|
*
|
|
1111
|
-
* @param
|
|
1112
|
-
* @
|
|
798
|
+
* @param option Signal containing the expression option (string, function, static value, or undefined)
|
|
799
|
+
* @param formContext Signal providing the current form context for expression evaluation
|
|
800
|
+
* @param expressionService Service used to parse and evaluate string expressions
|
|
801
|
+
* @returns Computed signal that resolves to the evaluated value or undefined
|
|
1113
802
|
*/
|
|
1114
|
-
function
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
optional: true,
|
|
1119
|
-
skipSelf: true,
|
|
1120
|
-
});
|
|
1121
|
-
const parentGroupIsReadonly = computed(() => {
|
|
1122
|
-
const parentGroup = parentGroupDirective;
|
|
1123
|
-
if (!parentGroup) {
|
|
1124
|
-
return false;
|
|
1125
|
-
}
|
|
1126
|
-
return parentGroup.readonly();
|
|
1127
|
-
});
|
|
1128
|
-
const readonlyAst = computed(() => {
|
|
1129
|
-
const readonlyOption = content().readonly;
|
|
1130
|
-
if (typeof readonlyOption === 'boolean' ||
|
|
1131
|
-
typeof readonlyOption === 'function') {
|
|
1132
|
-
return null;
|
|
1133
|
-
}
|
|
1134
|
-
return expressionService.parseExpressionToAst(readonlyOption);
|
|
1135
|
-
});
|
|
1136
|
-
const readonlyBool = computed(() => {
|
|
1137
|
-
const readonlyOption = content().readonly;
|
|
1138
|
-
if (typeof readonlyOption !== 'boolean') {
|
|
1139
|
-
return null;
|
|
1140
|
-
}
|
|
1141
|
-
return readonlyOption;
|
|
1142
|
-
});
|
|
1143
|
-
const readonlyFunction = computed(() => {
|
|
1144
|
-
const readonlyOption = content().readonly;
|
|
1145
|
-
if (typeof readonlyOption !== 'function') {
|
|
803
|
+
function resolveExpression(option, formContext, expressionService) {
|
|
804
|
+
const ast = computed(() => {
|
|
805
|
+
const value = option();
|
|
806
|
+
if (typeof value !== 'string') {
|
|
1146
807
|
return null;
|
|
1147
808
|
}
|
|
1148
|
-
return
|
|
1149
|
-
});
|
|
809
|
+
return expressionService.parseExpressionToAst(value);
|
|
810
|
+
}, ...(ngDevMode ? [{ debugName: "ast" }] : []));
|
|
1150
811
|
return computed(() => {
|
|
1151
|
-
const
|
|
1152
|
-
if (
|
|
1153
|
-
return
|
|
812
|
+
const value = option();
|
|
813
|
+
if (value === undefined) {
|
|
814
|
+
return undefined;
|
|
815
|
+
}
|
|
816
|
+
if (isExpressionFn(value)) {
|
|
817
|
+
return value(formContext());
|
|
1154
818
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
.value;
|
|
1158
|
-
const evaluationContext = reactiveFormValues ?? currentSynchronousFormValues;
|
|
1159
|
-
const readonlyFn = readonlyFunction();
|
|
1160
|
-
if (readonlyFn) {
|
|
1161
|
-
return readonlyFn(evaluationContext);
|
|
819
|
+
if (typeof value !== 'string') {
|
|
820
|
+
return value;
|
|
1162
821
|
}
|
|
1163
|
-
const
|
|
1164
|
-
if (!
|
|
1165
|
-
return
|
|
822
|
+
const parsedAst = ast();
|
|
823
|
+
if (!parsedAst) {
|
|
824
|
+
return undefined;
|
|
1166
825
|
}
|
|
1167
|
-
|
|
1168
|
-
return readonly;
|
|
826
|
+
return expressionService.evaluateExpression(parsedAst, formContext());
|
|
1169
827
|
});
|
|
1170
828
|
}
|
|
1171
829
|
|
|
1172
830
|
/**
|
|
1173
|
-
*
|
|
831
|
+
* Resolves an inheritable boolean expression with parent state fallback
|
|
1174
832
|
*
|
|
1175
|
-
*
|
|
1176
|
-
* 1. If
|
|
1177
|
-
*
|
|
1178
|
-
* 2. If no hidden expression is defined, the control inherits the hidden state
|
|
1179
|
-
* from its parent group
|
|
1180
|
-
* 3. Both conditions can be combined - a control is hidden if either its own
|
|
1181
|
-
* condition evaluates to true OR its parent group is hidden
|
|
833
|
+
* Used for states like disabled and readonly that follow a common pattern:
|
|
834
|
+
* 1. If the option is undefined, inherit the parent state
|
|
835
|
+
* 2. Otherwise, resolve the expression and default to false
|
|
1182
836
|
*
|
|
1183
|
-
* @param
|
|
1184
|
-
* @
|
|
837
|
+
* @param option Signal containing the expression option (boolean, string expression, function, or undefined)
|
|
838
|
+
* @param formContext Signal providing the current form context for expression evaluation
|
|
839
|
+
* @param expressionService Service used to parse and evaluate string expressions
|
|
840
|
+
* @param parentState Signal providing the parent group's state to inherit from
|
|
841
|
+
* @returns Computed signal that resolves to a boolean state
|
|
1185
842
|
*/
|
|
1186
|
-
function
|
|
1187
|
-
const
|
|
1188
|
-
const expressionService = inject(ExpressionService);
|
|
1189
|
-
const parentGroupDirective = inject((NgxfbGroupDirective), {
|
|
1190
|
-
optional: true,
|
|
1191
|
-
skipSelf: true,
|
|
1192
|
-
});
|
|
1193
|
-
const parentGroupIsHidden = computed(() => {
|
|
1194
|
-
const parentGroup = parentGroupDirective;
|
|
1195
|
-
if (!parentGroup) {
|
|
1196
|
-
return false;
|
|
1197
|
-
}
|
|
1198
|
-
return parentGroup.isHidden();
|
|
1199
|
-
});
|
|
1200
|
-
const visibilityAst = computed(() => {
|
|
1201
|
-
const hiddenOption = content().hidden;
|
|
1202
|
-
if (typeof hiddenOption !== 'string') {
|
|
1203
|
-
return null;
|
|
1204
|
-
}
|
|
1205
|
-
return expressionService.parseExpressionToAst(hiddenOption);
|
|
1206
|
-
});
|
|
1207
|
-
const visibilityFunction = computed(() => {
|
|
1208
|
-
const visibilityOption = content().hidden;
|
|
1209
|
-
if (typeof visibilityOption !== 'function') {
|
|
1210
|
-
return null;
|
|
1211
|
-
}
|
|
1212
|
-
return visibilityOption;
|
|
1213
|
-
});
|
|
843
|
+
function resolveInheritableExpression(option, formContext, expressionService, parentState) {
|
|
844
|
+
const resolved = resolveExpression(option, formContext, expressionService);
|
|
1214
845
|
return computed(() => {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
.value;
|
|
1218
|
-
const evaluationContext = reactiveFormValues ?? currentSynchronousFormValues;
|
|
1219
|
-
const visibilityFn = visibilityFunction();
|
|
1220
|
-
if (visibilityFn) {
|
|
1221
|
-
return visibilityFn(evaluationContext);
|
|
1222
|
-
}
|
|
1223
|
-
const ast = visibilityAst();
|
|
1224
|
-
if (!ast) {
|
|
1225
|
-
return parentGroupIsHidden();
|
|
846
|
+
if (option() === undefined) {
|
|
847
|
+
return parentState();
|
|
1226
848
|
}
|
|
1227
|
-
|
|
1228
|
-
return isHidden || parentGroupIsHidden();
|
|
849
|
+
return resolved() ?? false;
|
|
1229
850
|
});
|
|
1230
851
|
}
|
|
852
|
+
|
|
1231
853
|
/**
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1237
|
-
*
|
|
1238
|
-
*
|
|
1239
|
-
*
|
|
1240
|
-
* @param
|
|
1241
|
-
* @
|
|
854
|
+
* Resolves the hidden state with parent combination logic
|
|
855
|
+
*
|
|
856
|
+
* The hidden state differs from disabled/readonly inheritance:
|
|
857
|
+
* 1. If the option is undefined, inherit the parent hidden state
|
|
858
|
+
* 2. If the option is a function expression, use only the resolved value
|
|
859
|
+
* 3. If the option is a string expression, OR with the parent hidden state
|
|
860
|
+
* (a child is hidden if either its own condition or its parent is hidden)
|
|
861
|
+
*
|
|
862
|
+
* @param option Signal containing the hidden expression option
|
|
863
|
+
* @param formContext Signal providing the current form context for expression evaluation
|
|
864
|
+
* @param expressionService Service used to parse and evaluate string expressions
|
|
865
|
+
* @param parentHiddenState Signal providing the parent group's hidden state
|
|
866
|
+
* @returns Computed signal that resolves to a boolean hidden state
|
|
1242
867
|
*/
|
|
1243
|
-
function
|
|
868
|
+
function resolveHiddenState(option, formContext, expressionService, parentHiddenState) {
|
|
869
|
+
const resolved = resolveExpression(option, formContext, expressionService);
|
|
1244
870
|
return computed(() => {
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
return null;
|
|
871
|
+
const hiddenOption = option();
|
|
872
|
+
if (hiddenOption === undefined) {
|
|
873
|
+
return parentHiddenState();
|
|
1249
874
|
}
|
|
1250
|
-
|
|
875
|
+
const isHidden = resolved() ?? false;
|
|
876
|
+
if (typeof hiddenOption === 'function') {
|
|
877
|
+
return isHidden;
|
|
878
|
+
}
|
|
879
|
+
return isHidden || parentHiddenState();
|
|
1251
880
|
});
|
|
1252
881
|
}
|
|
882
|
+
|
|
1253
883
|
/**
|
|
1254
|
-
* Creates an effect that manages
|
|
884
|
+
* Creates an effect that manages disabled state transitions
|
|
1255
885
|
*
|
|
1256
|
-
*
|
|
1257
|
-
*
|
|
1258
|
-
* 2. Detaches the control from the form when hidden and strategy is 'remove'
|
|
1259
|
-
* 3. Manages control values based on the specified valueStrategy when visibility changes
|
|
886
|
+
* When disabledHandling is 'auto', calls the appropriate enable/disable
|
|
887
|
+
* function based on the disabled signal. When 'manual', does nothing.
|
|
1260
888
|
*
|
|
1261
|
-
* @param options Configuration object for
|
|
1262
|
-
* @param options.
|
|
1263
|
-
* @param options.
|
|
1264
|
-
* @param options.
|
|
1265
|
-
* @param options.
|
|
1266
|
-
* @param options.hideStrategySignal Signal with the strategy for handling hidden controls
|
|
1267
|
-
* @param options.valueStrategySignal Signal with the strategy for handling control values
|
|
1268
|
-
* @param options.parentValueStrategySignal Signal with the parent's value strategy
|
|
1269
|
-
* @param options.attachFunction Function to call when control should be attached
|
|
1270
|
-
* @param options.detachFunction Function to call when control should be detached
|
|
1271
|
-
* @param options.valueHandleFunction Function to handle control value based on strategy
|
|
889
|
+
* @param options Configuration object for disabled effect
|
|
890
|
+
* @param options.disabledSignal Signal that indicates if the component should be disabled
|
|
891
|
+
* @param options.disabledHandlingSignal Signal that determines how disabled state changes should be handled
|
|
892
|
+
* @param options.enableFunction Function to call when component should be enabled
|
|
893
|
+
* @param options.disableFunction Function to call when component should be disabled
|
|
1272
894
|
*/
|
|
1273
|
-
function
|
|
1274
|
-
const parentContainer = inject(ControlContainer);
|
|
1275
|
-
const parentFormGroup = parentContainer.control;
|
|
895
|
+
function resolveDisabledEffect(options) {
|
|
1276
896
|
effect(() => {
|
|
1277
|
-
options.
|
|
1278
|
-
const
|
|
1279
|
-
|
|
1280
|
-
const valueStrategy = options.valueStrategySignal() ?? options.parentValueStrategySignal();
|
|
1281
|
-
const formControl = untracked(() => parentFormGroup?.get(options.name()));
|
|
1282
|
-
// Re-attach control
|
|
1283
|
-
// On initial render the form control will not be attached, but we need it for the hide strategy "keep"
|
|
1284
|
-
if (!formControl && (!isHidden || hideStrategy === 'keep')) {
|
|
1285
|
-
untracked(() => {
|
|
1286
|
-
options.attachFunction();
|
|
1287
|
-
});
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
// Control is already detached
|
|
1291
|
-
if (hideStrategy === 'remove' && !formControl) {
|
|
897
|
+
const disabled = options.disabledSignal();
|
|
898
|
+
const disabledHandling = options.disabledHandlingSignal();
|
|
899
|
+
if (disabledHandling === 'manual') {
|
|
1292
900
|
return;
|
|
1293
901
|
}
|
|
1294
|
-
|
|
1295
|
-
if (hideStrategy === 'remove' && isHidden) {
|
|
902
|
+
if (!disabled) {
|
|
1296
903
|
untracked(() => {
|
|
1297
|
-
options.
|
|
904
|
+
options.enableFunction();
|
|
1298
905
|
});
|
|
906
|
+
return;
|
|
1299
907
|
}
|
|
1300
|
-
// Only thing left to check is value strategy
|
|
1301
908
|
untracked(() => {
|
|
1302
|
-
options.
|
|
909
|
+
options.disableFunction();
|
|
1303
910
|
});
|
|
1304
911
|
});
|
|
1305
912
|
}
|
|
1306
913
|
|
|
1307
914
|
/**
|
|
1308
|
-
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1313
|
-
*
|
|
1314
|
-
*
|
|
915
|
+
* Resolves a test ID for a form element
|
|
916
|
+
*
|
|
917
|
+
* The test ID is determined using the following priority:
|
|
918
|
+
* 1. A local testIdBuilder function if provided
|
|
919
|
+
* 2. A global testIdBuilder function if provided
|
|
920
|
+
* 3. Default: joins parentTestId and name with '-', or just name if no parent
|
|
921
|
+
*
|
|
922
|
+
* @param content Signal containing the base content configuration
|
|
923
|
+
* @param name Signal containing the control/group name
|
|
924
|
+
* @param testIdBuilder Signal holding an optional local testIdBuilder function
|
|
925
|
+
* @param globalTestIdBuilder Optional global testIdBuilder function from configuration
|
|
926
|
+
* @param parentTestId Signal providing the parent group's test ID
|
|
927
|
+
* @returns Computed signal that resolves to the test ID string
|
|
1315
928
|
*/
|
|
1316
|
-
function
|
|
1317
|
-
const validatorRegistrations = inject(NGX_VALIDATOR_RESOLVER).registrations;
|
|
1318
|
-
return computed(() => {
|
|
1319
|
-
const validatorKeys = content().validators ?? [];
|
|
1320
|
-
return validatorKeys.flatMap((key) => validatorRegistrations().get(key) ?? []);
|
|
1321
|
-
});
|
|
1322
|
-
}
|
|
1323
|
-
/**
|
|
1324
|
-
* Computes a reactive array of async validators based on control content
|
|
1325
|
-
*
|
|
1326
|
-
* Extracts async validator keys from content and maps them to actual async validator
|
|
1327
|
-
* functions by looking them up in the validator registration service.
|
|
1328
|
-
*
|
|
1329
|
-
* @param content Signal containing control configuration with async validator keys
|
|
1330
|
-
* @returns Computed signal that resolves to an array of async validator functions
|
|
1331
|
-
*/
|
|
1332
|
-
function withAsyncValidators(content) {
|
|
1333
|
-
const asyncValidatorRegistrations = inject(NGX_VALIDATOR_RESOLVER).asyncRegistrations;
|
|
1334
|
-
return computed(() => {
|
|
1335
|
-
const validatorKeys = content().asyncValidators ?? [];
|
|
1336
|
-
return validatorKeys.flatMap((key) => asyncValidatorRegistrations().get(key) ?? []);
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
class NgxFbConfigurationService {
|
|
1341
|
-
_config = inject(NGX_FW_CONFIG_RESOLVED);
|
|
1342
|
-
get testIdBuilder() {
|
|
1343
|
-
return this._config.testIdBuilderFn;
|
|
1344
|
-
}
|
|
1345
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxFbConfigurationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1346
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxFbConfigurationService, providedIn: 'root' });
|
|
1347
|
-
}
|
|
1348
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxFbConfigurationService, decorators: [{
|
|
1349
|
-
type: Injectable,
|
|
1350
|
-
args: [{
|
|
1351
|
-
providedIn: 'root',
|
|
1352
|
-
}]
|
|
1353
|
-
}] });
|
|
1354
|
-
|
|
1355
|
-
/**
|
|
1356
|
-
* Creates a computed signal that extracts the ID for testing purposes
|
|
1357
|
-
*
|
|
1358
|
-
* This utility function derives a test identifier from a form control's content,
|
|
1359
|
-
* which can be used for targeting elements in automated tests.
|
|
1360
|
-
*
|
|
1361
|
-
* @template T - Type extending NgxFbBaseContent
|
|
1362
|
-
* @param content - Signal containing the control or group content configuration
|
|
1363
|
-
* @param name - Signal containing the name of the control
|
|
1364
|
-
* @param testIdBuilder - Signal holding a testIdBuilder function
|
|
1365
|
-
* @returns Computed signal that resolves to the element's ID for testing
|
|
1366
|
-
*/
|
|
1367
|
-
function withTestId(content, name, testIdBuilder) {
|
|
1368
|
-
const parentGroupDirective = inject((NgxfbGroupDirective), {
|
|
1369
|
-
optional: true,
|
|
1370
|
-
skipSelf: true,
|
|
1371
|
-
});
|
|
1372
|
-
const globalConfig = inject(NgxFbConfigurationService);
|
|
1373
|
-
const globalTestIdBuilder = globalConfig.testIdBuilder;
|
|
929
|
+
function resolveTestId(content, name, testIdBuilder, globalTestIdBuilder, parentTestId) {
|
|
1374
930
|
return computed(() => {
|
|
1375
931
|
const contentValue = content();
|
|
1376
932
|
const id = name();
|
|
1377
|
-
const parentGroupTestId =
|
|
933
|
+
const parentGroupTestId = parentTestId();
|
|
1378
934
|
const builderFn = testIdBuilder();
|
|
1379
935
|
if (builderFn) {
|
|
1380
936
|
return builderFn(contentValue, id, parentGroupTestId);
|
|
@@ -1390,1040 +946,51 @@ function withTestId(content, name, testIdBuilder) {
|
|
|
1390
946
|
}
|
|
1391
947
|
|
|
1392
948
|
/**
|
|
1393
|
-
*
|
|
949
|
+
* Resolves the hidden attribute value for DOM binding
|
|
1394
950
|
*
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1399
|
-
* - Uses the application's default strategy as final fallback
|
|
951
|
+
* When visibilityHandling is 'auto', returns `true` (attribute present)
|
|
952
|
+
* or `null` (attribute absent) based on the hidden state.
|
|
953
|
+
* When visibilityHandling is 'manual', always returns `null` so the
|
|
954
|
+
* component can handle visibility itself.
|
|
1400
955
|
*
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
*
|
|
1405
|
-
*
|
|
1406
|
-
* @param content Signal containing the NgxFbAbstractControl with possible updateOn configuration
|
|
1407
|
-
* @returns Computed signal providing the resolved update strategy
|
|
956
|
+
* @param options Configuration object
|
|
957
|
+
* @param options.hiddenSignal Signal indicating whether the element should be hidden
|
|
958
|
+
* @param options.hiddenHandlingSignal Signal for the visibility handling strategy
|
|
959
|
+
* @returns Computed signal resolving to `true` (hidden) or `null` (visible)
|
|
1408
960
|
*/
|
|
1409
|
-
function
|
|
1410
|
-
const parentGroupDirective = inject((NgxfbGroupDirective), {
|
|
1411
|
-
optional: true,
|
|
1412
|
-
skipSelf: true,
|
|
1413
|
-
});
|
|
1414
|
-
const defaultUpdateStrategy = inject(NGX_FW_DEFAULT_UPDATE_STRATEGY);
|
|
1415
|
-
const parentGroupUpdateStrategy = computed(() => {
|
|
1416
|
-
return parentGroupDirective?.updateStrategy();
|
|
1417
|
-
});
|
|
961
|
+
function resolveHiddenAttribute(options) {
|
|
1418
962
|
return computed(() => {
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
/**
|
|
1424
|
-
* Computes a dynamic title for a form control based on expression evaluation
|
|
1425
|
-
*
|
|
1426
|
-
* @param content Signal containing control configuration with dynamicTitle property
|
|
1427
|
-
* @returns Computed signal that resolves to the evaluated dynamic title string or undefined
|
|
1428
|
-
*/
|
|
1429
|
-
function withDynamicTitle(content) {
|
|
1430
|
-
const formService = inject(FormService);
|
|
1431
|
-
const expressionService = inject(ExpressionService);
|
|
1432
|
-
const dynamicTitleAst = computed(() => {
|
|
1433
|
-
const dynamicTitleOption = content().dynamicTitle;
|
|
1434
|
-
if (typeof dynamicTitleOption !== 'string') {
|
|
1435
|
-
return null;
|
|
1436
|
-
}
|
|
1437
|
-
return expressionService.parseExpressionToAst(dynamicTitleOption);
|
|
1438
|
-
});
|
|
1439
|
-
const dynamicTitleFunction = computed(() => {
|
|
1440
|
-
const dynamicTitleOption = content().dynamicTitle;
|
|
1441
|
-
if (typeof dynamicTitleOption !== 'function') {
|
|
963
|
+
const isHidden = options.hiddenSignal();
|
|
964
|
+
const visibilityHandling = options.hiddenHandlingSignal();
|
|
965
|
+
if (visibilityHandling !== 'auto') {
|
|
1442
966
|
return null;
|
|
1443
967
|
}
|
|
1444
|
-
return
|
|
1445
|
-
});
|
|
1446
|
-
return computed(() => {
|
|
1447
|
-
const reactiveFormValues = formService.formValue();
|
|
1448
|
-
const currentSynchronousFormValues = formService.formGroup
|
|
1449
|
-
.value;
|
|
1450
|
-
const evaluationContext = reactiveFormValues ?? currentSynchronousFormValues;
|
|
1451
|
-
const dynamicTitleFn = dynamicTitleFunction();
|
|
1452
|
-
if (dynamicTitleFn) {
|
|
1453
|
-
return dynamicTitleFn(evaluationContext);
|
|
1454
|
-
}
|
|
1455
|
-
const ast = dynamicTitleAst();
|
|
1456
|
-
if (!ast) {
|
|
1457
|
-
return undefined;
|
|
1458
|
-
}
|
|
1459
|
-
const title = expressionService.evaluateExpression(ast, evaluationContext);
|
|
1460
|
-
return title;
|
|
1461
|
-
});
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
/**
|
|
1465
|
-
* Core directive for creating form groups in ngx-formbar.
|
|
1466
|
-
*
|
|
1467
|
-
* This directive handles the integration between Angular's reactive forms and
|
|
1468
|
-
* ngx-formbar's declarative configuration for FormGroups. It manages:
|
|
1469
|
-
*
|
|
1470
|
-
* - Group registration and lifecycle within parent forms
|
|
1471
|
-
* - State management (hidden, disabled, readonly)
|
|
1472
|
-
* - Validation setup
|
|
1473
|
-
* - Test ID generation
|
|
1474
|
-
* - Dynamic title support
|
|
1475
|
-
* - Child control management
|
|
1476
|
-
*
|
|
1477
|
-
* Use this directive with hostDirectives in your custom group components:
|
|
1478
|
-
*
|
|
1479
|
-
* ```typescript
|
|
1480
|
-
* @Component({
|
|
1481
|
-
* hostDirectives: [
|
|
1482
|
-
* {
|
|
1483
|
-
* directive: NgxfbGroupDirective,
|
|
1484
|
-
* inputs: ['content', 'name'],
|
|
1485
|
-
* }
|
|
1486
|
-
* ],
|
|
1487
|
-
* })
|
|
1488
|
-
* export class GroupComponent {
|
|
1489
|
-
* private readonly control = inject(NgxfbGroupDirective<Group>);
|
|
1490
|
-
* readonly content = this.control.content;
|
|
1491
|
-
* readonly controls = this.control.controls;
|
|
1492
|
-
* }
|
|
1493
|
-
* ```
|
|
1494
|
-
*
|
|
1495
|
-
* @template T Type of the group configuration, must extend NgxFbFormGroup
|
|
1496
|
-
*/
|
|
1497
|
-
class NgxfbGroupDirective {
|
|
1498
|
-
parentContainer = inject(ControlContainer);
|
|
1499
|
-
parentGroupDirective = inject((NgxfbGroupDirective), {
|
|
1500
|
-
optional: true,
|
|
1501
|
-
skipSelf: true,
|
|
1502
|
-
});
|
|
1503
|
-
/**
|
|
1504
|
-
* Required input containing the group configuration
|
|
1505
|
-
* Defines properties like type, controls, validation, and state expressions
|
|
1506
|
-
*/
|
|
1507
|
-
content = input.required();
|
|
1508
|
-
/**
|
|
1509
|
-
* Required input for the group's name
|
|
1510
|
-
* Used as the key in the parent FormGroup
|
|
1511
|
-
*/
|
|
1512
|
-
name = input.required();
|
|
1513
|
-
/**
|
|
1514
|
-
* Signal for managing the visibility handling strategy ('auto' or 'manual')
|
|
1515
|
-
* - 'auto': directive handles visibility via hidden attribute
|
|
1516
|
-
* - 'manual': component handles visibility in its own template
|
|
1517
|
-
*/
|
|
1518
|
-
visibilityHandling = signal('auto');
|
|
1519
|
-
/**
|
|
1520
|
-
* Signal for managing the disabled state handling strategy ('auto' or 'manual')
|
|
1521
|
-
* - 'auto': directive handles disabled state via FormGroup methods
|
|
1522
|
-
* - 'manual': component handles disabled state in its own template
|
|
1523
|
-
*/
|
|
1524
|
-
disabledHandling = signal('auto');
|
|
1525
|
-
/**
|
|
1526
|
-
* Signal for the test ID builder function
|
|
1527
|
-
* Used to customize how test IDs are generated
|
|
1528
|
-
*/
|
|
1529
|
-
testIdBuilder = signal(undefined);
|
|
1530
|
-
/**
|
|
1531
|
-
* Computed test ID derived from the group's name
|
|
1532
|
-
* Used for automated testing identification
|
|
1533
|
-
*
|
|
1534
|
-
* Access this in your component template:
|
|
1535
|
-
* ```html
|
|
1536
|
-
* <div [attr.data-testid]="testId()">...</div>
|
|
1537
|
-
* ```
|
|
1538
|
-
*/
|
|
1539
|
-
testId = withTestId(this.content, this.name, this.testIdBuilder);
|
|
1540
|
-
/**
|
|
1541
|
-
* Computed signal for the group's hide strategy
|
|
1542
|
-
* Determines how the group behaves when hidden (keep or remove from form)
|
|
1543
|
-
*/
|
|
1544
|
-
hideStrategy = computed(() => this.content().hideStrategy);
|
|
1545
|
-
/**
|
|
1546
|
-
* Computed signal for the group's value strategy
|
|
1547
|
-
* Determines how the group's values are managed when visibility changes:
|
|
1548
|
-
* - 'last': preserves last values
|
|
1549
|
-
* - 'default': reverts to default values
|
|
1550
|
-
* - 'reset': clears values
|
|
1551
|
-
*/
|
|
1552
|
-
valueStrategy = computed(() => this.content().valueStrategy ?? this.parentValueStrategy());
|
|
1553
|
-
/**
|
|
1554
|
-
* Computed signal for the parent's value strategy
|
|
1555
|
-
* Used when group doesn't define its own strategy
|
|
1556
|
-
*/
|
|
1557
|
-
parentValueStrategy = computed(() => this.parentGroupDirective?.valueStrategy());
|
|
1558
|
-
/**
|
|
1559
|
-
* Computed signal for the hidden state
|
|
1560
|
-
* True when the group should be hidden based on 'hidden' expression
|
|
1561
|
-
*
|
|
1562
|
-
* Use this in your component when implementing custom visibility handling:
|
|
1563
|
-
* ```typescript
|
|
1564
|
-
* readonly isHidden = this.control.isHidden;
|
|
1565
|
-
* ```
|
|
1566
|
-
*/
|
|
1567
|
-
isHidden = withHiddenState(this.content);
|
|
1568
|
-
/**
|
|
1569
|
-
* Computed signal for the hidden attribute
|
|
1570
|
-
* Used in DOM binding to show/hide the group element
|
|
1571
|
-
*/
|
|
1572
|
-
hiddenAttribute = withHiddenAttribute({
|
|
1573
|
-
hiddenSignal: this.isHidden,
|
|
1574
|
-
hiddenHandlingSignal: this.visibilityHandling,
|
|
1575
|
-
});
|
|
1576
|
-
/**
|
|
1577
|
-
* Computed signal for the disabled state
|
|
1578
|
-
* True when the group should be disabled based on 'disabled' expression
|
|
1579
|
-
*
|
|
1580
|
-
* Use this in your component for custom disabled state handling:
|
|
1581
|
-
* ```typescript
|
|
1582
|
-
* readonly disabled = this.control.disabled;
|
|
1583
|
-
* ```
|
|
1584
|
-
*/
|
|
1585
|
-
disabled = withDisabledState(this.content);
|
|
1586
|
-
/**
|
|
1587
|
-
* Computed signal for the readonly state
|
|
1588
|
-
* True when the group should be readonly based on 'readonly' expression
|
|
1589
|
-
*
|
|
1590
|
-
* Use this in your component to implement readonly behavior:
|
|
1591
|
-
* ```typescript
|
|
1592
|
-
* readonly readonly = this.control.readonly;
|
|
1593
|
-
* ```
|
|
1594
|
-
*/
|
|
1595
|
-
readonly = withReadonlyState(this.content);
|
|
1596
|
-
/**
|
|
1597
|
-
* Computed signal for the update strategy
|
|
1598
|
-
* Determines when form values are updated ('change', 'blur', or 'submit')
|
|
1599
|
-
*/
|
|
1600
|
-
updateStrategy = withUpdateStrategy(this.content);
|
|
1601
|
-
/**
|
|
1602
|
-
* Computed signal for the dynamic title
|
|
1603
|
-
* Contains the evaluated result of the dynamicTitle expression
|
|
1604
|
-
*
|
|
1605
|
-
* Use this in your component to display dynamic titles:
|
|
1606
|
-
* ```typescript
|
|
1607
|
-
* readonly displayTitle = computed(() => {
|
|
1608
|
-
* const dynamic = this.control.dynamicTitle();
|
|
1609
|
-
* return dynamic || this.content().title || '';
|
|
1610
|
-
* });
|
|
1611
|
-
* ```
|
|
1612
|
-
*/
|
|
1613
|
-
dynamicTitle = withDynamicTitle(this.content);
|
|
1614
|
-
/**
|
|
1615
|
-
* Computed signal for the validators
|
|
1616
|
-
* Contains validator functions derived from configuration keys
|
|
1617
|
-
*/
|
|
1618
|
-
validators = withValidators(this.content);
|
|
1619
|
-
/**
|
|
1620
|
-
* Computed signal for the async validators
|
|
1621
|
-
* Contains async validator functions derived from configuration keys
|
|
1622
|
-
*/
|
|
1623
|
-
asyncValidators = withAsyncValidators(this.content);
|
|
1624
|
-
/**
|
|
1625
|
-
* Computed signal for the form group instance
|
|
1626
|
-
* Creates a new FormGroup with appropriate validators and configuration
|
|
1627
|
-
*/
|
|
1628
|
-
groupInstance = computed(() => {
|
|
1629
|
-
const validators = this.validators();
|
|
1630
|
-
const asyncValidators = this.asyncValidators();
|
|
1631
|
-
const updateOn = this.updateStrategy();
|
|
1632
|
-
return new FormGroup({}, {
|
|
1633
|
-
validators,
|
|
1634
|
-
asyncValidators,
|
|
1635
|
-
updateOn,
|
|
1636
|
-
});
|
|
968
|
+
return isHidden ? true : null;
|
|
1637
969
|
});
|
|
1638
|
-
/**
|
|
1639
|
-
* Computed signal for the title of the group
|
|
1640
|
-
* Returns the static title from the configuration
|
|
1641
|
-
*/
|
|
1642
|
-
title = computed(() => this.content().title);
|
|
1643
|
-
/**
|
|
1644
|
-
* Computed signal for the child controls of the group
|
|
1645
|
-
* Returns an array of [name, control] pairs for rendering
|
|
1646
|
-
*/
|
|
1647
|
-
controls = computed(() => Object.entries(this.content().controls));
|
|
1648
|
-
/**
|
|
1649
|
-
* Access to the parent FormGroup containing this group
|
|
1650
|
-
*/
|
|
1651
|
-
get parentFormGroup() {
|
|
1652
|
-
return this.parentContainer.control;
|
|
1653
|
-
}
|
|
1654
|
-
/**
|
|
1655
|
-
* Access to this group's FormGroup instance
|
|
1656
|
-
* Use this to access validation state, errors, and other FormGroup methods
|
|
1657
|
-
*/
|
|
1658
|
-
get formGroup() {
|
|
1659
|
-
return this.parentFormGroup?.get(this.name());
|
|
1660
|
-
}
|
|
1661
|
-
constructor() {
|
|
1662
|
-
hiddenEffect({
|
|
1663
|
-
content: this.content,
|
|
1664
|
-
name: this.name,
|
|
1665
|
-
controlInstance: this.groupInstance,
|
|
1666
|
-
hiddenSignal: this.isHidden,
|
|
1667
|
-
hideStrategySignal: this.hideStrategy,
|
|
1668
|
-
valueStrategySignal: this.valueStrategy,
|
|
1669
|
-
parentValueStrategySignal: this.parentValueStrategy,
|
|
1670
|
-
attachFunction: this.setGroup.bind(this),
|
|
1671
|
-
detachFunction: this.removeGroup.bind(this),
|
|
1672
|
-
valueHandleFunction: this.handleValue.bind(this),
|
|
1673
|
-
});
|
|
1674
|
-
disabledEffect({
|
|
1675
|
-
disabledSignal: this.disabled,
|
|
1676
|
-
disabledHandlingSignal: this.disabledHandling,
|
|
1677
|
-
enableFunction: this.enableGroup.bind(this),
|
|
1678
|
-
disableFunction: this.disableGroup.bind(this),
|
|
1679
|
-
});
|
|
1680
|
-
}
|
|
1681
|
-
/**
|
|
1682
|
-
* Sets the visibility handling strategy
|
|
1683
|
-
* Determines if visibility should be managed by the component (manual) or by Formbar (auto)
|
|
1684
|
-
*
|
|
1685
|
-
* Use 'manual' when implementing custom visibility handling in your component:
|
|
1686
|
-
* ```typescript
|
|
1687
|
-
* constructor() {
|
|
1688
|
-
* this.control.setVisibilityHandling('manual');
|
|
1689
|
-
* }
|
|
1690
|
-
* ```
|
|
1691
|
-
*
|
|
1692
|
-
* @param visibilityHandling Strategy for handling visibility ('auto' or 'manual')
|
|
1693
|
-
*/
|
|
1694
|
-
setVisibilityHandling(visibilityHandling) {
|
|
1695
|
-
this.visibilityHandling.set(visibilityHandling);
|
|
1696
|
-
}
|
|
1697
|
-
/**
|
|
1698
|
-
* Sets the disabled handling strategy
|
|
1699
|
-
* Determines if disabled state should be managed by the component (manual) or by Formbar (auto)
|
|
1700
|
-
*
|
|
1701
|
-
* Use 'manual' when implementing custom disabled state handling in your component:
|
|
1702
|
-
* ```typescript
|
|
1703
|
-
* constructor() {
|
|
1704
|
-
* this.control.setDisabledHandling('manual');
|
|
1705
|
-
* }
|
|
1706
|
-
* ```
|
|
1707
|
-
*
|
|
1708
|
-
* @param disabledHandling Strategy for handling disabled state ('auto' or 'manual')
|
|
1709
|
-
*/
|
|
1710
|
-
setDisabledHandling(disabledHandling) {
|
|
1711
|
-
this.disabledHandling.set(disabledHandling);
|
|
1712
|
-
}
|
|
1713
|
-
/**
|
|
1714
|
-
* Sets the function to use for building a test id.
|
|
1715
|
-
* This allows custom test ID generation strategies to be used.
|
|
1716
|
-
*
|
|
1717
|
-
* @param builderFn Function that returns the test id
|
|
1718
|
-
*/
|
|
1719
|
-
setTestIdBuilderFn(builderFn) {
|
|
1720
|
-
this.testIdBuilder.set(builderFn);
|
|
1721
|
-
}
|
|
1722
|
-
/**
|
|
1723
|
-
* Registers this group with the parent FormGroup
|
|
1724
|
-
* @private
|
|
1725
|
-
*/
|
|
1726
|
-
setGroup() {
|
|
1727
|
-
this.parentFormGroup?.setControl(this.name(), this.groupInstance(), {
|
|
1728
|
-
emitEvent: false,
|
|
1729
|
-
});
|
|
1730
|
-
}
|
|
1731
|
-
/**
|
|
1732
|
-
* Removes this group from the parent FormGroup
|
|
1733
|
-
* @private
|
|
1734
|
-
*/
|
|
1735
|
-
removeGroup() {
|
|
1736
|
-
const id = this.name();
|
|
1737
|
-
const formGroup = this.formGroup;
|
|
1738
|
-
// Check if control exists immediately before attempting removal
|
|
1739
|
-
if (formGroup) {
|
|
1740
|
-
this.parentFormGroup?.removeControl(id, { emitEvent: false });
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
/**
|
|
1744
|
-
* Enables the form group
|
|
1745
|
-
* @private
|
|
1746
|
-
*/
|
|
1747
|
-
enableGroup() {
|
|
1748
|
-
const formGroup = this.groupInstance();
|
|
1749
|
-
formGroup.enable({ emitEvent: false });
|
|
1750
|
-
}
|
|
1751
|
-
/**
|
|
1752
|
-
* Disables the form group
|
|
1753
|
-
* @private
|
|
1754
|
-
*/
|
|
1755
|
-
disableGroup() {
|
|
1756
|
-
const formGroup = this.groupInstance();
|
|
1757
|
-
formGroup.disable({ emitEvent: false });
|
|
1758
|
-
}
|
|
1759
|
-
/**
|
|
1760
|
-
* Handles value changes when visibility changes
|
|
1761
|
-
* @param valueStrategy Strategy for handling values
|
|
1762
|
-
* @private
|
|
1763
|
-
*/
|
|
1764
|
-
handleValue(valueStrategy) {
|
|
1765
|
-
switch (valueStrategy) {
|
|
1766
|
-
case 'last':
|
|
1767
|
-
break;
|
|
1768
|
-
case 'default':
|
|
1769
|
-
break;
|
|
1770
|
-
default:
|
|
1771
|
-
// Instead of resetting the group, we need to reset the controls individually
|
|
1772
|
-
// to allow them to overwrite the value strategy
|
|
1773
|
-
// If a control doesn't have a value strategy, we reset it
|
|
1774
|
-
Object.entries(this.content().controls).forEach(([name, control]) => {
|
|
1775
|
-
if (!('valueStrategy' in control)) {
|
|
1776
|
-
return;
|
|
1777
|
-
}
|
|
1778
|
-
if (control.valueStrategy) {
|
|
1779
|
-
return;
|
|
1780
|
-
}
|
|
1781
|
-
const formControl = this.formGroup?.get(name);
|
|
1782
|
-
if (formControl) {
|
|
1783
|
-
formControl.reset(undefined, { emitEvent: false });
|
|
1784
|
-
}
|
|
1785
|
-
});
|
|
1786
|
-
break;
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
/**
|
|
1790
|
-
* Removes the group when the directive is destroyed
|
|
1791
|
-
*/
|
|
1792
|
-
ngOnDestroy() {
|
|
1793
|
-
this.removeGroup();
|
|
1794
|
-
}
|
|
1795
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1796
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.15", type: NgxfbGroupDirective, isStandalone: true, selector: "[ngxfbGroup]", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: true, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.hidden": "hiddenAttribute()" } }, ngImport: i0 });
|
|
1797
970
|
}
|
|
1798
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbGroupDirective, decorators: [{
|
|
1799
|
-
type: Directive,
|
|
1800
|
-
args: [{
|
|
1801
|
-
selector: '[ngxfbGroup]',
|
|
1802
|
-
host: {
|
|
1803
|
-
'[attr.hidden]': 'hiddenAttribute()',
|
|
1804
|
-
},
|
|
1805
|
-
}]
|
|
1806
|
-
}], ctorParameters: () => [] });
|
|
1807
971
|
|
|
1808
972
|
/**
|
|
1809
|
-
*
|
|
973
|
+
* Resolves the update strategy for a form control or group
|
|
1810
974
|
*
|
|
1811
|
-
* The
|
|
1812
|
-
* 1.
|
|
1813
|
-
* 2.
|
|
1814
|
-
*
|
|
1815
|
-
* 3. If no disabled property is defined, the control inherits the disabled state
|
|
1816
|
-
* from its parent group
|
|
975
|
+
* The strategy is determined using the following priority:
|
|
976
|
+
* 1. The control's own updateOn value if defined
|
|
977
|
+
* 2. The parent group's strategy if defined
|
|
978
|
+
* 3. The application-wide default strategy
|
|
1817
979
|
*
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1820
|
-
*
|
|
1821
|
-
* @
|
|
1822
|
-
* @returns Computed signal that resolves to boolean disabled state
|
|
1823
|
-
*/
|
|
1824
|
-
function withDisabledState(content) {
|
|
1825
|
-
const formService = inject(FormService);
|
|
1826
|
-
const expressionService = inject(ExpressionService);
|
|
1827
|
-
const parentGroupDirective = inject((NgxfbGroupDirective), {
|
|
1828
|
-
optional: true,
|
|
1829
|
-
skipSelf: true,
|
|
1830
|
-
});
|
|
1831
|
-
const parentGroupIsDisabled = computed(() => {
|
|
1832
|
-
const parentGroup = parentGroupDirective;
|
|
1833
|
-
if (!parentGroup) {
|
|
1834
|
-
return false;
|
|
1835
|
-
}
|
|
1836
|
-
return parentGroup.disabled();
|
|
1837
|
-
});
|
|
1838
|
-
const disabledAst = computed(() => {
|
|
1839
|
-
const disabledOption = content().disabled;
|
|
1840
|
-
if (typeof disabledOption !== 'string') {
|
|
1841
|
-
return null;
|
|
1842
|
-
}
|
|
1843
|
-
return expressionService.parseExpressionToAst(disabledOption);
|
|
1844
|
-
});
|
|
1845
|
-
const disabledBool = computed(() => {
|
|
1846
|
-
const disabledOption = content().disabled;
|
|
1847
|
-
if (typeof disabledOption !== 'boolean') {
|
|
1848
|
-
return null;
|
|
1849
|
-
}
|
|
1850
|
-
return disabledOption;
|
|
1851
|
-
});
|
|
1852
|
-
const disabledFunction = computed(() => {
|
|
1853
|
-
const disabledOption = content().disabled;
|
|
1854
|
-
if (typeof disabledOption !== 'function') {
|
|
1855
|
-
return null;
|
|
1856
|
-
}
|
|
1857
|
-
return disabledOption;
|
|
1858
|
-
});
|
|
1859
|
-
return computed(() => {
|
|
1860
|
-
const disabledStatic = disabledBool();
|
|
1861
|
-
if (disabledStatic !== null) {
|
|
1862
|
-
return disabledStatic;
|
|
1863
|
-
}
|
|
1864
|
-
const reactiveFormValues = formService.formValue();
|
|
1865
|
-
const currentSynchronousFormValues = formService.formGroup
|
|
1866
|
-
.value;
|
|
1867
|
-
const evaluationContext = reactiveFormValues ?? currentSynchronousFormValues;
|
|
1868
|
-
const disableFn = disabledFunction();
|
|
1869
|
-
if (disableFn) {
|
|
1870
|
-
return disableFn(evaluationContext);
|
|
1871
|
-
}
|
|
1872
|
-
const ast = disabledAst();
|
|
1873
|
-
if (!ast) {
|
|
1874
|
-
return parentGroupIsDisabled();
|
|
1875
|
-
}
|
|
1876
|
-
const disabled = expressionService.evaluateExpression(ast, evaluationContext) ?? false;
|
|
1877
|
-
return disabled;
|
|
1878
|
-
});
|
|
1879
|
-
}
|
|
1880
|
-
/**
|
|
1881
|
-
* Creates an effect that manages control/group disabled state
|
|
1882
|
-
*
|
|
1883
|
-
* @param options Configuration object for disabled effect
|
|
1884
|
-
* @param options.disabledSignal Signal that indicates if the component should be disabled
|
|
1885
|
-
* @param options.disabledHandlingSignal Signal that determines how disabled state changes should be handled
|
|
1886
|
-
* @param options.enableFunction Function to call when component should be enabled
|
|
1887
|
-
* @param options.disableFunction Function to call when component should be disabled
|
|
1888
|
-
*/
|
|
1889
|
-
function disabledEffect(options) {
|
|
1890
|
-
effect(() => {
|
|
1891
|
-
const disabled = options.disabledSignal();
|
|
1892
|
-
const disabledHandling = options.disabledHandlingSignal();
|
|
1893
|
-
if (disabledHandling === 'manual') {
|
|
1894
|
-
return;
|
|
1895
|
-
}
|
|
1896
|
-
if (!disabled) {
|
|
1897
|
-
untracked(() => {
|
|
1898
|
-
options.enableFunction();
|
|
1899
|
-
});
|
|
1900
|
-
return;
|
|
1901
|
-
}
|
|
1902
|
-
untracked(() => {
|
|
1903
|
-
options.disableFunction();
|
|
1904
|
-
});
|
|
1905
|
-
});
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
function withComputedValue(content) {
|
|
1909
|
-
const formService = inject(FormService);
|
|
1910
|
-
const expressionService = inject(ExpressionService);
|
|
1911
|
-
const parentContainer = inject(ControlContainer);
|
|
1912
|
-
const computedValueAst = computed(() => {
|
|
1913
|
-
const computedValueOption = content().computedValue;
|
|
1914
|
-
if (typeof computedValueOption !== 'string') {
|
|
1915
|
-
return null;
|
|
1916
|
-
}
|
|
1917
|
-
return expressionService.parseExpressionToAst(computedValueOption);
|
|
1918
|
-
});
|
|
1919
|
-
const computedValueFunction = computed(() => {
|
|
1920
|
-
const computedValueOption = content().computedValue;
|
|
1921
|
-
if (typeof computedValueOption !== 'function') {
|
|
1922
|
-
return null;
|
|
1923
|
-
}
|
|
1924
|
-
return computedValueOption;
|
|
1925
|
-
});
|
|
1926
|
-
return computed(() => {
|
|
1927
|
-
const value = formService.formValue() ?? parentContainer.value;
|
|
1928
|
-
const computedValueFn = computedValueFunction();
|
|
1929
|
-
if (computedValueFn) {
|
|
1930
|
-
return computedValueFn(value);
|
|
1931
|
-
}
|
|
1932
|
-
const ast = computedValueAst();
|
|
1933
|
-
if (!ast) {
|
|
1934
|
-
return undefined;
|
|
1935
|
-
}
|
|
1936
|
-
return expressionService.evaluateExpression(ast, value);
|
|
1937
|
-
});
|
|
1938
|
-
}
|
|
1939
|
-
function setComputedValueEffect(options) {
|
|
1940
|
-
const parentContainer = inject(ControlContainer);
|
|
1941
|
-
effect(() => {
|
|
1942
|
-
const control = options.controlInstance();
|
|
1943
|
-
const value = options.computeValueSignal();
|
|
1944
|
-
if (!value && parentContainer.pristine) {
|
|
1945
|
-
return;
|
|
1946
|
-
}
|
|
1947
|
-
control.setValue(value);
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
/**
|
|
1952
|
-
* Computes a dynamic label for a form control based on expression evaluation
|
|
1953
|
-
*
|
|
1954
|
-
* @param content Signal containing control configuration with dynamicLabel property
|
|
1955
|
-
* @returns Computed signal that resolves to the evaluated dynamic label string or undefined
|
|
980
|
+
* @param controlUpdateOn Signal containing the control's updateOn configuration
|
|
981
|
+
* @param parentStrategy Signal providing the parent group's update strategy
|
|
982
|
+
* @param defaultStrategy The application-wide default update strategy
|
|
983
|
+
* @returns Computed signal providing the resolved update strategy
|
|
1956
984
|
*/
|
|
1957
|
-
function
|
|
1958
|
-
const formService = inject(FormService);
|
|
1959
|
-
const expressionService = inject(ExpressionService);
|
|
1960
|
-
const dynamicLabelAst = computed(() => {
|
|
1961
|
-
const dynamicLabelOption = content().dynamicLabel;
|
|
1962
|
-
if (typeof dynamicLabelOption !== 'string') {
|
|
1963
|
-
return null;
|
|
1964
|
-
}
|
|
1965
|
-
return expressionService.parseExpressionToAst(dynamicLabelOption);
|
|
1966
|
-
});
|
|
1967
|
-
const dynamicLabelFunction = computed(() => {
|
|
1968
|
-
const dynamicLabelOption = content().dynamicLabel;
|
|
1969
|
-
if (typeof dynamicLabelOption !== 'function') {
|
|
1970
|
-
return null;
|
|
1971
|
-
}
|
|
1972
|
-
return dynamicLabelOption;
|
|
1973
|
-
});
|
|
985
|
+
function resolveUpdateStrategy(controlUpdateOn, parentStrategy, defaultStrategy) {
|
|
1974
986
|
return computed(() => {
|
|
1975
|
-
|
|
1976
|
-
const currentSynchronousFormValues = formService.formGroup
|
|
1977
|
-
.value;
|
|
1978
|
-
const evaluationContext = reactiveFormValues ?? currentSynchronousFormValues;
|
|
1979
|
-
const dynamicLabelFn = dynamicLabelFunction();
|
|
1980
|
-
if (dynamicLabelFn) {
|
|
1981
|
-
return dynamicLabelFn(evaluationContext);
|
|
1982
|
-
}
|
|
1983
|
-
const ast = dynamicLabelAst();
|
|
1984
|
-
if (!ast) {
|
|
1985
|
-
return undefined;
|
|
1986
|
-
}
|
|
1987
|
-
const label = expressionService.evaluateExpression(ast, evaluationContext);
|
|
1988
|
-
return label;
|
|
987
|
+
return controlUpdateOn() ?? parentStrategy() ?? defaultStrategy;
|
|
1989
988
|
});
|
|
1990
989
|
}
|
|
1991
990
|
|
|
1992
|
-
/**
|
|
1993
|
-
* Core directive for creating form controls in ngx-formbar.
|
|
1994
|
-
*
|
|
1995
|
-
* This directive handles the integration between Angular's reactive forms and
|
|
1996
|
-
* ngx-formbar's declarative configuration. It manages:
|
|
1997
|
-
*
|
|
1998
|
-
* - Control registration and lifecycle within parent form groups
|
|
1999
|
-
* - State management (hidden, disabled, readonly)
|
|
2000
|
-
* - Validation setup
|
|
2001
|
-
* - Dynamic value computation
|
|
2002
|
-
* - Test ID generation
|
|
2003
|
-
* - Dynamic label support
|
|
2004
|
-
*
|
|
2005
|
-
* Use this directive with hostDirectives in your custom control components:
|
|
2006
|
-
*
|
|
2007
|
-
* ```typescript
|
|
2008
|
-
* @Component({
|
|
2009
|
-
* hostDirectives: [
|
|
2010
|
-
* {
|
|
2011
|
-
* directive: NgxfbControlDirective,
|
|
2012
|
-
* inputs: ['content', 'name'],
|
|
2013
|
-
* }
|
|
2014
|
-
* ],
|
|
2015
|
-
* })
|
|
2016
|
-
* export class TextControlComponent {
|
|
2017
|
-
* private readonly control = inject(NgxfbControlDirective<TextControl>);
|
|
2018
|
-
* readonly content = this.control.content;
|
|
2019
|
-
* readonly name = this.control.name;
|
|
2020
|
-
* }
|
|
2021
|
-
* ```
|
|
2022
|
-
*
|
|
2023
|
-
* @template T Type of the control configuration, must extend NgxFbControl
|
|
2024
|
-
*/
|
|
2025
|
-
class NgxfbControlDirective {
|
|
2026
|
-
parentContainer = inject(ControlContainer);
|
|
2027
|
-
parentGroupDirective = inject((NgxfbGroupDirective), {
|
|
2028
|
-
optional: true,
|
|
2029
|
-
});
|
|
2030
|
-
/**
|
|
2031
|
-
* Required input containing the control configuration
|
|
2032
|
-
* Defines properties like ID, default value, validation, and state expressions
|
|
2033
|
-
*/
|
|
2034
|
-
content = input.required();
|
|
2035
|
-
/**
|
|
2036
|
-
* Required input for the controls name
|
|
2037
|
-
*/
|
|
2038
|
-
name = input.required();
|
|
2039
|
-
visibilityHandling = signal('auto');
|
|
2040
|
-
disabledHandling = signal('auto');
|
|
2041
|
-
testIdBuilder = signal(undefined);
|
|
2042
|
-
/**
|
|
2043
|
-
* Computed test ID derived from the control's ID
|
|
2044
|
-
* Used for automated testing identification
|
|
2045
|
-
*
|
|
2046
|
-
* Access this in your component template:
|
|
2047
|
-
* ```html
|
|
2048
|
-
* <input [attr.data-testid]="testId()" ... />
|
|
2049
|
-
* ```
|
|
2050
|
-
*/
|
|
2051
|
-
testId = withTestId(this.content, this.name, this.testIdBuilder);
|
|
2052
|
-
/**
|
|
2053
|
-
* Computed signal for the control's hide strategy
|
|
2054
|
-
* Determines how the control behaves when hidden (keep or remove from form)
|
|
2055
|
-
*/
|
|
2056
|
-
hideStrategy = computed(() => this.content().hideStrategy);
|
|
2057
|
-
/**
|
|
2058
|
-
* Computed signal for the control's value strategy
|
|
2059
|
-
* Determines how the control's value is managed when visibility changes
|
|
2060
|
-
*/
|
|
2061
|
-
valueStrategy = computed(() => this.content().valueStrategy ?? this.parentValueStrategy());
|
|
2062
|
-
/**
|
|
2063
|
-
* Computed signal for the parent's value strategy
|
|
2064
|
-
* Used when control doesn't define its own strategy
|
|
2065
|
-
*/
|
|
2066
|
-
parentValueStrategy = computed(() => this.parentGroupDirective?.valueStrategy());
|
|
2067
|
-
/**
|
|
2068
|
-
* Computed signal for the hidden state
|
|
2069
|
-
* True when the control should be hidden based on 'hidden' expression
|
|
2070
|
-
*
|
|
2071
|
-
* Use this in your component when implementing custom visibility handling:
|
|
2072
|
-
* ```typescript
|
|
2073
|
-
* readonly isHidden = this.control.isHidden;
|
|
2074
|
-
* ```
|
|
2075
|
-
*/
|
|
2076
|
-
isHidden = withHiddenState(this.content);
|
|
2077
|
-
/**
|
|
2078
|
-
* Computed signal for the hidden attribute
|
|
2079
|
-
* Used in DOM binding to show/hide the control element
|
|
2080
|
-
*/
|
|
2081
|
-
hiddenAttribute = withHiddenAttribute({
|
|
2082
|
-
hiddenSignal: this.isHidden,
|
|
2083
|
-
hiddenHandlingSignal: this.visibilityHandling,
|
|
2084
|
-
});
|
|
2085
|
-
/**
|
|
2086
|
-
* Computed signal for the disabled state
|
|
2087
|
-
* True when the control should be disabled based on 'disabled' expression
|
|
2088
|
-
*
|
|
2089
|
-
* Use this in your component for custom disabled state handling:
|
|
2090
|
-
* ```typescript
|
|
2091
|
-
* readonly disabled = this.control.disabled;
|
|
2092
|
-
* ```
|
|
2093
|
-
*/
|
|
2094
|
-
disabled = withDisabledState(this.content);
|
|
2095
|
-
/**
|
|
2096
|
-
* Computed signal for the readonly state
|
|
2097
|
-
* True when the control should be readonly based on 'readonly' expression
|
|
2098
|
-
*
|
|
2099
|
-
* Use this in your component to implement readonly behavior:
|
|
2100
|
-
* ```html
|
|
2101
|
-
* <input [attr.readonly]="readonly() || null" ... />
|
|
2102
|
-
* ```
|
|
2103
|
-
*/
|
|
2104
|
-
readonly = withReadonlyState(this.content);
|
|
2105
|
-
/**
|
|
2106
|
-
* Computed signal for the update strategy
|
|
2107
|
-
* Determines when form values are updated ('change', 'blur', or 'submit')
|
|
2108
|
-
*/
|
|
2109
|
-
updateStrategy = withUpdateStrategy(this.content);
|
|
2110
|
-
/**
|
|
2111
|
-
* Computed signal for the dynamic label
|
|
2112
|
-
* Contains the evaluated result of the dynamicLabel expression
|
|
2113
|
-
*
|
|
2114
|
-
* Use this in your component to display dynamic labels:
|
|
2115
|
-
* ```typescript
|
|
2116
|
-
* readonly displayLabel = computed(() => {
|
|
2117
|
-
* const dynamic = this.control.dynamicLabel();
|
|
2118
|
-
* return dynamic || this.content().label;
|
|
2119
|
-
* });
|
|
2120
|
-
* ```
|
|
2121
|
-
*/
|
|
2122
|
-
dynamicLabel = withDynamicLabel(this.content);
|
|
2123
|
-
/**
|
|
2124
|
-
* Computed signal for the validators
|
|
2125
|
-
* Contains validator functions derived from configuration keys
|
|
2126
|
-
*/
|
|
2127
|
-
validators = withValidators(this.content);
|
|
2128
|
-
/**
|
|
2129
|
-
* Computed signal for the async validators
|
|
2130
|
-
* Contains async validator functions derived from configuration keys
|
|
2131
|
-
*/
|
|
2132
|
-
asyncValidators = withAsyncValidators(this.content);
|
|
2133
|
-
/**
|
|
2134
|
-
* Computed signal for the computed value
|
|
2135
|
-
* Contains the evaluated result of computedValue expressions
|
|
2136
|
-
*/
|
|
2137
|
-
computedValue = withComputedValue(this.content);
|
|
2138
|
-
/**
|
|
2139
|
-
* Computed signal for the form control instance
|
|
2140
|
-
* Creates a new FormControl with appropriate validators and configuration
|
|
2141
|
-
*/
|
|
2142
|
-
controlInstance = computed(() => {
|
|
2143
|
-
const content = this.content();
|
|
2144
|
-
const validators = this.validators();
|
|
2145
|
-
const asyncValidators = this.asyncValidators();
|
|
2146
|
-
const updateOn = this.updateStrategy();
|
|
2147
|
-
return new FormControl(content.defaultValue, {
|
|
2148
|
-
nonNullable: content.nonNullable,
|
|
2149
|
-
validators,
|
|
2150
|
-
asyncValidators,
|
|
2151
|
-
updateOn,
|
|
2152
|
-
});
|
|
2153
|
-
});
|
|
2154
|
-
/**
|
|
2155
|
-
* Access to the parent FormGroup containing this control
|
|
2156
|
-
*/
|
|
2157
|
-
get parentFormGroup() {
|
|
2158
|
-
return this.parentContainer.control;
|
|
2159
|
-
}
|
|
2160
|
-
/**
|
|
2161
|
-
* Access to this control's FormControl instance
|
|
2162
|
-
* Use this to access validation state, errors, and other FormControl methods
|
|
2163
|
-
*
|
|
2164
|
-
* Example:
|
|
2165
|
-
* ```typescript
|
|
2166
|
-
* get hasError() {
|
|
2167
|
-
* return this.control.formControl?.hasError('required');
|
|
2168
|
-
* }
|
|
2169
|
-
* ```
|
|
2170
|
-
*/
|
|
2171
|
-
get formControl() {
|
|
2172
|
-
const id = this.name();
|
|
2173
|
-
if (!this.parentFormGroup?.contains(id)) {
|
|
2174
|
-
return null;
|
|
2175
|
-
}
|
|
2176
|
-
return this.parentFormGroup.get(id);
|
|
2177
|
-
}
|
|
2178
|
-
constructor() {
|
|
2179
|
-
hiddenEffect({
|
|
2180
|
-
content: this.content,
|
|
2181
|
-
name: this.name,
|
|
2182
|
-
controlInstance: this.controlInstance,
|
|
2183
|
-
hiddenSignal: this.isHidden,
|
|
2184
|
-
hideStrategySignal: this.hideStrategy,
|
|
2185
|
-
valueStrategySignal: this.valueStrategy,
|
|
2186
|
-
parentValueStrategySignal: this.parentValueStrategy,
|
|
2187
|
-
attachFunction: this.setControl.bind(this),
|
|
2188
|
-
detachFunction: this.removeControl.bind(this),
|
|
2189
|
-
valueHandleFunction: this.handleValue.bind(this),
|
|
2190
|
-
});
|
|
2191
|
-
disabledEffect({
|
|
2192
|
-
disabledSignal: this.disabled,
|
|
2193
|
-
disabledHandlingSignal: this.disabledHandling,
|
|
2194
|
-
enableFunction: this.enableControl.bind(this),
|
|
2195
|
-
disableFunction: this.disableControl.bind(this),
|
|
2196
|
-
});
|
|
2197
|
-
setComputedValueEffect({
|
|
2198
|
-
controlInstance: this.controlInstance,
|
|
2199
|
-
computeValueSignal: this.computedValue,
|
|
2200
|
-
});
|
|
2201
|
-
}
|
|
2202
|
-
/**
|
|
2203
|
-
* Sets the visibility handling strategy
|
|
2204
|
-
* Determines if visibility should be managed by the component (manual) or by Formbar (auto)
|
|
2205
|
-
*
|
|
2206
|
-
* Use 'manual' when implementing custom visibility handling in your component:
|
|
2207
|
-
* ```typescript
|
|
2208
|
-
* constructor() {
|
|
2209
|
-
* this.control.setVisibilityHandling('manual');
|
|
2210
|
-
* }
|
|
2211
|
-
* @param visibilityHandling Strategy for handling visibility ('auto' or 'manual')
|
|
2212
|
-
*/
|
|
2213
|
-
setVisibilityHandling(visibilityHandling) {
|
|
2214
|
-
this.visibilityHandling.set(visibilityHandling);
|
|
2215
|
-
}
|
|
2216
|
-
/**
|
|
2217
|
-
* Sets the disabled handling strategy
|
|
2218
|
-
* Determines if disabled state should be managed by the component (manual) or by Formbar (auto)
|
|
2219
|
-
*
|
|
2220
|
-
* @param disabledHandling Strategy for handling disabled state ('auto' or 'manual')
|
|
2221
|
-
*/
|
|
2222
|
-
setDisabledHandling(disabledHandling) {
|
|
2223
|
-
this.disabledHandling.set(disabledHandling);
|
|
2224
|
-
}
|
|
2225
|
-
/**
|
|
2226
|
-
* Sets the function to use for building a test id.
|
|
2227
|
-
*
|
|
2228
|
-
* @param builderFn Function that returns the test id
|
|
2229
|
-
*/
|
|
2230
|
-
setTestIdBuilderFn(builderFn) {
|
|
2231
|
-
this.testIdBuilder.set(builderFn);
|
|
2232
|
-
}
|
|
2233
|
-
setControl() {
|
|
2234
|
-
this.parentFormGroup?.setControl(this.name(), this.controlInstance(), {
|
|
2235
|
-
emitEvent: false,
|
|
2236
|
-
});
|
|
2237
|
-
}
|
|
2238
|
-
removeControl() {
|
|
2239
|
-
const id = this.name();
|
|
2240
|
-
const formControl = this.formControl;
|
|
2241
|
-
// Check if control exists immediately before attempting removal
|
|
2242
|
-
if (formControl) {
|
|
2243
|
-
this.parentFormGroup?.removeControl(id, { emitEvent: false });
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
2246
|
-
enableControl() {
|
|
2247
|
-
const formControl = this.controlInstance();
|
|
2248
|
-
formControl.enable({ emitEvent: false });
|
|
2249
|
-
}
|
|
2250
|
-
disableControl() {
|
|
2251
|
-
const formControl = this.controlInstance();
|
|
2252
|
-
formControl.disable({ emitEvent: false });
|
|
2253
|
-
}
|
|
2254
|
-
handleValue(valueStrategy) {
|
|
2255
|
-
switch (valueStrategy) {
|
|
2256
|
-
case 'last':
|
|
2257
|
-
break;
|
|
2258
|
-
case 'reset':
|
|
2259
|
-
this.controlInstance().reset(undefined, { emitEvent: false });
|
|
2260
|
-
break;
|
|
2261
|
-
default:
|
|
2262
|
-
this.controlInstance().setValue(this.content().defaultValue);
|
|
2263
|
-
break;
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
ngOnDestroy() {
|
|
2267
|
-
this.removeControl();
|
|
2268
|
-
}
|
|
2269
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbControlDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2270
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.15", type: NgxfbControlDirective, isStandalone: true, selector: "[ngxfbControl]", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: true, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.hidden": "hiddenAttribute()" } }, ngImport: i0 });
|
|
2271
|
-
}
|
|
2272
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbControlDirective, decorators: [{
|
|
2273
|
-
type: Directive,
|
|
2274
|
-
args: [{
|
|
2275
|
-
selector: '[ngxfbControl]',
|
|
2276
|
-
host: {
|
|
2277
|
-
'[attr.hidden]': 'hiddenAttribute()',
|
|
2278
|
-
},
|
|
2279
|
-
}]
|
|
2280
|
-
}], ctorParameters: () => [] });
|
|
2281
|
-
|
|
2282
|
-
/**
|
|
2283
|
-
* Core directive for non-form elements that appear in forms.
|
|
2284
|
-
*
|
|
2285
|
-
* Block elements represent UI components that don't contribute to the form's value
|
|
2286
|
-
* but provide information or functionality within forms, such as:
|
|
2287
|
-
* - Information blocks
|
|
2288
|
-
* - Images
|
|
2289
|
-
* - Dividers
|
|
2290
|
-
* - Help text
|
|
2291
|
-
* - Custom UI elements
|
|
2292
|
-
*
|
|
2293
|
-
* This directive handles visibility conditions and test ID generation for block elements.
|
|
2294
|
-
*
|
|
2295
|
-
* Use this directive with hostDirectives in your custom block components:
|
|
2296
|
-
*
|
|
2297
|
-
* ```typescript
|
|
2298
|
-
* @Component({
|
|
2299
|
-
* hostDirectives: [
|
|
2300
|
-
* {
|
|
2301
|
-
* directive: NgxfbBlockDirective,
|
|
2302
|
-
* inputs: ['content', 'name'],
|
|
2303
|
-
* }
|
|
2304
|
-
* ],
|
|
2305
|
-
* })
|
|
2306
|
-
* export class InfoBlockComponent {
|
|
2307
|
-
* private readonly blockDirective = inject(NgxfbBlockDirective<InfoBlock>);
|
|
2308
|
-
* readonly content = this.blockDirective.content;
|
|
2309
|
-
* readonly message = computed(() => this.content().message);
|
|
2310
|
-
* }
|
|
2311
|
-
* ```
|
|
2312
|
-
*
|
|
2313
|
-
* @template T Type of the block configuration, must extend NgxFbBaseContent
|
|
2314
|
-
*/
|
|
2315
|
-
class NgxfbBlockDirective {
|
|
2316
|
-
/**
|
|
2317
|
-
* Reference to the parent form container.
|
|
2318
|
-
* Provides access to the form that contains this block.
|
|
2319
|
-
*/
|
|
2320
|
-
parentContainer = inject(ControlContainer);
|
|
2321
|
-
/**
|
|
2322
|
-
* Required input containing the block configuration.
|
|
2323
|
-
* Defines properties like type, hidden condition, and custom properties.
|
|
2324
|
-
*/
|
|
2325
|
-
content = input.required();
|
|
2326
|
-
/**
|
|
2327
|
-
* Required input for the block's name.
|
|
2328
|
-
* Used as an identifier within the form.
|
|
2329
|
-
*/
|
|
2330
|
-
name = input.required();
|
|
2331
|
-
/**
|
|
2332
|
-
* Signal for managing the visibility handling strategy ('auto' or 'manual').
|
|
2333
|
-
* - 'auto': directive handles visibility via hidden attribute
|
|
2334
|
-
* - 'manual': component handles visibility in its own template
|
|
2335
|
-
*/
|
|
2336
|
-
visibilityHandling = signal('auto');
|
|
2337
|
-
/**
|
|
2338
|
-
* Signal for the test ID builder function.
|
|
2339
|
-
* Used to customize how test IDs are generated.
|
|
2340
|
-
*/
|
|
2341
|
-
testIdBuilder = signal(undefined);
|
|
2342
|
-
/**
|
|
2343
|
-
* Computed test ID derived from the block's name.
|
|
2344
|
-
* Used for automated testing identification.
|
|
2345
|
-
*
|
|
2346
|
-
* Access this in your component template:
|
|
2347
|
-
* ```html
|
|
2348
|
-
* <div [attr.data-testid]="testId()">...</div>
|
|
2349
|
-
* ```
|
|
2350
|
-
*/
|
|
2351
|
-
testId = withTestId(this.content, this.name, this.testIdBuilder);
|
|
2352
|
-
/**
|
|
2353
|
-
* Computed signal for the hidden state.
|
|
2354
|
-
* True when the block should be hidden based on 'hidden' expression.
|
|
2355
|
-
*
|
|
2356
|
-
* Use this in your component when implementing custom visibility handling:
|
|
2357
|
-
* ```typescript
|
|
2358
|
-
* readonly isHidden = this.blockDirective.isHidden;
|
|
2359
|
-
* ```
|
|
2360
|
-
*/
|
|
2361
|
-
isHidden = withHiddenState(this.content);
|
|
2362
|
-
/**
|
|
2363
|
-
* Computed signal for the hidden attribute.
|
|
2364
|
-
* Used in DOM binding to show/hide the block element.
|
|
2365
|
-
*/
|
|
2366
|
-
hiddenAttribute = withHiddenAttribute({
|
|
2367
|
-
hiddenSignal: this.isHidden,
|
|
2368
|
-
hiddenHandlingSignal: this.visibilityHandling,
|
|
2369
|
-
});
|
|
2370
|
-
/**
|
|
2371
|
-
* Returns the parent form container.
|
|
2372
|
-
* Provides access to the form instance that contains this block.
|
|
2373
|
-
*
|
|
2374
|
-
* Use this to access form data or methods:
|
|
2375
|
-
* ```typescript
|
|
2376
|
-
* const formData = this.blockDirective.rootForm.control.value;
|
|
2377
|
-
* ```
|
|
2378
|
-
*/
|
|
2379
|
-
get rootForm() {
|
|
2380
|
-
return this.parentContainer;
|
|
2381
|
-
}
|
|
2382
|
-
/**
|
|
2383
|
-
* Sets the visibility handling strategy.
|
|
2384
|
-
* Determines if visibility should be managed by the component (manual) or by Formbar (auto).
|
|
2385
|
-
*
|
|
2386
|
-
* Use 'manual' when implementing custom visibility handling in your component:
|
|
2387
|
-
* ```typescript
|
|
2388
|
-
* constructor() {
|
|
2389
|
-
* this.blockDirective.setVisibilityHandling('manual');
|
|
2390
|
-
* }
|
|
2391
|
-
* ```
|
|
2392
|
-
*
|
|
2393
|
-
* @param visibilityHandling Strategy for handling visibility ('auto' or 'manual')
|
|
2394
|
-
*/
|
|
2395
|
-
setVisibilityHandling(visibilityHandling) {
|
|
2396
|
-
this.visibilityHandling.set(visibilityHandling);
|
|
2397
|
-
}
|
|
2398
|
-
/**
|
|
2399
|
-
* Sets the function to use for building a test id.
|
|
2400
|
-
* This allows custom test ID generation strategies to be used.
|
|
2401
|
-
*
|
|
2402
|
-
* @param builderFn Function that returns the test id
|
|
2403
|
-
*/
|
|
2404
|
-
setTestIdBuilderFn(builderFn) {
|
|
2405
|
-
this.testIdBuilder.set(builderFn);
|
|
2406
|
-
}
|
|
2407
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbBlockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2408
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.15", type: NgxfbBlockDirective, isStandalone: true, selector: "[ngxfbBlock]", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: true, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.hidden": "hiddenAttribute()" } }, ngImport: i0 });
|
|
2409
|
-
}
|
|
2410
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: NgxfbBlockDirective, decorators: [{
|
|
2411
|
-
type: Directive,
|
|
2412
|
-
args: [{
|
|
2413
|
-
selector: '[ngxfbBlock]',
|
|
2414
|
-
host: {
|
|
2415
|
-
'[attr.hidden]': 'hiddenAttribute()',
|
|
2416
|
-
},
|
|
2417
|
-
}]
|
|
2418
|
-
}] });
|
|
2419
|
-
|
|
2420
|
-
/*
|
|
2421
|
-
* Public API Surface of core
|
|
2422
|
-
*/
|
|
2423
|
-
|
|
2424
991
|
/**
|
|
2425
992
|
* Generated bundle index. Do not edit.
|
|
2426
993
|
*/
|
|
2427
994
|
|
|
2428
|
-
export {
|
|
995
|
+
export { ComponentRegistrationService, ExpressionService, NGX_FW_COMPONENT_REGISTRATIONS, NGX_FW_COMPONENT_RESOLVER, NGX_FW_CONFIG, NGX_FW_CONFIG_RESOLVED, NGX_FW_DEFAULT_CONFIG, NGX_FW_DEFAULT_UPDATE_STRATEGY, NgxFbConfigurationService, loadComponent, resolveDisabledEffect, resolveExpression, resolveHiddenAttribute, resolveHiddenState, resolveInheritableExpression, resolveTestId, resolveUpdateStrategy, staticComponent, toSafeString };
|
|
2429
996
|
//# sourceMappingURL=ngx-formbar-core.mjs.map
|