I a working on angular 4.4 + material beta12 custom component and not able to figure out what is wrong in my implementation
I am trying to achieve the below custom input
Task:
- set value to formControl, once I got data from server(data.productTeam is data-can see in code)
- on edit, formcontrol should be updated with values (eg:P12DT2H231M)
Issues:
- I am not able to bind default value to formcontrol.
- Without ngDefaultControl (No value accessor for form control with name: 'productTeam' error occuring)
dashboard.component.js
this.CRForm = this.fb.group({ productTeam: [data.productTeam || ''] });
In Dashboard.html
<mat-form-field floatPlaceholder="always" > <app-mat-custom-form-field #custref formControlName="productTeam" placeholder="P12D" ></app-mat-custom-form-field> <!--<app-mat-custom-form-field #custref formControlName="productTeam" placeholder="P12D" ngDefaultControl></app-mat-custom-form-field> --> </mat-form-field> {{custref.value}} -- gives value eg:[P12DT1H2M] and only if ngDefaultControl {{CRForm['controls']['productTeam']['value']}} --not giving any
mat-custom-form-field.ts
import { Component, OnInit, OnDestroy, Input, HostBinding, Optional, Renderer2, Self, forwardRef, ElementRef } from '@angular/core'; import { MatFormFieldControl } from '@angular/material'; import { ControlValueAccessor, FormGroup, FormBuilder, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { FocusMonitor } from '@angular/cdk/a11y'; import { Subject } from 'rxjs/Subject'; class Duration { constructor(public days: number, public hours: number, public minutes: number) {} getDuration() { return 'P' + (this.days || 0) + 'DT' + (this.hours || 0) + 'H' + (this.minutes || 0) + 'M'; } setDuration() {} } @Component({ selector: 'app-mat-custom-form-field', templateUrl: './mat-custom-form-field.component.html', styleUrls: ['./mat-custom-form-field.component.scss'], providers: [{ provide: MatFormFieldControl, useExisting: MatCustomFormFieldComponent }, { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MatCustomFormFieldComponent), multi: true } ] }) export class MatCustomFormFieldComponent implements OnInit, MatFormFieldControl < Duration > , ControlValueAccessor, OnDestroy { parts: FormGroup; focused = false; stateChanges = new Subject < void > (); errorState = false; controlType = 'my-tel-input'; private _disabled = false; private _required = false; private _placeholder: string; static nextId = 0; @Input() get required() { return this._required; } set required(req) { this._required = coerceBooleanProperty(req); this.stateChanges.next(); } @Input() get disabled() { return this._disabled; } set disabled(dis) { this._disabled = coerceBooleanProperty(dis); this.stateChanges.next(); } /* code for placeholder property */ @Input() get placeholder() { return this._placeholder; } set placeholder(plh) { this._placeholder = plh; this.stateChanges.next(); } @Input() get value(): Duration | null { let n = this.parts.value; if (n.days && n.hours && n.minutes) { return new Duration(n.days, n.hours, n.minutes); } return null; } set value(duration: Duration | null) { duration = duration || new Duration(0, 0, 0); this.parts.setValue({ days: duration.days, hours: duration.hours, minutes: duration.minutes }); this.writeValue('P' + (duration.days || 0) + 'DT' + (duration.hours || 0) + 'H' + (duration.minutes || 0) + 'M'); this.stateChanges.next(); } onContainerClick(event: MouseEvent) { if ((event.target as Element).tagName.toLowerCase() != 'input') { this.elRef.nativeElement.querySelector('input').focus(); } } /* code to get id and set id*/ @HostBinding() id = `mat-custom-form- field-${MatCustomFormFieldComponent.nextId++}`; @HostBinding('class.floating') get shouldPlaceholderFloat() { return this.focused || !this.empty; } @HostBinding('attr.aria-describedby') describedBy = ''; setDescribedByIds(ids: string[]) { this.describedBy = ids.join(' '); } constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef, renderer: Renderer2, public ngControl: NgControl, ) { fm.monitor(elRef.nativeElement, renderer, true).subscribe(origin => { this.focused = !!origin; this.stateChanges.next(); }); ngControl.valueAccessor = this; this.parts = fb.group({ 'days': '', 'hours': '', 'minutes': '', }); } ngOnInit() {} ngOnDestroy() { this.stateChanges.complete(); this.fm.stopMonitoring(this.elRef.nativeElement); } get empty() { let n = this.parts.value; return !n.area && !n.exchange && !n.subscriber; } private propagateChange = (_: any) => {}; public writeValue(a: any) { if (a !== undefined) { this.parts.setValue({ days: a.substring(a.lastIndexOf("P") + 1, a.lastIndexOf("D")), hours: a.substring(a.lastIndexOf("T") + 1, a.lastIndexOf("H")), minutes: a.substring(a.lastIndexOf("H") + 1, a.lastIndexOf("M")) }); } }; public registerOnChange(fn: any) { this.propagateChange = fn; } // not used, used for touch input public registerOnTouched() {} // change events from the textarea }
mat-custom-form-field.html
< div[formGroup]="parts"> < input class="area" formControlName="days" size="3"> < span> & ndash; < /span> < input class="exchange" formControlName="hours" size="3"> < span> & ndash; < /span> < input class="subscriber" formControlName="minutes" size="3"> < /div>