5

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);
  }

}
1
  • Just add a default value as an empty string that will be displayed to the user wainting the http result... customer=defaultCustomer={name:''} Commented Jan 5, 2017 at 8:25

4 Answers 4

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

Sign up to request clarification or add additional context in comments.

7 Comments

if i will have 20 fields, it's a bad idea to set every val(((
just loop over the properties, that's not a problem
anyway, that's the only way.
also: map will not work: this.customer.map is not a function
customer has to be an Observable of ICustomer, not an ICustomer if you want to use the map() method.
|
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.

3 Comments

export interface ICustomer { name: string; address: string; } i have customer model...
you can directly access the name property then, did the above method work? @brabertaser19
ngOnInit() { console.log(this.customer); - Object {} (empty) but in view: /{{customer.name}}/ - outputs normal name... something strange (in input as before: empty val)
0

Instead of ngOnInit , try ngAfterViewInit

1 Comment

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.
0

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

4 Comments

what do you mean by do not use subscribe ? (also check my upd)
plunkr as example.
please try to set up a simple version of your code in plunkr.
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.