简体   繁体   中英

Angular patch observable data to reactive form fields

I've got a reactive form that I'm initializing oninit() like this, along with a couple other properties I'm using to grab the passed id out of the URL and tell whether or not the form is being used to update, or create a new entry in a mysql table. The issue I'm having is in using patchValue to pass the data returned from my service into my form:

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", [])
        )
        );
    }

and just in case it's helpful, this is an example of the response json I'm getting when this method is run

[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]

if there isn't an id that gets passed in, isNewMode is true and the form is initialized with blank values, works fine from there. When an id is passed in, I'm passing that to a method in the data service to query the database and return just that row. This also seems to work fine as I'm able to log that data in json format to the console. I just can't figure out how to get the data to patch into the form after trying this out a few different ways.

Currently, the way that I think this should work which is what this code is an example of, in patchValue() the compiler throws an error that " property field1 does not exist on type ticketModel[] " when it absolutely does exist as a property on that model.

I feel like I'm probably missing something pretty small and any help in figuring out what would be wildly appreciated, thank you!

You have declared ticket:: ticketModel[] an an Array type.

Your response is also an array -

[{"id":18,"field1":"string data","field2":"data is here","field3":"another string goes here","field4":"this is another example string"}]

then why are you not treating this.ticket as an array here?

field1: this.ticket.field1,

Either use it this way - field1: this.ticket[0].field1 or use for loop on it to get the field1 and other values from this.

And you need to patch the form inside the subscribe block, because it's an async operation.

really your service getById should return one 'TicketModel' nor an array of TicketModel. better than mannage in the component, mannage in the service using 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", [])
    )
    );
}

Futhermore, you need use the "patchValue" inside subcription function, and you can use the patchValue wihout create a new object because has the same properties

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

(you can also put in ngOnInit instead of ngAfterViewInit)

Update another aproach to the "clasic" problem to create a component to edit/create an element.

If you has a function like

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

You can in ngOnInit make some like

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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