Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to put FormGroup into a Class

I'm new to Reactive Forms, I use to work with template driven forms.

I'm following the tutorial here: https://angular-templates.io/tutorials/about/angular-forms-and-validations

I have a User Class:

export class User {
  public pseudo: string;
  public email: string;
  public password: string;

  public constructor(init?: User) {
    Object.assign(this, init);
  }
}

And I got my FormGroups in a component:

 this.matching_passwords_group = new FormGroup(
      {
        password: new FormControl(
          '',
          Validators.compose([
            Validators.minLength(5),
            Validators.required,
            Validators.pattern(
              '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$'
            )
          ])
        ),
        confirm_password: new FormControl('')
      },
      (formGroup: FormGroup) => {
        return PasswordValidator.areEqual(formGroup);
      }
    );

    // user details form validations
    this.userDetailsForm = this.fb.group({
      pseudo: new FormControl('', Validators.required),
      email: new FormControl(
        '',
        Validators.compose([
          Validators.required,
          Validators.pattern('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$')
        ])
      ),
      matching_passwords: this.matching_passwords_group,
      terms: new FormControl(false, Validators.pattern('true'))
    });
  }

As you can see there's a nested formGroup for password confirmation (to check that both password are equals).

Then when user clicks on submit I want to transform my formGroup values into my User object.

I followed what's recommended here: Reactive Forms correctly convert Form Value to Model Object

Here my submit method:

onSubmitUserDetails(value) {
    this.newUser = new User(this.userDetailsForm.value);
}

But obviously, with the nested password formGroup I don't have what I need in this.newUser:

{email: "test@test" matching_passwords: {password: "Test1", confirm_password: "Test1"} pseudo: "test" terms: true}

I could set values one by one but could be very long for bigger forms. Is there any handy way to set formGroup values into a class and solve problem with the nested password formGroup? How are we supposed to achieve that?

Is the best solution to have my User object reflecting exactly the formGroups structure, and then exclude useless fields (like password confirmation) when I send my object to the API?

Also what if I had a nested object in my User class, let say a collection of Books for instance, how am I supposed to transform nested FormGroups to match with the classes structure?

like image 225
Lempkin Avatar asked Oct 14 '18 16:10

Lempkin


People also ask

How do you use a FormGroup?

A FormGroup instance can contain a nested FormGroup instance. The use of nested FormGroup is that if we want to get validity state of only few form controls and not all, then we can put those form controls in a nested FormGroup as given below. userForm = new FormGroup({ name: new FormControl('', [Validators.

What is the difference between FormControl and formControlName?

[formControl] assigns a reference to the FormControl instance you created to the FormControlDirective . formControlName assigns a string for the forms module to look up the control by name.

What is difference between FormBuilder and FormControl?

The official docs describe it as: Creating form control instances manually can become repetitive when dealing with multiple forms. The FormBuilder service provides convenient methods for generating controls. So basically saying that FormBuilder is a service that is trying to help us reduce boiler-plate code.


1 Answers

You can use destructuring for this. Considering this as the value of your form:

{
  email: "test@test",
  matching_passwords: {
    password: "Test1",
    confirm_password: "Test1"
  },
  pseudo: "test",
  terms: true
}

You can do this:

onSubmitUserDetails(value) {
  const { email, pseudo } = value;
  const password = value.matching_passwords.password;
  const user = { pseudo, email, password }
  this.newUser = new User(user);
}

UPDATE

There are other ways to make this work as well. Since your form had a different section for passwords(matching_passwords), and I assumed that you didn't want to send that to your API to save it I just took the relevant fields.

  1. If you want to leverage the data directly from your form, then you would have to design your form in such a way, that it's value matches your Class definition.(best suited for bigger objects with nested objects inside them)

  2. You could also use the spread operator(...) to spread out the form value and then set the irrelevant fields to null(best suited for Objects with flat structure)

.

onSubmitUserDetails(value) {
  let user = {...value};
  user.password = value.matching_passwords.password;
  user.matching_passwords = null;
  user.terms = null;
  this.newUser = new User(user);
}
like image 187
SiddAjmera Avatar answered Nov 05 '22 09:11

SiddAjmera