2

I have a dateTime component that I built that I am going to use throughout an application. I have a formGroup that I am using to submit a form on a separate component, and I need this component to be a part of that form. I can't seem to get the data from the child form to show in the parentForm. Is there a way to set this as a property/ value of the parent form?

Child DateTime Picker HTML:

<mat-form-field>
  <input matInput [ngxMatDatetimePicker]="picker" placeholder="{{ name }}" [formControl]="dateControl" required="true">
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <ngx-mat-datetime-picker #picker startView="year"></ngx-mat-datetime-picker>
</mat-form-field>

Child Typescript:

@Input() name: string;
@Input() displayTime: boolean;
@Input() allowDateInPast: boolean;

public dateControl = new FormControl();

constructor() { }

ngOnInit() {

}

Parent HTML/form:

<form [formGroup]="formGroup">
<mat-label>Name</mat-label>
    <input type="textfield" formControlName="reportName" matInput required="true" placeholder="Report Name" name="reportName">
</mat-form-field>

<div class="col-sm">
    <app-datetime-picker [name]="'Start Date'" [displayTime]="true" [allowDateInPast]="true"></app-datetime-picker>
</div>

<button class="float-right" [disabled]="formGroup.invalid" (click)="createReport()" mat-raised-button color="primary">Save</button>
  </div>
</form>

Parent Typescript:

formGroup: FormGroup = new FormGroup({

reportName: new FormControl("", Validators.required),
// ?? something here
});

Is this possible? Would I need to use @Output() in some manner?

Thanks for any help.
Travis W-

1
  • You should implement the ControlValueAccessor interface in your date picker. Check this Stackblitz: stackblitz.com/edit/angular-oe2eth Commented Jan 31, 2020 at 18:33

3 Answers 3

2

What I usually do is I pass the FormControl down as an input. So on the child component you have an input: @Input() dateControl: FormControl;

In your parent html you pass down the FormControl like: <app-datetime-picker [dateControl]="formGroup['dateControl'] >

You can now read the properties of the FormControl in the parent component just as normal.

I do agree with the other answers as well, control value accessor would be a good solution for this, but a bit harder to implement.

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

1 Comment

I built a new answer based off this one. A mixture of your answer and some googling around is what finally helped me solve it. Thank you!
0

Since you want you app-datetime-picker component to be used as a form control, I'd make sure you implement ControlValueAccessor. This will allow you to use it as if it was something like an <input> or <select>.

Your date picker control will look something like this:

@Component({
  selector: 'app-datetime-picker',
  templateUrl: './app-datetime-picker.html',
  styleUrls: ['./app-datetime-picker.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DateControlPicker),
      multi: true
    }
  ]
})
export class DateTimePicker implements ControlValueAccessor {
  disabled = false;
  innerValue: Date;

  //Make sure your template calls this function to update the value
  valueChanged(obj: Date) {
    this.writeValue(obj); //save the value so this component can render it
    this.onChangeCallback(obj); //update the form
  }

  //satisfy the ControlValueAccessor interface
  //the forms library will call this when the form is first initialized or when the control is updated by the code "myFormGroup.controls['someControlName'].patchValue(new Date())"
  writeValue(obj: any): void {
    this.selectedValue = obj;
  }

  //Register a callback - this callback function is what we neeed to call to update that form
  registerOnChange(fn: any): void {
    this.onChangeCallback = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouchedCallback = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  private onTouchedCallback: () => void = () => {
  };
  private onChangeCallback: (_: any) => void = () => {
  };
}

Then you can use it in template that has a form:

<form [formGroup]="myFormGroup">
    <app-datetime-picker formControlName="myControlName"></app-datetime-picker>
</form>

The code-behind to build the form group:

constructor(private formBuilder: FormBuilder){}

ngOnInit(){
    this.myFormGroup = this.formBuilder.group({
        myDateControlName: new FormControl(new Date());
    })
}

Comments

-1

Inspired by @SnorreDan ... but here is a more complete answer.

Here is the child component TS:

@Input() dateTime: FormGroup = new FormGroup({
    startDate: new FormControl("", Validators.required),
  });


constructor() { 
    this.dateTime = new FormGroup({});
    this.dateTime.addControl("startDate", new FormControl());
  }

In the child html:

<mat-form-field>
      <input matInput [ngxMatDatetimePicker]="picker" placeholder="{{ placeholder }}" required="true" formControlName="startDate">
      <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
      <ngx-mat-datetime-picker #picker startView="year"></ngx-mat-datetime-picker>
</mat-form-field>

In the parent formGroup:

 // This is important! It must match what is in the child component.
 formGroup: FormGroup = new FormGroup({
    startDate: new FormControl("", Validators.required),
 });

finally the parent HTML:

<app-datetime-picker [dateTime]="formGroup" [placeholder]="'Start Date'" [endDate]="false" [displayTime]="false" [allowDateInPast]="true"></app-datetime-picker>

Happy to answer any questions for anyone who struggles through this one! Took me way longer than it should have.

2 Comments

You should check @spots' answer. It's much better than this one.
Can you help me understand why it's better? Seems like the simpler answer is this one. What makes it worse?

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.