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);
}
}
as @Bhushan Gadekar stated, you are accessing customer when it has not been initialized.
There are multiple way to handle this correctly :
@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)])]
});
}
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();
}
@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();
}
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With