user3595026
user3595026

Reputation: 629

Using Angular2 reactive forms with nested TypeScript model objects

I am trying to use angular 2 model driven forms in my application, but if I have nested objects(with null value) I am not able to make it work as desired.

Here is my code:

person.model.ts (This is Person model object with address as nested object)

import {Address} from './address.model';
export class Person{
   personId: string;
   name: string
   age: number;
   address: Address;
}

address.model.ts

export class Address{
   addressId: string;
   street: string
   city: string;
   state: string;
   zip: string
}

person.component.ts

@Component( {
selector: 'app-person',
templateUrl: 'person.component.html'
})
export class PersonComponent implements OnInit {
personForm: FormGroup;
person: Person;

constructor( private someService: PersonService, private formBuilder: FormBuilder) {}    

ngOnInit(){
   this.someService.getPersonCall( personId)
      .subscribe ( person => {
           this.person = person;
           this.buildForm();
       }
};

buildForm(): void {
   this.personForm = this.formBuilder.group ({
        'name': [this.person.name, [Validator.required]],
        'age': [this.person.age], 
        'street': [this.person.address.streetOne],
        'city': [this.person.address.streetOne],
        'state': [this.person.address.state],
        'zip': [this.person.address.zip],

    });
    this.registerForChanges();
}

registerForChanges(): void {
    this.personForm.get('name').valueChanges.subscribe(value => this.person.name=value);
    this.personForm.get('age').valueChanges.subscribe(value => this.person.age=value);
    this.personForm.get('street').valueChanges.subscribe(value => this.person.address.streetOne=value);
    this.personForm.get('city').valueChanges.subscribe(value => this.person.address.city=value);
    this.personForm.get('state').valueChanges.subscribe(value => this.person.address.state=value);
    this.personForm.get('zip').valueChanges.subscribe(value => this.person.address.zip=value);
}


onSubmit() {
    this.someService.update(this.person).subscribe( response => 
    this.person = response);
}

Here is my person.component.html

<form *ngIf="person" (ngSubmit)="onSubmit()" [formGroup]="personForm"
          novalidate>
    <div class="col-md-6">
        <div class="form-group">
            <label for="nameId">Name</label>
            <input type="text" class="form-control" formControlName="name" id="nameId"/>    
        </div>
        <div class="form-group">
            <label for="ageId">Age</label>
            <input type="number" class="form-control" formControlName="age" id="ageId"/>    
        </div>
   </div>
   <div class="col-md-6">
        <div class="form-group">
            <label for="streetId">Street</label>
            <input type="text" class="form-control" formControlName="street" id="streetId"/>    
        </div>
        <div class="form-group">
            <label for="cityId">City</label>
            <input type="text" class="form-control" formControlName="city" id="cityId"/>    
        </div>
        <div class="form-group">
            <label for="stateId">State</label>
            <input type="text" class="form-control" formControlName="state" id="stateId"/>    
        </div>
        <div class="form-group">
            <label for="zipId">Zip</label>
            <input type="text" class="form-control" formControlName="zip" id="zipId"/>    
        </div>
   </div>

   <button type="submit" [disabled]="!personForm.valid">Save </button>

</form>

I am trying to update the person object that I populated from the service call, but when I retrieved the person object the person object has the null value on the address property and it is breaking my code in buildForm function.

I tried couple of other ways, but not able to make it work

Version #2

buildForm(): void {
 if ( !this.person.address ) {
    this.personForm = this.formBuilder.group ({
        'name': [this.person.name, [Validator.required]],
        'age': [this.person.age], 
        'street': [''],
        'city': [''],
        'state': [''],
        'zip': [''],

    });
 } else {
     this.personForm = this.formBuilder.group ({
        'name': [this.person.name, [Validator.required]],
        'age': [this.person.age], 
        'street': [this.person.address.streetOne],
        'city': [this.person.address.streetOne],
        'state': [this.person.address.state],
        'zip': [this.person.address.zip],

     });
 }

    this.registerForChanges();
}

With this change I was able to render the form without any error, but when I try to update any address fields it failed in registerForChanges function.

Version #3

registerForChanges(): void {

if (! person.address) {
    person.address = new Address();    
this.personForm.get('name').valueChanges.subscribe(value => this.person.name=value);
this.personForm.get('age').valueChanges.subscribe(value => this.person.age=value);
this.personForm.get('street').valueChanges.subscribe(value => this.person.address.streetOne=value);
this.personForm.get('city').valueChanges.subscribe(value => this.person.address.city=value);
this.personForm.get('state').valueChanges.subscribe(value => this.person.address.state=value);
this.personForm.get('zip').valueChanges.subscribe(value => this.person.address.zip=value);
}

after this change I end up adding empty records to the address table when I save the form without changing the address fields.

This code is working without fine if the address property on the function is not null.

Can someone help me make this code work

plunker link

Upvotes: 3

Views: 3602

Answers (1)

Stefan Svrkota
Stefan Svrkota

Reputation: 50643

If I understood your problem well, address is optional. There are two scenarios you want to cover:

  1. There is no address (it's null) when the form is loaded and if nothing is added to address values (Street, City, State or Zip), you want it to stay null when Save is clicked

  2. There is no address (it's null) when the form is loaded and if something is added to address values (Street, City, State or Zip, you want to save those values

You can achieve it like this - in buildForm() function, check if this.person.address is equal to null. If it is, then create new Address:

    buildForm(): void {

        if (!this.person.address) {
        this.person.address = new Address();
        }
    }

This will prevent the error that comes up when you create form when address is null. Next step is to create a function that will be used in onSubmit() function to check if this.person.address values are still null or '' (empty string). If they are, the function will set this.person.address to null and you won't be saving empty Address object, it will still be null instead. If it's not null, it will save Address object with inserted values. Let's call that function checkAddress():

checkAddress(): void {
  if (this.person.address.street == null | this.person.address.street == '' &&
      this.person.address.city == null | this.person.address.city == '' &&
      this.person.address.state == null | this.person.address.state == '' &&
      this.person.address.zip == null | this.person.address.zip == '') {
        this.person.address = null;
      }
}

And finally, you need to call function checkAddress in function onSubmit():

onSubmit() {
  console.log('On Save')
  this.checkAddress();
  console.log(this.person);
  console.log(this.person.address);
}

Here's working Plunker.

Note - I added the following code below "save" code in onSubmit() function to prevent the error if you first click Save with empty values and then try to insert some values:

if (!this.person.address) {
    this.person.address = new Address();
}

Hope this helps. Cheers.

Upvotes: 1

Related Questions