Guide to successfully submitting an Angular form that includes a nested component

I have developed a custom dateTime component for my application. I am currently facing an issue where I need to integrate this component within a formGroup in a separate component. Despite several attempts, I am unable to display the data from the child form within the parentForm. Is there any way to set this as a property or 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 it feasible to achieve this? Do I need to utilize @Output() in some way?

Appreciate any assistance.
Travis W-

Answer №1

My preferred approach is to pass the FormControl down as an input. In the child component, set up an input like this:

@Input() dateControl: FormControl;

In the parent html file, simply pass down the FormControl like so:

<app-datetime-picker [dateControl]="formGroup['dateControl'] >

Now you can access and manipulate the properties of the FormControl in the parent component just like you normally would.

While I agree that a control value accessor would also be a great solution, it might be a bit more complex to implement.

Answer №2

If you want your app-datetime-picker component to function as a form control, it's important to implement the ControlValueAccessor interface. This will enable you to utilize it similarly to an <input> or <select>.

Your date picker control should follow this structure:

@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;

  //Ensure that your template invokes this function to update the value
  valueChanged(obj: Date) {
    this.writeValue(obj); //store the value for rendering within this component
    this.onChangeCallback(obj); //update the form
  }

  //Fulfill the ControlValueAccessor contract
  writeValue(obj: any): void {
    this.selectedValue = obj;
  }

  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 = () => {
  };
}

You can then include it in a form template like so:

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

The corresponding code to establish the form group would be:

constructor(private formBuilder: FormBuilder){}

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

Answer №3

Shoutout to @SnorreDan for the inspiration... but check out this more detailed answer.

Below is the TypeScript code for the child component:

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


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

In the child's HTML file:

<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's formGroup:

 // Make sure this matches the structure in the child component.
 formGroup: FormGroup = new FormGroup({
    startDate: new FormControl("", Validators.required),
 });

And here's how it looks in the parent's HTML:

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

If you have any questions or need help with this, feel free to reach out! It took me longer than expected to figure it all out.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What methods can be used to avoid getting distracted by input?

Is there a way to prevent the input field in angular-material2 from gaining focus when clicking on a visibility button? To see an example of how it works, visit: material.angular.io Image before pressing the button: https://i.stack.imgur.com/5SmKv.png ...

Can we verify if this API response is accurate?

I am currently delving into the world of API's and developing a basic response for users when they hit an endpoint on my express app. One question that has been lingering in my mind is what constitutes a proper API response – must it always be an o ...

Guide to aligning a component in the middle of the screen - Angular

As I delve into my project using Angular, I find myself unsure about the best approach to rendering a component within the main component. Check out the repository: https://github.com/jrsbaum/crud-angular See the demo here: Login credentials: Email: [e ...

Changing the global type in TypeScript

Currently, I am incorporating two third-party TypeScript libraries into my project. Interestingly, both of these libraries expose a global variable with the same name through the Window interface. However, they offer different methods for interacting with ...

Using Angular to call a separate function from within a click event handler within the same function itself

It seems like the issue at hand is related to the keyword "this" not being assigned as expected. I am currently using Angular 5 along with a WordCloud package. You can find more information about it here. There's a click callback function that retur ...

Implement static backgrounds on images within an Angular application

I am new to using Angular 7 and I have hit a roadblock. I need help understanding how to resize images so that either the height is 270 and the width is less than 470, or the width is 470 and the height is less than 270. Once resized, I want to place these ...

Having trouble getting the express router to function properly in your Node.js TypeScript project?

One of the components in this application is registerClass, where all routes are added. The source code is in the dist directory since this node app is using TypeScript. However, when calling the http://localhost:9001/user endpoint, it seems that it is not ...

Using Angular to parse intricate JSON data

Need help parsing an http request in the following format: [ { "id": 1, "date": "2022-01-13T00:00:00.000+00:00", "time": "2022-01-13T21:21:21.000+00:00", "office&quo ...

Displaying data in charts and tables using Angular ngrx

I am currently working on an Angular 5 app that utilizes an ngrx store to manage widgets connected to a backend using socket.io. Below is an example of how my widget objects are structured. widgets: { 1: { id: 1, type: 'datatabl ...

What steps should I follow to utilize a JavaScript dependency following an NPM installation?

After successfully installing Fuse.js using npm, I am having trouble using the dependency in my JavaScript code. The website instructions suggest adding the following code to make it work: var books = [{ 'ISBN': 'A', 'title&ap ...

Difficulty in monitoring the present machine status through XState in a React application

I'm encountering an issue where I am trying to access the Machine state from within a function in a React component using state.value. However, the current state never changes and it always displays the initial state. Strangely, if I include an onClic ...

Angular allows for the creation of a unique webpage layout featuring 6 divs

I am working on a project where I have an image of a car and I need to overlay 6 divs onto the image which can be selected with a mouse click. When a user clicks on one of the divs, it should change color. I've attempted using z-index, but it doesn&ap ...

Ways to transfer data from TypeScript to CSS within Angular 6

Trying to work with ngClass or ngStyle, but I'm struggling with passing the value. Here's my current code: strip.component.ts import { ... } from '@angular/core'; @Component({ selector: 'app-strip', templateUrl: &apo ...

Using {children} in NextJS & Typescript for layout components

I am looking to develop a component for my primary admin interface which will act as a wrapper for the individual screens. Here is the JavaScript code I have: import Header from '../Header' function TopNavbarLayout({ children }) { return ...

How to efficiently update a child component in React using UseState and establish a connection back to the parent component

I am currently working on developing a prototype for a master/detail scenario in React and Material-UI. The task involves creating a basic list of objects with the ability to edit and save an item using a dialog. While I have successfully updated the visit ...

Need to import a JSON file and convert it to an interface in order to ensure that all fields are included

I am facing an issue where I am attempting to parse a json file using require(). My goal is to convert it into a specific data type and have the conversion fail if the file does not contain all the necessary fields as defined by the interface. Below is my ...

Accepting undefined in rest parameter of typescript

I'm struggling with an exercise involving Function parameters: The maximum function below has the wrong type. To allow undefined in the rest arguments, you need to update the type of the rest parameter. Fortunately, you don't have to change the ...

Tips for resolving the unmounted component issue in React hooks

Any suggestions on resolving this issue: Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect ...

Issue with Prettier AutoFormatting in a project that combines TypeScript and JavaScript codebases

Recently, I've started incorporating TypeScript into an existing JavaScript project. The project is quite large, so I've decided to transition it to TypeScript gradually. Below is a snippet from my eslintrc.js file: module.exports = { parser: ...

Concealing the Submit Button During Server Processing (Issues with Binding?)

My Angular 2 form is set up to send data to the server asynchronously. I want to provide users with visual feedback during the waiting period by changing the blue 'submit' button to a greyed-out 'Please wait...' button. To achieve this, ...