7

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?

1 Answer 1

4

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

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

1 Comment

Old (from 2017) but a clever solution. I was able to adapt it to my case so that a SubFormGroup only becomes a part of the parent FormGroup when entering in SubFormGroup`s Input. Thank you very much.

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.