-
-
Notifications
You must be signed in to change notification settings - Fork 116
adjust NgxCVA to be usable with field directive and new signal-based forms #679
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9de0845
7535de7
88e9f2e
ff4a78f
fd1101d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,16 @@ | ||
| import { | ||
| Directive, | ||
| Input, | ||
| Output, | ||
| booleanAttribute, | ||
| Directive, | ||
| effect, | ||
| inject, | ||
| Input, | ||
| linkedSignal, | ||
| Output, | ||
| signal, | ||
| untracked, | ||
| } from '@angular/core'; | ||
| import { toObservable } from '@angular/core/rxjs-interop'; | ||
| import { NgControl, NgModel, type ControlValueAccessor } from '@angular/forms'; | ||
| import { type ControlValueAccessor, NgControl, NgModel } from '@angular/forms'; | ||
| import { createInjectionToken } from 'ngxtension/create-injection-token'; | ||
| import { skip } from 'rxjs'; | ||
|
|
||
|
|
@@ -210,22 +211,51 @@ export class NgxControlValueAccessor<T = any> implements ControlValueAccessor { | |
| if (this.ngControl != null) this.ngControl.valueAccessor = this; | ||
| } | ||
|
|
||
| /** @ignore */ | ||
| private initialValue = (): T => { | ||
| if (this.ngControl != null) return this.ngControl.value; | ||
| return injectCvaDefaultValue(); | ||
| }; | ||
| /** | ||
| * Captured at construction time (inside the injection context) so that | ||
| * `initialValue` — which runs as a `linkedSignal` source function outside | ||
| * the injection context — can read the default value without calling | ||
| * `inject()` again (which would throw NG0203). | ||
| * @ignore | ||
| */ | ||
| private readonly _cvaDefaultValue = injectCvaDefaultValue(); | ||
|
|
||
| /** The value of this. If a control is present, it reflects it's value. */ | ||
| public readonly value$ = signal(this.initialValue(), { | ||
| /** | ||
| * We need to use untracked here to avoid that the linkedSignal | ||
| * is initialized again. | ||
| * @ignore | ||
| */ | ||
| private readonly initialValue = (): T => | ||
| untracked(() => | ||
| this.ngControl ? this.ngControl.value : this._cvaDefaultValue, | ||
| ); | ||
|
|
||
| /** | ||
| * The value of this. If a control is present, it reflects it's value. | ||
| * @remarks Internally, this uses a `linkedSignal` to delay the initialization until | ||
| * the host component's inputs are set to avoid runtime exceptions. | ||
| */ | ||
| public readonly value$ = linkedSignal(this.initialValue, { | ||
| equal: (a, b) => this.compareTo(a, b), | ||
| }); | ||
|
|
||
| /** Whether this is disabled. If a control is present, it reflects it's disabled state. */ | ||
| public readonly disabled$ = signal(this.ngControl?.disabled ?? false); | ||
| /** | ||
| * We need to use untracked here to avoid that the linkedSignal | ||
| * is initialized again. | ||
| * @ignore | ||
| */ | ||
|
Comment on lines
+242
to
+246
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to a previous comment, this explanation for using /**
* We use `untracked` to prevent `linkedSignal` from creating a dependency
* on any signals that might be read here. This ensures the source function
* is for initialization only and doesn't re-run unexpectedly.
* @ignore
*/ |
||
| private readonly initialDisabled = (): boolean => | ||
| untracked(() => this.ngControl?.disabled ?? false); | ||
|
|
||
| /** | ||
| * A comparator, which determines value changes. Should return true, if two values are considered semanticly equal. | ||
| * Whether this is disabled. If a control is present, it reflects it's disabled state. | ||
| * | ||
| * @remarks Internally, this uses a `linkedSignal` to delay the initialization until | ||
| * the host component's inputs are set to avoid runtime exceptions. */ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| public readonly disabled$ = linkedSignal(this.initialDisabled); | ||
|
|
||
| /** | ||
| * A comparator, which determines value changes. Should return true, if two values are considered semantically equal. | ||
| * | ||
| * Defaults to {@link Object.is} in order to align with change detection behavior for inputs. | ||
| */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment here is misleading.
untrackeddoes not preventlinkedSignalfrom being re-initialized; it prevents dependency tracking on signals read within its scope. This clarification is important for future maintainability. Let's update the comment to accurately reflect whatuntrackeddoes in this context.