简体   繁体   English

角度2:使用http中的数据填充FormBuilder

[英]Angular 2: populate FormBuilder with data from http

I get my data from http with rjsx in component (let name it customer ). 我从HTTP组件中使用rjsx获取数据(将其命名为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. 如果我理解正确 :那是由于调用了构造函数的事实,但尚未从http提取数据,因此customer为空。 But how to fix this? 但是如何解决呢?

upd: my http data get: upd:我的http数据得到:

   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. 正如@Bhushan Gadekar所说,您正在访问尚未初始化的客户。

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 使用Observable

Here, the customer needs to be an Observable of ICustomer 在这里,客户需要是ICustomerObservable

@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相同,则可以遍历客户属性:

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. 如果不是,则可能需要进行哈希映射,将客户属性名称映射为formControls名称。

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. 您永远不要访问构造函数的输入,因为它们尚未被填充,所有输入都应在ngOnInit钩子之前填充(至少是同步的)。 Take a look at the Lifecycle hooks documentation 看看Lifecycle hooks文档

I can see that you are trying to access customer object when it is not populated. 我可以看到您尝试在未填充customer对象时对其进行访问。

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. 这里的问题是http调用需要一些时间才能解决。因此,即使未定义视图,您的视图仍在尝试访问客户对象。

try this: 尝试这个:

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

Though the way you are accessing name property is also not good. 尽管您访问name属性的方式也不好。 Best approach is to create a customer model and use your property as className.propertyName 最好的方法是创建一个客户模型,并将您的媒体资源用作className.propertyName

Hoe this helps. this这有帮助。

代替ngOnInit,尝试ngAfterViewInit

不要在component.ts中使用subscribe在component.html中添加异步管道,例如: <customer-form [customer]="customer | async"></customer-form>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM