6

I get my data from http with rjsx in component (let name it customer).

Then i'm using inner component in customer:

<customer>
  <customer-form [customer]="customer"></customer-form>
</customer>



<!-- [customer]="customer" // here is data from http -->

and in customer-form i have:

@Input() customer:ICustomer;

complexForm : FormGroup;



constructor(fb: FormBuilder) {

  this.complexForm = fb.group({
    'name': [this.customer['name'], Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(255)])]
  });
}

but i get:

Cannot read property 'name' of undefined
TypeError: Cannot read property 'name' of undefined

if i understood correctly: it's due to the fact that constructor is called, but data isn't fetched yet from http, so customer is empty. But how to fix this?

upd: my http data get:

   getCustomer(id) {
    this.customerService.getCustomer(id)
      .subscribe(
        customer => this.customer = customer,
        error =>  this.errorMessage = <any>error);
  }
  ----


@Injectable()
export class CustomerService {

  private customersUrl = 'api/customer';

  constructor (private http: Http) {}

  getCustomers (): Observable<ICustomer[]> {
    return this.http.get(this.customersUrl)
      .map(this.extractData)
      .catch(this.handleError);
  }

  getCustomer (id): Observable<ICustomer> {
    return this.http.get(this.customersUrl + '/' + id)
      .map(this.extractData)
      .catch(this.handleError);
  }



  private extractData(res: Response) {
    let body = res.json();
    return body || { };
  }


  private handleError (error: Response | any) {
    // In a real world app, we might use a remote logging infrastructure
    let errMsg: string;
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || JSON.stringify(body);
      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
  }

}
brabertaser19
  • 5,678
  • 16
  • 78
  • 184
  • Just add a default value as an empty string that will be displayed to the user wainting the http result... `customer=defaultCustomer={name:''}` – Karbos 538 Jan 05 '17 at 08:25

4 Answers4

6

as @Bhushan Gadekar stated, you are accessing customer when it has not been initialized.

There are multiple way to handle this correctly :

Using a setter:

@Input("customer") 
set _customer(c:ICustomer){
  this.customer=c;
  this.complexForm.get("name").setValue(c.name,{onlySelf:true});
}
customer:ICustomer;
complexForm : FormGroup;

constructor(fb: FormBuilder) {

  this.complexForm = fb.group({
    'name': [null, Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(255)])]
  });
}

Using an Observable

Here, the customer needs to be an Observable of ICustomer

@Input() customer:Observable<ICustomer>;

complexForm : FormGroup;

constructor(fb: FormBuilder) {
  this.complexForm = fb.group({
    'name': [this.customer['name'], Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(255)])]
  });
}

ngOnInit(){
  this.customer.map(c=>this.complexForm.get("name").setValue(c.name,{onlySelf:true}))
  .subscribe();
}

Mixing both :

@Input("customer") 
set _customer(c:ICustomer){
  this.customer.next(c);
}
customer=New Subject<ICustomer>();
complexForm : FormGroup;

constructor(fb: FormBuilder) {
  this.complexForm = fb.group({
    'name': [null, Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(255)])]
  });
}

ngOnInit(){
  this.customer.map(c=>this.complexForm.get("name").setValue(c.name,{onlySelf:true}))
  .subscribe();
}

Case for multiple properties :

If you don't want to write every form update one by one, and if your form's field names are the same as your Object you can loop over customer properties:

Object.keys(customer).forEach(k=>{
  let control = this.complexForm.get(k);
  if(control)
    control.setValue(customer[k],{onlySelf:true});
});

Note that this code will work only if your form's controls are named the same way as customer's properties are. If not, you may need to make a hash mapping customer properties name to formControls name.

Important point:

Yous should never access inputs from the constructor as they are not populated yet, all inputs should get populated (at least the synchronous ones) just before the ngOnInit hook. Take a look at the Lifecycle hooks documentation

n00dl3
  • 21,213
  • 7
  • 66
  • 76
0

I can see that you are trying to access customer object when it is not populated.

Issue here is that http call takes some time to be resolved.thus, your view is trying to access customer object even when it is undefined.

try this:

<customer *ngIf="customer">
  <customer-form [customer]="customer"></customer-form>
</customer>

Though the way you are accessing name property is also not good. Best approach is to create a customer model and use your property as className.propertyName

Hoe this helps.

Bhushan Gadekar
  • 13,485
  • 21
  • 82
  • 131
0

Instead of ngOnInit , try ngAfterViewInit

Bhushan Gadekar
  • 13,485
  • 21
  • 82
  • 131
  • i've told you) this won't work: becouse formBuilder should be initialized onInit, otherwise i'll get: `formGroup expects a FormGroup instance. Please pass one in.` – brabertaser19 Jan 05 '17 at 09:14
0

do not use subscribe in component.ts and add async pipe in component.html, like so: <customer-form [customer]="customer | async"></customer-form>

Timathon
  • 1,049
  • 9
  • 11
  • what do you mean by `do not use subscribe` ? (also check my upd) – brabertaser19 Jan 05 '17 at 09:24
  • [plunkr](https://plnkr.co/edit/YZ1o3pv2TXk5gswStwNC?p=preview) as example. – Timathon Jan 05 '17 at 09:27
  • please try to set up a simple version of your code in [plunkr](https://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). – Timathon Jan 05 '17 at 09:29
  • Not a complete copy of your code, just a simple version. Play with plunkr when you have time. It's not that hard. And it will help people to understand your problem much easier. – Timathon Jan 05 '17 at 09:37