2

I've created component for datepicker that I want to use in many other components in my application. I have some problems becouse it doesn't change value of parent form group control.

Lets get to the code:

export class DatePickerComponent {
@ViewChild('input', {read: ElementRef, static: false}) inputRef: ElementRef;
@Input()
formName?: FormGroup;
@Input()
formControlName: any;

constructor() {
this.formControlName.setValue(['']);
}

onKeyPress = (event) => {
const pressedKey = event.keyCode;

if (
  pressedKey !== 8 &&
  pressedKey !== 9 &&
  pressedKey !== 13 &&
  ((pressedKey < 48 || pressedKey > 105) || (pressedKey > 57 && pressedKey < 96))
) {
  event.preventDefault();
}
}

onKeyUp = (event) => {
const pressedKey = event.keyCode;
const inputString: string = this.formControlName.value;

if (!inputString) {
  return;
}

if (inputString.length === 11) {
  this.inputRef.nativeElement.value = inputString.slice(0, -1);
  return;
}

if (pressedKey === 8 && (inputString.length === 3 || inputString.length === 6)) {
  const newValue = inputString.slice(0, -1);
  this.formControlName.setValue(newValue);
  this.inputRef.nativeElement.value = newValue;
} else if (inputString.length === 2 || inputString.length === 5) {
  const newValue = inputString + '/';
  this.formControlName.setValue(newValue);
  this.inputRef.nativeElement.value = newValue;
}
}

}

And the template:

<div [formGroup]="formName">
<input
class="form-control"
formControlName="{{formControlName}}"
placeholder="DD/MM/YYYY"
name="dp"
ngbDatepicker
#d="ngbDatepicker"
[minDate]="{year: 1930, month: 1, day: 1}"
(click)="d.toggle()"
(keydown)="onKeyPress($event)"
(keyup)="onKeyUp($event)">
</div>

So I wanted to use it in parent component that is form with FormGroup property set to X. To use my component I've added <app-date-picker [formControlName]="birthDate" [formName]="registerForm"></app-date-picker>

Maybe I did something wrong in parent component:

export class UserFormComponent implements OnInit {

@Input() isRegisterPage: boolean;
@Input() isProfilePage: boolean;
@Input() user: UserInfo;
registerForm: FormGroup;
birthDate: FormControl;
loading: boolean;
.e
.t
.c

How to make my datepicker reusable correctly? Maybe somone can help me?

Fiddle with only datepicker (not used in parent form): https://stackblitz.com/edit/angular-i47zuz-jj5glk?file=app/datepicker-popup.ts

3 Answers 3

1

you are doing it wrong !, to make your custom component work with forms as FormControl you should make it FormControl, to do so, you need to implement ControlValueAccessor interface, so your component would work as custom FormControl, you can read how to do it here -> ControlValueAccessor, if you have problems on implementing it let me know, I can provide further help, but try to implement it on your own first, to learn better

Sign up to request clarification or add additional context in comments.

2 Comments

Hi @Artyom, thanks for Your answer. I tried to implement ControlValueAccessor interface but it's not so easy for me. What I did is in the fiddle: stackblitz.com/edit/angular-i47zuz . I have two imporant method (onKeyPress and onKeyUp - I need them to put / after 2 and 4 digits to make date format as expected and there I'm setting value of form control name. Could You help me with this?
@DominikZ here I wrote minimal implementation stackblitz.com/edit/angular-i47zuz-gwsvav, you can check it and implement on it your version, also check comments I wrote, it will help you
0

Really you need't make a custom form Control, it's only pass the control as input

In parent, e.g.

<form [formGroup]="form">
   <my-component [control]="form.get('bithdate')"></my-component>
</form>

You component

<input [formControl]="control" ...>

@Input() control:FormControl

Comments

0

You can take a look at a library I wrote for building forms called Easy Angular. https://github.com/adriandavidbrand/ngx-ez it allows you to build self validating forms with very little code and extending the controls to make your own is easy.

Here is a BootStrap date picker implemented in it.

import { Component, Optional, Self } from '@angular/core';
import { NgControl } from '@angular/forms';

import {
  EzControlBase,
  EzFormDirective,
  EzFormConfigService,
  EzFormConfigDirective,
  EzFormReadonlyDirective
} from 'ngx-ez';

@Component({
  selector: 'ez-date',
  templateUrl: './ez-date.component.html',
  styleUrls: ['./ez-date.component.css'],
  providers: [{ provide: EzControlBase, useExisting: EzDateComponent }]
})
export class EzDateComponent extends EzControlBase {
  constructor(
    configService: EzFormConfigService,
    @Optional() configDirective: EzFormConfigDirective,
    @Optional() ezForm: EzFormDirective,
    @Optional() ezezReadonly: EzFormReadonlyDirective,
    @Self() @Optional() ngControl: NgControl
  ) {
    super(configService, configDirective, ezForm, ezezReadonly, ngControl);
  }
}

and the template

<ez-control>
  <ng-content></ng-content>
  <ng-container controls-container>
    <ng-container *ngIf="readonly$ | async">{{ value | date : 'dd/MM/yyyy' }}</ng-container>
    <input bsDatepicker [bsConfig]="{ dateInputFormat: 'DD/MM/YYYY' }" [id]="(name$ | async) + '_control_input'" [attr.aria-describedby]="(valid$ | async) ? null : (name$ | async) + '_validation_message'" [attr.maxlength]="maxlength$ | async" type="text" [attr.placeholder]="placeholder$ | async" [value]="value | date : 'dd/MM/yyyy'" (bsValueChange)="onChange($event)"
      [ngClass]="(config$ | async).inputClasses" [valid]="valid$ | async" [class.d-none]="readonly$ | async">
  </ng-container>
</ez-control>

See a working demo at this StackBlitz https://stackblitz.com/edit/angular-t1wshi?file=src%2Fapp%2Fez-date%2Fez-date.component.html

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.