35
loading...
This website collects cookies to deliver better user experience
@Directive({
selector:
'input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]',
host: {'(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()'},
providers: [CHECKBOX_VALUE_ACCESSOR]
})
export class CheckboxControlValueAccessor extends BuiltInControlValueAccessor implements
ControlValueAccessor {
/**
* Sets the "checked" property on the input element.
* @nodoc
*/
writeValue(value: any): void {
this.setProperty('checked', value);
}
}
import { Component, EventEmitter } from '@angular/core';
export enum Mood {
Red = 'red',
Green = 'green',
}
@Component({
selector: 'app-custom-component-and-directive',
templateUrl: './custom-component-and-directive.component.html',
styleUrls: ['./custom-component-and-directive.component.scss'],
})
export class CustomComponentAndDirectiveComponent {
/* Reference to the Enum to be used in the template */
readonly moodRef = Mood;
disable: boolean = false;
selected: Mood = Mood.Green;
/* Simulating an standard output of a component */
onChange: EventEmitter<Mood> = new EventEmitter();
updateState(selectedItem: Mood): void {
this.selected = selectedItem; // Updating internal state
this.onChange.emit(this.selected); // 'publish' the new state
}
}
<p>How do you feel?</p>
<ng-container *ngIf="!disable; else disabledTemplate">
<button
[ngClass]="{
custom__button__red: true,
'custom__button--selected': selected === moodRef.Red
}"
(click)="updateState(moodRef.Red)"
>
Red
</button>
<button
[ngClass]="{
custom__button__green: true,
'custom__button--selected': selected === moodRef.Green
}"
(click)="updateState(moodRef.Green)"
>
Green
</button>
</ng-container>
<ng-template #disabledTemplate>
<p>I'm disabled</p>
</ng-template>
.custom__button {
&__red {
background-color: red;
}
&__green {
background-color: green;
}
&--selected {
margin: 1em;
border: solid 5px black;
}
}
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Directive, OnDestroy, forwardRef } from '@angular/core';
import { Subject, filter, takeUntil } from 'rxjs';
import { CustomComponentAndDirectiveComponent } from './custom-component-and-directive.component';
@Directive({
// Indicates the component that the directive is used on
selector: 'app-custom-component-and-directive',
providers: [
// This part is very important to register the class as a ControlValueAccessor one
{
provide: NG_VALUE_ACCESSOR,
// This reference the class that implements Control Value Accessor
useExisting: forwardRef(() => CustomComponentDirective),
multi: true,
},
],
})
export class CustomComponentDirective
implements ControlValueAccessor, OnDestroy
{
private readonly destroyed$ = new Subject<void>();
/**
* @param element Reference to the component instance
*/
constructor(private readonly element: CustomComponentAndDirectiveComponent) {
this.listenComponentChanges();
}
ngOnDestroy(): void {
this.destroyed$.next();
this.destroyed$.complete();
}
/**
* Subscribes to the component output and updates the internal state
*/
private listenComponentChanges(): void {
if (!this.element) {
return;
}
/**
* Event emitter is an Observable that emits events.
*
* Take a look on the definition:
* - export declare interface EventEmitter<T> extends Subject<T> { }
* */
this.element.onChange
.pipe(
filter(() => this.onChange !== null), // check that we have the correct ref to the callback
takeUntil(this.destroyed$)
)
.subscribe((value) => {
this.onChange(value);
});
}
/***********************************************************************
* Control Value Accessor Implementation
***********************************************************************/
private onChange: any;
private onTouch: any;
// Invoked by angular - update internal state
writeValue(obj: any): void {
this.element.selected = obj; // Updating component internal state
}
// Invoked by angular - callback function for changes
registerOnChange(fn: any): void {
this.onChange = fn;
}
// Invoked by angular - callback function for touch events
registerOnTouched(fn: any): void {
this.onTouch = fn;
}
// Invoked by angular - update disabled state
setDisabledState?(isDisabled: boolean): void {
this.element.disable = isDisabled; // Updating component status
}
}
<form [formGroup]="formGroup">
<app-custom-component-and-directive
[formControlName]="controlsRef.Mood"
></app-custom-component-and-directive>
</form>
<form>
<app-custom-component-and-directive
[disabled]="disabled"
[(ngModel)]="selectedMood"
[ngModelOptions]="{ standalone: true }"
></app-custom-component-and-directive>
</form>