Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating optional nested formgroups in Angular 2

I am trying to implement a model-driven form in Angular 2. The structure of my data model is as follows:

archive (FormGroup)
    name (FormControl)
    description (FormControl)
    connection (FormGroup)
        url (FormControl)
        authentication (FormGroup)
            username (FormControl)
            password (FormControl)

Within this data model, the top-level name is required but the description field is optional. I can apply a required validator to name, and omit a validator from description.

For the connection, I want it to be optional but if a connection is present its URL becomes required. Similarly for the authentication model of the connection: It is optional but if present the username and password would be required.

I am trying to understand how to set up validators to enforce these rules. I have tried just omitting any validators from the connection form group, but that seemed to be requiring me to have a connection. I've seen online tutorials explaining how to implement custom validation on a nested form groups but nothing that describes how to make the entire nested formgroup optional.

Is there a straightforward way to implement this model with Angular 2 FormGroup?

like image 691
Tim Dean Avatar asked Mar 14 '17 14:03

Tim Dean


1 Answers

I had a similar need and here is a way to solve it:

this.form = new FormGroup({
    'name': new FormControl(null, Validators.required),
    'description': new FormControl(null),
    'connection': new FormGroup({
        'url': new FormControl(null),
        'authentification': new FormGroup({
            'username': new FormControl(null, Validators.minLength(5)),
            'password': new FormControl(null),
        }, this.authentificationValidator.bind(this)),
    }, this.connectionValidator.bind(this))
});

The 2 validator functions:

authentificationValidator(g: FormGroup): any {
    const username = g.get('username').value;
    const password = g.get('password').value;

    if( (username && !password) || (!username && password) ) {
        return {
            authentification: true
        };
    }
}

connectionValidator(g: FormGroup): any {
    const url = g.get('url').value;

    const authentification = g.get('authentification');
    const username = authentification.get('username').value;
    const password = authentification.get('password').value;

    if( (username || password) && !url ) {
      return {
          connection: true
      };
    }
}

And for the output, if you fill only the name, you will still have:

{
  "name": null,
  "description": null,
  "connection": {
    "url": null,
    "authentification": {
      "username": null,
      "password": null
    }
  }
}

So you have to conditionnaly create a new object to have:

{
  "name": null,
  "description": null,
  "connection": null
}

Check this plunker to experiment this solution

like image 51
bertrandg Avatar answered Sep 24 '22 03:09

bertrandg