I am currently working on a component that implements the ControlValueAccessor
interface, and I am facing some difficulties in understanding how to properly utilize it:
// Angular Imports
import { Component, OnInit, forwardRef, Output, EventEmitter, OnChanges, Input, ViewChild } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { UserOrEmail } from '../entities/UserOrEmail';
// Provider for the Control Value Accessor
export const USER_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AddUserOrEmailComponent),
multi: true,
};
const noop = () => {
// Placeholder operation
};
@Component({
selector: 'app-add-user-or-email',
templateUrl: './add-user-or-email.component.html',
providers: [USER_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class AddUserOrEmailComponent implements OnInit, ControlValueAccessor {
@Input()
user: any = UserOrEmail;
@Output()
change: EventEmitter<UserOrEmail> = new EventEmitter<UserOrEmail>();
users: any = [];
ngOnInit() {
this.user = {
userId: 'ull',
name: 'null',
email: 'null'
};
this.users = ConstantService.UserArray;
}
// Value Accessor Interface
private onTouchedCallback: () => void = noop;
private onChangeCallback: (_: any) => void = noop;
get value(): any {
return this.user;
}
set value(v: any) {
if (this.user !== v) {
this.user = <UserOrEmail>v;
this.onChangeCallback(v);
this.change.next(this.user);
}
}
writeValue(value: any) {
if (value !== this.user)
this.user = <UserOrEmail>value;
}
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
}
Here is the corresponding HTML:
<div>
<div class="form-column">
<div class="form-row">
<label>
{{'GENERIC.USER'|translate}}
</label>
<select>
<option [ngValue]="'default'"></option>
<option *ngFor="let user of users" [ngValue]="user">{{user.login}}</option>
</select>
</div>
<div class="form-row">
<label for="addPersonEmail" >{{'GENERIC.EMAIL' | translate}}</label>
<input type="email" placeholder="{{'GENERIC.EMAIL'|translate}}" pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$">
</div>
</div>
</div>
I have attempted to use this component in another component:
HTML:
<app-modal class="form" #addMilestoneModal [width]="550">
<div header>{{'MODALS.ADD_MILESTONE'|translate}}</div>
<div body>
<div class="form-column">
<div class="form-value">
<app-add-user-or-email #addUser [(ngModel)]="milestone.assignee"></app-add-user-or-email>
</div>
<div class="form-row">
<label for="addMilestoneDescription">{{'GENERIC.DESCRIPTION' | translate}}</label>
<textarea style="height: 150px" [(ngModel)]="milestone.description"></textarea>
</div>
</div>
</div>
<div footer class="flex-container">
<button class="flex-item-row btn btn-a" [disabled]="!milestone.description" (click)="apply()">{{'GENERIC.APPLY' | translate}}</button>
</div>
</app-modal>
Typescript:
// Angular Imports
import { Component, ViewChild, EventEmitter } from '@angular/core';
import { ModalComponent } from '../../widgets/modal/modal.component';
import { Milestone } from '../../entities/Milestone';
import { ConstantService } from '../../services/ConstantService';
import { AddUserOrEmailComponent } from '../../add-user-or-email/add-user-or-email.component';
@Component({
selector: 'app-add-milestone-modal',
templateUrl: './add-milestone-modal.component.html'
})
export class AddMilestoneModalComponent {
@ViewChild('addMilestoneModal')
modal: ModalComponent;
cs = ConstantService;
emitter: EventEmitter<Milestone> = new EventEmitter<Milestone>();
milestone: any = Milestone;
apply() {
console.log(this.milestone);
debugger;
this.emitter.next(this.milestone);
this.modal.close();
}
cancel() {
this.emitter.next(null);
this.modal.close();
}
}
However, when accessing the milestone
object, it appears to be empty. What could be the issue here?