Updating reactive form fields with patched observable data in Angular

Struggling with initializing a reactive form in my component's onInit() method, as well as handling data from a service to update or create entries in a MySQL table. The issue lies in using patchValue to pass the retrieved data into the 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 no ID is passed, the form is initialized with blank values. When an ID is passed, data is fetched from the database correctly. However, there seems to be an issue with patching the data into the form.

The compiler throws an error that "property field1 does not exist on type ticketModel[]" even though it does. Any guidance would be greatly appreciated, thank you!

Answer №1

You've defined the variable ticket!: ticketModel[] as an Array type.

Your response is structured as an array:

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

So why aren't you accessing this.ticket as an array in this case?

field1: this.ticket.field1,

You should either utilize it like this - field1: this.ticket[0].field1 or use a for loop to retrieve the field1 and other values from it.

Additionally, make sure to patch the form inside the subscribe block, as it's an asynchronous operation.

Answer №2

It is suggested that the getById service should return a single 'TicketModel' instead of an array of TicketModel. It is more efficient to handle this in the service using the map function rather than managing it in the component.

// This function returns an observable of "ticketModel"
getById(id: string): Observable<ticketModel> {
    // No need to specify {responseType:'json'}, Angular by default expects Json
    return this.http
    .get<ticketModel[]>(`${this.url}/${id}`)
    .pipe(
         // Return the only element in the response array
         map(res=>res.length ? res[0] : null),
         tap((_) => console.log('Data returned by service: ', JSON.stringify(_))),
         catchError(
           this.errorHandlerService.handleError<ticketModel>("fetchAll", [])
         )
    );
}

Additionally, use the 'patchValue' method inside the subscription function and you can directly patch the existing object without creating a new one since they have the same properties.

if(!this.isNewMode){
  this.sub = this.dataService.getById(this.id)
  .pipe(first())
  .subscribe({
    next: ticketData => {
    this.ticket = ticketData;
    this.form.patchValue(ticketData);
  })
}

(You may also place this code in ngOnInit instead of ngAfterViewInit)

Update: Another approach to the common problem of creating a component to edit/create an element.

If you have 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 do something like this in 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)
        })
      }
   }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The form will only display results after the "Submit" button is clicked twice

Recently, I built a flask website where the form and results are displayed on the same page. However, there seems to be an issue that arises upon clicking the 'submit' button for the first time after running 'flask run'. The error messa ...

Using Two Unique Typeface Options in Material UI for ReactJS

Currently, in my React App, I am utilizing the Material UI library with Typescript instead of regular Javascript. I've encountered a small hurdle that I can't seem to overcome. The two typefaces I want to incorporate into my app are: Circular-S ...

Using Ionic2, include NavController into Injectable Service

Having an issue with an Injectable Service in Angular2 with Ionic2 framework. Here is how my service is structured: import {Injectable} from '@angular/core'; import {NavController} from 'ionic-angular'; @Injectable() export class Vie ...

Is it true that Safari restricts AJAX Requests following a form submission?

I've developed a JavaScript-based upload progress meter that utilizes the standard multipart submit method instead of submitting files in an iframe. The process involves sending AJAX requests during submission to retrieve the percentage complete of th ...

List of Ionic Cordova plugins sorted by Android version

Seeking guidance on how to determine which versions of Cordova plugins are compatible with Android 5.0.0. When attempting to build with the latest plugins, I encounter errors indicating that they are not supported in this version of Android. For instance: ...

Implement Cross-Origin Resource Sharing in Angular frontend

I am facing an issue with two microfrontends running on different ports (4200 and 4201) where one frontend is unable to access the translation files of the other due to CORS restrictions. To overcome this obstacle, I created a custom loader in my code that ...

Implementing an Ajax function for handling multiple button clicks

So, here's the situation - I've inherited a form with two buttons that inexplicably have the same name and id (seriously, don't ask me why, I didn't design this form haha). I'm attempting to utilize the Ajax .click function to trig ...

The RC-dock library's 'DockLayout' is not compatible with JSX components. The instance type 'DockLayout' is not a valid JSX element and cannot be used as such

Despite encountering similar questions, none of the provided answers seem to address the issue within my codebase. My project utilizes React 17, Mui v5, and TS v4. I attempted to integrate a basic component from an external package called rc-dock. I simply ...

The content of XMLHttpRequest is accessible via the property response

Typically, when we want to retrieve data using AJAX, we would use code like this: var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(xhr.readyState == 4 && xhr.status == 200){ elem.innerHTML = xhr.responseText; ...

Leveraging Font Awesome and Material Icons within ngFor in Angular 2

I have a unique situation where I need to display a dynamic list of icons in a right side bar. These icons are added to the sidebar based on user actions and are displayed using the ngFor directive. The challenge here is that some icons come from Font Awe ...

How do I search for a JSON object in JavaScript?

I have an array containing screen resolutions and I need to find the correct resolution range for the user's viewport (I have the width x height of the current window). Here is the sample JavaScript array with JSON objects: [ {width:100,height:200, ...

How to trigger a function across various controllers in AngularJS

We're in the process of creating an app using phonegap onsen and angularJS. Attempting to invoke a function from a different controller has been challenging. Despite following various documentation such as this Unfortunately, it's not working f ...

Having trouble with the authorization aspect of Next Auth. The error message reads: "NextAuth.js does not support the action api with HTTP GET method

Recently, I've been encountering a puzzling error with my Next.js app authentication. It seems that I am unable to authenticate with the provided credentials. After reviewing the documentation, everything appears to be correct on my end. Additionall ...

Execute a PHP task when the Jquery toggle button is activated, all without causing the page to redirect

I have implemented a toggle button in jQuery to display additional information. It is working well, but I am now trying to process some PHP tasks in the backend without having to redirect the page where the toggle resides. I have been struggling with thi ...

Rearranging Arrays in AngularJS: Ensuring a Specific Element Stays at the

I have exhaustively searched for an answer to my query without success. I hope that my search efforts were sufficient! Here is the issue at hand : <div id="{{expression.comment_id.$id}}" class="comments" ng-repeat="expression in expressions| orderBy:or ...

Avoid activating the panel by pressing the button on the expansion header

I'm facing a problem with the delete button on my expansion panel. Instead of just triggering a dialogue, clicking on the delete button also expands the panel. How can I prevent this from happening? https://i.stack.imgur.com/cc4G0.gif <v-expansion ...

I continue to encounter an error every time I attempt to place an HTML nested div on a separate line

When I structure the HTML like this, I don't encounter any errors: <div class="game-card"><div class="flipped"></div></div> However, if I format it differently, I receive an error message saying - Cannot set property 'vi ...

When using the executeScript() method, it unexpectedly gives a null result instead of allowing me to scroll

The following code snippet is used to scroll horizontally within a web page: WebElement element=driver.findElement(By.xpath("//div[@class='table-wrapper']")); JavascriptExecutor js=(JavascriptExecutor)driver; js.executeScript("arguments[0].scroll ...

Encountering a SyntaxError while implementing lightweight-charts in NextJS

I'm encountering an issue while trying to integrate the lightweight-charts package into my nextjs project. When attempting to utilize the createChart function, I am receiving a syntax error in my Node.js console. ...\lightweight-charts\dist& ...

Which is the better option: utilizing the submit event of the form, or incorporating ajax functionality?

Forms are an essential part of my website design, and I often find myself contemplating whether it's better to submit a form using a standard submit button or utilizing Ajax. Typically, I opt for Ajax to prevent the dreaded issue of form re-submission ...