0

I have a simple user component that retrieve user's firstname and lastname. When user click on the submit button, it will make a restful call and retrieve the details and pass back to my user object. However, this is not happening at the moment and I'm not sure what went wrong.

When I initiate the model during init manually, the form will display the user's data correctly. If I retrieve and assign the values back to the form from the Rest API call, the form is not being updated.

I've verified the data is being retrieved correctly, my diagnostic method also shows the model has the response data mapped correctly. I've tried the zone.run but the form is not updated too.

UPDATE I found out the reason is because the response is an array of objects. Changing the following has resolved the issue.

this.model = new User().fromJSON(user)[0];

I'm not sure whether there is any way to ensure my service always return an JSON object instead of an array of objects?

userservice.ts

 return this.http.post('http://ng2nodejs.azurewebsites.net/api/user', data , {
            headers: authHeader
        })
        .map((response: Response) => { return response.json()});

My user component - The diagnostic is displaying the data correctly though

{{diagnostic}}

<h1>User Form</h1>
<form (ngSubmit)="f.form.valid && getUser()" #f="ngForm" >                        
    <div class="form-group">
       <label for="firstname">First Name</label>
       <input type="text" id="firstname" [(ngModel)]="model.firstname" name="firstname" >
    </div>
    <div class="form-group">
       <label for="lastname">Last Name</label>
       <input type="text" id="lastname" [(ngModel)]="model.lastname" name="lastname" >
    </div>  

    <button>Submit</button>                
</form>

The user model, the fromJSON method is to convert the response to my user model.

export class User {
  username: string;      
  firstname: string;
  lastname: string; 

  fromJSON(json) {
        for (var propName in json)
            this[propName] = json[propName];
        return this;
    }
}

My component

 import { Component, NgZone } from '@angular/core';
 import { User } from '../auth/user';
 import { UserService } from '../auth/user.service';

 @Component({
   moduleId: module.id,
   selector: 'app-first',
   templateUrl: './first.component.html',
   styleUrls: ['./first.component.css'],

 })
 export class FirstComponent {
     constructor(private userService: UserService, private zone:NgZone) { }  
     model =  new User();    
     getUser() {             
          this.userService
             .getUser('User123')
             .subscribe((user: Object) => {
                  this.zone.run(() => { 
                     this.model = new User().fromJSON(user);     
                  });  
             });
     }

     get diagnostic() { return JSON.stringify(this.model); }
 }
CalebC
  • 922
  • 11
  • 24
  • when do you call your method `getUser()` ? You should call it onInit or in the constructor. – mickdev Mar 06 '17 at 08:58
  • what does ` this.model = new User().fromJSON(user); ` returns in console. try logging 'this.model'. – candidJ Mar 06 '17 at 09:08
  • try `this.model = this.model.fromJSON(user);` inside `getUser` method – candidJ Mar 06 '17 at 09:22
  • @mickdev getUser is called by the form (ngSubmit)="f.form.valid && getUser()" – CalebC Mar 06 '17 at 09:59
  • @candidJ console.log is showing the retrieved user object, replacing new User with this.model did not work. – CalebC Mar 06 '17 at 10:04
  • That's a little weird to me. Did you try to call it in constructor so it'll set the value of model at component construction? – mickdev Mar 06 '17 at 10:12
  • @mickdev Yes. I tried that as well when the form is initiated. Seems like the ngForm model and the component model is detached. – CalebC Mar 06 '17 at 10:30
  • I don't know that any of these minor oversights is causing your problem but it can't hurt to fix them: 1) button should be ` – AngularChef Mar 06 '17 at 10:58
  • You are right. Regarding the sitenote, this is more like a POC to test the binding, when user click on the Submit it will retrieve the user's profile. – CalebC Mar 07 '17 at 00:35

1 Answers1

0

I don't know if this solves your issue, but I would give some pointers here.

If you really don't have a reason to use Classes, I would suggest you'd move to interfaces:

export interface User {
  username: string;      
  firstname: string;
  lastname: string; 
}

and NgZone shouldn't be needed here.

export class FirstComponent {
  constructor(private userService: UserService) { }  
  model: User = {};   

  getUser() {             
    this.userService
      .getUser('User123')
      .subscribe((user: Object) => {
         this.model = user;
      });
  }
}

And if you have control of your API, why not return the newly created user when you actually create the new User, so that you do not need to make a separate http call to get the user.

With the above at least this plunker seems to be working well. The properties are a bit different, since I use mockbackend here.

AT82
  • 71,416
  • 24
  • 140
  • 167
  • If this does not work, you could try and recreate the issue in a plunker? – AT82 Mar 06 '17 at 11:56
  • Thanks. I did a quick test using your codes and I'm getting an error Type '{}' is not assignable to type 'User'. Property 'firstname' is missing in type '{}' for the model: User = {}; The plunker seems to be working fine though. – CalebC Mar 07 '17 at 00:33
  • Yaah, the error is when you are instantiating the User Object? Try this: `model = {};` – AT82 Mar 07 '17 at 08:19
  • Not really. The main reason is the response returned is an array, the solution is this.model = new User().fromJSON(user)[0]; which I updated in my question. – CalebC Mar 12 '17 at 11:49