3

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

user3595026
  • 560
  • 2
  • 9
  • 26
  • If you're not too lazy, it would be best to create plunker that reproduces your problem. Here's empty Angular 2 project to get you started: https://angular.io/resources/live-examples/quickstart/ts/plnkr.html – Stefan Svrkota Oct 24 '16 at 19:40
  • @StefanSvrkota Thanks for the link! created the [plunker](http://plnkr.co/edit/pYp0KASufq2RW6cWADRL?p=preview) – user3595026 Oct 25 '16 at 00:14
  • I'm looking at your code now, but I'm not sure what the problem is. Can you explain it in a few words? :) – Stefan Svrkota Oct 25 '16 at 07:03
  • If I save that form without changing anything I am creating/saving empty Address object(Address property with all null values). @StefanSvrkota – user3595026 Oct 25 '16 at 16:04

1 Answers1

1

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.

Stefan Svrkota
  • 48,787
  • 9
  • 98
  • 87
  • Thanks for your effort! We looked at this option when we are developing our form, but code like this is hard to maintain(In case if we want to add more properties to the address model in future. In our real application child object has many properties). Is there any other way without using `checkAddress()` function? – user3595026 Oct 26 '16 at 00:49