Skip to content
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

feat(NgxControlValueAccessor): Add NgxControlValueAccessor #227

Merged
merged 9 commits into from
Jan 20, 2024

Conversation

RobbyRabbitman
Copy link
Contributor

NgxControlValueAccessor is a directive to reduce boilerplate when building components, which implement the ControlValueAccessor interface.

Usage

NgxControlValueAccessor implements the ControlValueAccessor interface and exposes a simpler api. Declare NgxControlValueAccessor in the hostDirectives section of your component and inject the instance in order to wire up your template:

  • NgxControlValueAccessor.value for syncing the value
  • NgxControlValueAccessor.disabled for syncing the disabled state
  • NgxControlValueAccessor.markAsTouched for marking the view as touched

The value and disabled state are also available as signals:

  • NgxControlValueAccessor.value$
  • NgxControlValueAccessor.disabled$

Example

In this example NgxControlValueAccessor is used to create a CustomInput component.

@Component({
	selector: 'custom-input',
	hostDirectives: [NgxControlValueAccessor],
	template: `
		<label>
			<b>Custom label</b>
			<input
				type="text"
				(input)="cva.value = $event.target.value"
				[value]="cva.value$()"
				[disabled]="cva.disabled$()"
				(blur)="cva.markAsTouched()"
			/>
		</label>
	`,
	standalone: true,
})
export class CustomInput {
	protected cva = inject<NgxControlValueAccessor<string>>(
		NgxControlValueAccessor,
	);
}

With usage:

<custom-input [formControl]="control" />
<custom-input [(ngModel)]="value" />

Non Primitive Values

When your model is a non primitive datatype, you should provide a comparator. It is a pure function which tells NgxControlValueAccessor, whether two values are semantically equal:

(a, b) => boolean;

Example

In this example NgxControlValueAccessor is used to create a User select. A User is identified by its id.

interface User {
	id: string;
	name: string;
}

const userComparator: NgxControlValueAccessorCompareTo<User> = (a, b) =>
	a?.id === b?.id;

provideCvaCompareTo(userComparator, true);

// or

provideCvaCompareToByProp<User>('id');

Full example:

@Component({
	selector: 'user-select',
	standalone: true,
	hostDirectives: [NgxControlValueAccessor],
	providers: [provideCvaCompareToByProp<User>('id')],
	template: `
		<label>
			<b>Select a user:</b>
			<select
				[disabled]="cva.disabled$()"
				(blur)="cva.markAsTouched()"
				(change)="onChange($event)"
			>
				<option [selected]="cva.value === null">-- no user selected --</option>
				@for (user of users; track user.id) {
					<option [value]="user.id" [selected]="user.id === cva.value?.id">
						{{ user.name }}
					</option>
				}
			</select>
		</label>
	`,
})
export class UserSelect {
	protected cva = inject<NgxControlValueAccessor<User | null>>(
		NgxControlValueAccessor,
	);

	protected onChange = (event: Event) =>
		(this.cva.value =
			this.users.find(({ id }) => event.target.value === id) ?? null);

	@Input()
	users: User[] = [];
}

With usage:

<user-select [formControl]="userControl" [options]="users" />
<user-select [(ngModel)]="user" [options]="users" />

Without NgControl

Optionally you can expose inputs and outputs in the hostDirectives declaration
and use it without a NgControl directive.

hostDirectives: [
	{
		directive: NgxControlValueAccessor,
		inputs: ['value'],
		outputs: ['valueChange'],
	},
];
<custom-input [(value)]="value" />

Copy link

nx-cloud bot commented Jan 15, 2024

☁️ Nx Cloud Report

CI is running/has finished running commands for commit f4169f1. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


✅ Successfully ran 3 targets

Sent with 💌 from NxCloud.

@RobbyRabbitman
Copy link
Contributor Author

@nartc Hello, could you please share your thoughts on this feature and do a review? 😃

Thank you!

@nartc nartc merged commit 7692c46 into ngxtension:main Jan 20, 2024
6 checks passed
@RobbyRabbitman RobbyRabbitman deleted the feat/ngx-control-value-accessor branch August 7, 2024 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants