Tim Dean
Tim Dean

Reputation: 8292

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?

Upvotes: 6

Views: 3273

Answers (1)

bertrandg
bertrandg

Reputation: 3207

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),
        'authentication': new FormGroup({
            'username': new FormControl(null, Validators.minLength(5)),
            'password': new FormControl(null),
        }, this.authenticationValidator.bind(this)),
    }, this.connectionValidator.bind(this))
});

The 2 validator functions:

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

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

connectionValidator(g: FormGroup): any {
    const url = g.get('url').value;
    
    const authentication = g.get('authentication');
    const username = authentication.get('username').value;
    const password = authentication.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,
    "authentication": {
      "username": null,
      "password": null
    }
  }
}

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

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

Check this plunker to experiment this solution

Upvotes: 3

Related Questions