title: angular material 2 custom component with ng value accessor
body: <p>I a working on angular 4.4 + material beta12 custom component and not able to figure out what is wrong in my implementation</p>
  
  <p>I am trying to achieve the below custom input<br>
  <a href="https://i.stack.imgur.com/HCHLV.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/HCHLV.jpg" alt="enter image description here"></a></p>
  
  <p>Task:</p>
  
  <ol>
  <li>set value to formControl, once I got data from server(data.productTeam is data-can see in code)</li>
  <li>on edit, formcontrol should be updated with values (eg:P12DT2H231M)</li>
  </ol>
  
  <p>Issues:</p>
  
  <ol>
  <li>I am not able to bind default value to formcontrol. </li>
  <li>Without ngDefaultControl (No value accessor for form control with name: 'productTeam' error occuring)</li>
  </ol>
  
  <p>dashboard.component.js</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>this.CRForm = this.fb.group({
        productTeam: [data.productTeam || '']
  });</code></pre>
  </div>
  </div>
  </p>
  
  <p>In Dashboard.html</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;mat-form-field  floatPlaceholder="always" &gt;
          &lt;app-mat-custom-form-field #custref formControlName="productTeam" placeholder="P12D" &gt;&lt;/app-mat-custom-form-field&gt;
      &lt;!--&lt;app-mat-custom-form-field #custref formControlName="productTeam" placeholder="P12D" ngDefaultControl&gt;&lt;/app-mat-custom-form-field&gt; --&gt;
        &lt;/mat-form-field&gt;
   {{custref.value}} -- gives value eg:[P12DT1H2M] and only if ngDefaultControl
   {{CRForm['controls']['productTeam']['value']}} --not giving any</code></pre>
  </div>
  </div>
  </p>
  
  <p>mat-custom-form-field.ts</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>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(() =&gt; MatCustomFormFieldComponent),
        multi: true
      }
    ]
  })
  export class MatCustomFormFieldComponent implements OnInit,
  MatFormFieldControl &lt; Duration &gt; , ControlValueAccessor, OnDestroy {
    parts: FormGroup;
    focused = false;
    stateChanges = new Subject &lt; void &gt; ();
    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 &amp;&amp; n.hours &amp;&amp; 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 =&gt; {
        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 &amp;&amp; !n.exchange &amp;&amp; !n.subscriber;
    }
    private propagateChange = (_: any) =&gt; {};
  
    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
  }</code></pre>
  </div>
  </div>
  </p>
  
  <p>mat-custom-form-field.html</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt; div[formGroup]="parts"&gt;
    &lt; input class="area" formControlName="days" size="3"&gt;
      &lt; span&gt; &amp; ndash; &lt; /span&gt;
      &lt; input class="exchange" formControlName="hours" size="3"&gt;
      &lt; span&gt; &amp; ndash; &lt; /span&gt;
      &lt; input class="subscriber" formControlName="minutes" size="3"&gt;
    &lt; /div&gt;</code></pre>
  </div>
  </div>
  </p>