Angular 将可观察数据修补到反应式表单字段
Angular patch observable data to reactive form fields
我有一个响应式表单,我正在像这样初始化 oninit() 以及我用来从 URL 中获取传递的 id 并判断是否或不是用于更新或在 mysql table 中创建新条目的表单。我遇到的问题是使用 patchValue 将数据 return 从我的服务传递到我的表单中:
component.ts
export class formComponent implements OnInit, AfterViewInit {
constructor(
private dataService: dataService,
private route: ActivatedRoute,
private router: Router,
private formBuilder: FormBuilder,
private ticketModel: ticketModel,
) {}
Form!: FormGroup;
isNewMode!: boolean;
id!: string;
ticket!: ticketModel[];
ngOnInit(){
this.id = this.route.snapshot.params['id'];
this.isNewMode = !this.id;
this.Form = this.formBuilder.group({
field1: ['', Validators.required],
field2: ['', Validators.required],
field3: ['', Validators.required],
field4: ['', Validators.required]
});
}
ngAfterViewInit(){
if(!this.isNewMode){
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe({
next: ticketData => {
this.ticket = ticketData;
},
});
this.Form.patchValue({
field1: this.ticket.field1, //error, "Property 'field1' does not exist on type 'ticketModel[]'"
field2: this.ticket.field2, //error, "Property 'field2' does not exist on type 'ticketModel[]'"
field3: this.ticket.field3, //error, "Property 'field3' does not exist on type 'ticketModel[]'"
field4: this.ticket.field4, //error, "Property 'field4' does not exist on type 'ticketModel[]'"
});
}
}
}
ticketModel.ts
export class ticketModel {
id: string = '';
field1: string = '';
field2: string = '';
field3: string = '';
field4: string = '';
}
service.ts
export class dataService {
constructor(private errorHandlerService: errorHandlerService, private http: HttpClient) {}
private url = "/api/tickets";
httpOptions:{ headers: HttpHeaders } = {
headers: new HttpHeaders({ "Content-Type": "application/json" }),
};
getById(id: string): Observable<ticketModel[]> {
return this.http
.get<ticketModel[]>(`${this.url}/${id}`, {responseType: "json"})
.pipe(tap((_) => console.log('returned by service: ', JSON.stringify(_))),
catchError(
this.errorHandlerService.handleError<ticketModel[]>("fetchAll", [])
)
);
}
为了以防万一它有用,这是响应的示例 json 当此方法为 运行
时我得到的
[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]
如果没有传入 id,则 isNewMode 为 true 并且表单使用空白值初始化,从那里可以正常工作。当传入一个 id 时,我将其传递给数据服务中的一个方法以查询数据库和 return 只是那一行。这似乎也能正常工作,因为我能够以 json 格式将该数据记录到控制台。 在尝试了几种不同的方法后,我只是想不出如何将数据修补到表单中。
目前,我认为这应该工作的方式就是这段代码的示例,在 patchValue() 中,编译器抛出一个错误“属性 field1 不存在在类型 ticketModel[]" 上,当它在该模型上确实作为 属性 存在时。
我觉得我可能遗漏了一些非常小的东西,如果您能帮助我找出会非常感激的东西,谢谢!
您已将 ticket!: ticketModel[]
声明为数组类型。
你的回复也是一个数组-
[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]
那你为什么不把 this.ticket
当作一个数组呢?
field1: this.ticket.field1,
以这种方式使用它 - field1: this.ticket[0].field1
或对其使用 for 循环以从中获取 field1 和其他值。
并且您需要在订阅块中修改表单,因为它是一个异步操作。
实际上您的服务 getById 应该 return 一个 'TicketModel' 也不是一组 TicketModel。比组件中的管理更好,服务中的管理使用 map
//see that is an observable of "ticketModel"
getById(id: string): Observable<ticketModel> {
//you needn't use {responseType:'json'}, by defect Angular expect a Json
return this.http
.get<ticketModel[]>(`${this.url}/${id}`)
.pipe(
//just return the uniq element of the array
map(res=>res.length?res[0]:null),
tap((_) => console.log('returned by service: ', JSON.stringify(_))),
catchError(
this.errorHandlerService.handleError<ticketModel>("fetchAll", [])
)
);
}
此外,您需要使用“patchValue”inside subcription 函数,并且您可以使用 patchValue 而无需创建新对象,因为具有相同的属性
if(!this.isNewMode){
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe({
next: ticketData => {
this.ticket = ticketData; //<--really you needn't use ticketData
//unless you want compare old and new Values
//here you make the patchValue
this.form.patchValue(ticketData);
})
}
(你也可以输入 ngOnInit 而不是 ngAfterViewInit)
更新 另一种解决“经典”问题的方法,为 edit/create 元素创建组件。
如果你有这样的功能
getForm(data:TicketModel=null){
data=data || {} as TicketModel
return new FormGroup({
id: new FormControl(data.id,Validators.required),
field1: new FormControl(data.field1,Validators.required),
field2: new FormControl(data.field2,Validators.required),
field3: new FormControl(data.field3,Validators.required),
field4: new FormControl(data.field4,Validators.required)
})
}
你可以在 ngOnInit 中做一些像
ngOnInit(){
this.id = this.route.snapshot.params['id'];
this.isNewMode = !this.id;
if (this.isNewMode)
this.Form=this.getForm()
else
{
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe(res=>{
this.Form=this.getForm(res)
})
}
}
我有一个响应式表单,我正在像这样初始化 oninit() 以及我用来从 URL 中获取传递的 id 并判断是否或不是用于更新或在 mysql table 中创建新条目的表单。我遇到的问题是使用 patchValue 将数据 return 从我的服务传递到我的表单中:
component.ts
export class formComponent implements OnInit, AfterViewInit {
constructor(
private dataService: dataService,
private route: ActivatedRoute,
private router: Router,
private formBuilder: FormBuilder,
private ticketModel: ticketModel,
) {}
Form!: FormGroup;
isNewMode!: boolean;
id!: string;
ticket!: ticketModel[];
ngOnInit(){
this.id = this.route.snapshot.params['id'];
this.isNewMode = !this.id;
this.Form = this.formBuilder.group({
field1: ['', Validators.required],
field2: ['', Validators.required],
field3: ['', Validators.required],
field4: ['', Validators.required]
});
}
ngAfterViewInit(){
if(!this.isNewMode){
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe({
next: ticketData => {
this.ticket = ticketData;
},
});
this.Form.patchValue({
field1: this.ticket.field1, //error, "Property 'field1' does not exist on type 'ticketModel[]'"
field2: this.ticket.field2, //error, "Property 'field2' does not exist on type 'ticketModel[]'"
field3: this.ticket.field3, //error, "Property 'field3' does not exist on type 'ticketModel[]'"
field4: this.ticket.field4, //error, "Property 'field4' does not exist on type 'ticketModel[]'"
});
}
}
}
ticketModel.ts
export class ticketModel {
id: string = '';
field1: string = '';
field2: string = '';
field3: string = '';
field4: string = '';
}
service.ts
export class dataService {
constructor(private errorHandlerService: errorHandlerService, private http: HttpClient) {}
private url = "/api/tickets";
httpOptions:{ headers: HttpHeaders } = {
headers: new HttpHeaders({ "Content-Type": "application/json" }),
};
getById(id: string): Observable<ticketModel[]> {
return this.http
.get<ticketModel[]>(`${this.url}/${id}`, {responseType: "json"})
.pipe(tap((_) => console.log('returned by service: ', JSON.stringify(_))),
catchError(
this.errorHandlerService.handleError<ticketModel[]>("fetchAll", [])
)
);
}
为了以防万一它有用,这是响应的示例 json 当此方法为 运行
时我得到的[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]
如果没有传入 id,则 isNewMode 为 true 并且表单使用空白值初始化,从那里可以正常工作。当传入一个 id 时,我将其传递给数据服务中的一个方法以查询数据库和 return 只是那一行。这似乎也能正常工作,因为我能够以 json 格式将该数据记录到控制台。 在尝试了几种不同的方法后,我只是想不出如何将数据修补到表单中。
目前,我认为这应该工作的方式就是这段代码的示例,在 patchValue() 中,编译器抛出一个错误“属性 field1 不存在在类型 ticketModel[]" 上,当它在该模型上确实作为 属性 存在时。
我觉得我可能遗漏了一些非常小的东西,如果您能帮助我找出会非常感激的东西,谢谢!
您已将 ticket!: ticketModel[]
声明为数组类型。
你的回复也是一个数组-
[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]
那你为什么不把 this.ticket
当作一个数组呢?
field1: this.ticket.field1,
以这种方式使用它 - field1: this.ticket[0].field1
或对其使用 for 循环以从中获取 field1 和其他值。
并且您需要在订阅块中修改表单,因为它是一个异步操作。
实际上您的服务 getById 应该 return 一个 'TicketModel' 也不是一组 TicketModel。比组件中的管理更好,服务中的管理使用 map
//see that is an observable of "ticketModel"
getById(id: string): Observable<ticketModel> {
//you needn't use {responseType:'json'}, by defect Angular expect a Json
return this.http
.get<ticketModel[]>(`${this.url}/${id}`)
.pipe(
//just return the uniq element of the array
map(res=>res.length?res[0]:null),
tap((_) => console.log('returned by service: ', JSON.stringify(_))),
catchError(
this.errorHandlerService.handleError<ticketModel>("fetchAll", [])
)
);
}
此外,您需要使用“patchValue”inside subcription 函数,并且您可以使用 patchValue 而无需创建新对象,因为具有相同的属性
if(!this.isNewMode){
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe({
next: ticketData => {
this.ticket = ticketData; //<--really you needn't use ticketData
//unless you want compare old and new Values
//here you make the patchValue
this.form.patchValue(ticketData);
})
}
(你也可以输入 ngOnInit 而不是 ngAfterViewInit)
更新 另一种解决“经典”问题的方法,为 edit/create 元素创建组件。
如果你有这样的功能
getForm(data:TicketModel=null){
data=data || {} as TicketModel
return new FormGroup({
id: new FormControl(data.id,Validators.required),
field1: new FormControl(data.field1,Validators.required),
field2: new FormControl(data.field2,Validators.required),
field3: new FormControl(data.field3,Validators.required),
field4: new FormControl(data.field4,Validators.required)
})
}
你可以在 ngOnInit 中做一些像
ngOnInit(){
this.id = this.route.snapshot.params['id'];
this.isNewMode = !this.id;
if (this.isNewMode)
this.Form=this.getForm()
else
{
this.sub = this.dataService.getById(this.id)
.pipe(first())
.subscribe(res=>{
this.Form=this.getForm(res)
})
}
}