What is the correct way to transfer a function from a component.ts file to a service file?

I recently wrote a function in my component.ts file that sends a PUT request. It was working perfectly fine, but I decided to refactor and move the function to a service.ts file. After moving it, the functionality seems to have broken. Whenever I click on the "Save" button, an error is displayed in the console:

FormsComponent.html:32 ERROR TypeError: _co.editForm is not a function at Object.eval [as handleEvent] (FormsComponent.html:32) at handleEvent (core.js:23008) at callWithDebugContext (core.js:24078) at Object.debugHandleEvent [as handleEvent] (core.js:23805) at dispatchEvent (core.js:20457) at core.js:20904 at HTMLButtonElement. (platform-browser.js:993) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423) at Object.onInvokeTask (core.js:17279) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:422) View_FormsComponent_0 @ FormsComponent.html:32 proxyClass @ compiler.js:18234 push../node_modules/@angular/core/fesm5/core.js.DebugContext_.logError @ core.js:24040 push../node_modules/@angular/core/fesm5/core.js.ErrorHandler.handleError @ core.js:15761 dispatchEvent @ core.js:20461 (anonymous) @ core.js:20904 (anonymous) @ platform-browser.js:993 push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:423 onInvokeTask @ core.js:17279 push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:422 push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:195 push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:498 invokeTask @ zone.js:1744 globalZoneAwareCallback @ zone.js:1770 FormsComponent.html:32 ERROR CONTEXT DebugContext_ {view: {…}, nodeIndex: 31, nodeDef: {…}, elDef: {…}, elView: {…}}

The following is my forms.component.ts file:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { FormService } from './forms.service';
import { form } from './form-interface';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-forms',
  templateUrl: './forms.component.html',
  styleUrls: ['./forms.component.css']
})


export class FormsComponent implements OnInit {

  formsUrl = "https://jsonplaceholder.typicode.com/posts";
  id: any;
  form: form = {
    id: 0,
    userId: 0,
    title: '',
    body: ''
  };

  constructor(private formService: FormService ,private route: ActivatedRoute,
    private router: Router, private http: HttpClient) { }

  ngOnInit() {
    this.id=this.route.snapshot.params['id'];

    this.formService.getForms(this.id).subscribe(
      (forms: any) => {
        this.form = forms[0];
        console.log(this.form);
      }
    );

  }

  deleteForm() {
    this.http.delete(this.formsUrl + "/" + this.form.id)
    .subscribe(
      data  => {
      console.log("DELETE Request is successful ", data);
      },
      error  => {
      console.log("Error", error);
      }
    );
  }

}

Below is my forms.service.ts file:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { form } from './form-interface';



@Injectable({
    providedIn: 'root'
}) 

export class FormService {

formsUrl = "https://jsonplaceholder.typicode.com/posts";
form: form = {
    id: 0,
    userId: 0,
    title: '',
    body: ''
  };


    constructor(private http: HttpClient) { }

    getForms(id) {
        return this.http.get('https://jsonplaceholder.typicode.com/posts'
        + "?id=" + id)
      }

      editForm() {
        fetch(this.formsUrl + "/" + this.form.id, {
          method: 'PUT',
          body: JSON.stringify(this.form),
          headers: {
            "Content-type": "application/json; charset=UTF-8"
          }
        })
        .then(response => response.json())
        .then(json => console.log(json));
      }


}

Lastly, here is my HTML file:

<div class="forms container">
  <form #postForm="ngForm">
      <div class="form-group">
          <label for="title">Title</label>
          <input [(ngModel)]="form.title"
          name="title"  
          id="title" 
          type="text" 
          class="form-control"
          >
      </div>
      <div class="form-group">
        <label for="body">Body</label>
        <textarea [(ngModel)]="form.body" 
        name= "body" 
        id="body" 
        cols="30" 
        rows="10" 
        class="form-control"
        ></textarea>
      </div>
      <button class="btn btn-success" (click) = editForm()>Save</button>
      <button class="btn btn-danger pull-right" (click) = deleteForm()>Delete</button>
  </form>
</div>

Answer №1

If you're aiming to maintain cleanliness and organization in your code, consider implementing something along these lines:

export class FormsComponent implements OnInit {
  form = this.formService.getForm();

  constructor(private formService: FormService, private route: ActivatedRoute,
    private router: Router) { }

  ngOnInit() {
    this.id = this.route.snapshot.params['id'];

    this.formService.loadFormById(this.id)
        .subscribe(([form]) => this.formService.updateForm(form));
  }

  saveForm() {
    this.formService.saveForm();
  }

  deleteForm() {
    this.formService.deleteForm();
  }

}

As for the Service:

export class FormService {

    formsUrl = "https://jsonplaceholder.typicode.com/posts";
    form: form = {
       id: 0,
       userId: 0,
       title: '',
       body: ''
    };


    constructor(private http: HttpClient) { }

    loadFormById(id) {
       // Load the specific form here
    }

    updateForm(form) {
       // Update the form details here
    }

    saveForm() {
       // Save the form data here
    }

    deleteForm() {
       // Delete the form as required
    }
}

Answer №2

If you find yourself trying to call the editForm() function in the template but it does not exist in your component, there is a simple solution. Rather than attempting to call the function directly from the template, you can invoke it from the service using formService.editForm().

To successfully utilize this approach, ensure that the formService variable is made public, enabling you to access it throughout your component. Update private formService: FormService to public formService: FormService, allowing for seamless functionality.

An alternative method involves duplicating the function within the component itself, eliminating the need to modify the template or alter the service visibility. Define a new public function like

public editForm = formService.editForm
for convenient access.

Alternatively, you can explicitly define the editForm() function within the component as shown below:

editForm() {
     return this.formService.editForm();
}

Answer №3

it appears there is a parsing error. Please correct the HTML errors:

add (click) = "editForm()" and (click) = "deleteForm()"

<div class="forms container">
  <form #postForm="ngForm">
      <div class="form-group">
          <label for="title">Title</label>
          <input [(ngModel)]="form.title"
          name="title"  
          id="title" 
          type="text" 
          class="form-control"
          >
      </div>
      <div class="form-group">
        <label for="body">Body</label>
        <textarea [(ngModel)]="form.body" 
        name= "body" 
        id="body" 
        cols="30" 
        rows="10" 
        class="form-control"
        ></textarea>
      </div>
      <button class="btn btn-success" (click) = "editForm()">Save</button>
      <button class="btn btn-danger pull-right" (click) = "deleteForm()">Delete</button>
  </form>
</div>

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

Conceal a div element dynamically when clicking on another div

<script type="text/javascript"> function displayBookInfo(str) { if (str == "") { document.getElementById("more-info").innerHTML=""; return; } if (window.XMLHttpRequest) { // code for m ...

Let's update the VUE app's development server to point to my-testing-web-address.com instead of the default localhost:

I am working on a VUE application and I am trying to run it on an external link instead of localhost. I attempted to configure a vue.config.js devServer: { host: 'http://my-testing-web-address.com', port: 8080, ... } and adjusted t ...

You are unable to call a function that doesn't have a proper call signature when dynamically defined, yet surprisingly it still functions as intended

Being new to TypeScript, I'm still learning the ropes, so please bear with me if I make mistakes in using this technology! The challenge I'm grappling with involves creating a flexible way to structure my application errors while allowing users ...

Angular allows for updating all preexisting values in an array by pushing elements into the array within a loop

I found an interesting problem while working on my code. I am trying to push the dates of the week into an array. Upon reviewing the code, everything seems fine until the // good value line where the array has the correct value. However, each time the co ...

Incorporating additional ES6 modules during the development process

As I build a React component, I find that it relies on an ES6 component that I'm currently developing. Since I created the latter first, what is the typical method to include it during development as I work on the second component? If the dependency w ...

The name '__DEV__' is not discoverable at the moment

While working with the mobx library in my project, I encountered an issue after installing it using npm. Upon exploring the mobx/src/error.ts file within the node_modules folder, I came across a compile time error on line 78: const errors: typeof niceError ...

Unable to modify headers after they have already been sent to the client - an error has occurred

I am encountering an issue when trying to redirect the page to another page. Everything works fine with the first post request, but starting from the second one, I keep getting this error message: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after t ...

How come it functions with $scope but fails with `this`?

I am trying to figure out why my code only works with scripts 1 and 3, but not with script 2. I need this functionality for my project because I can't use $scope. Thank you!! <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19 ...

Tips on how to modify a select option in vue based on the value selected in another select

My Web Api has methods that load the first select and then dynamically load the options for the second select, with a parameter passed from the first selection. The URL structure for the second select is as follows: http://localhost:58209/api/Tecnico/Tanq ...

Highchart tip: How to create a scrollable chart with only one series and update the x-axis variable through drilldown

Before I pose my question, here is a link to my jsfiddle demo: http://jsfiddle.net/woon123/9155d4z6/1/ $(document).ready(function () { $('#deal_venue_chart').highcharts({ chart: { type: 'column' ...

Using Jquery Functions within Codeigniter

I am facing an issue with calling a function containing jQuery script within an if-else statement. Below is the codeignitor view code: Code if($osoption == "windows") { ?> <script> windows(); </script> <? ...

Tips for optimizing file sizes in an Angular 11 app for production deployment

Currently, I am troubleshooting an issue with my application where some of my production files are taking a long time to load. I have already deployed my application and tried using the --aot and optimizer commands during the production build process. Here ...

What is the best way to divide an array into pairs and store them in separate arrays?

I'm attempting to challenge my JavaScript skills and faced with a dilemma. There is an array containing data var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];. The goal is to pair the elements and generate a new array of arrays such as var newArray = [[1 ...

Discover the steps to transform the elements within an array to a particular structure

I am working with an API that has the following structure: [ { "id": 4, "code": "IN", "name": "India", "recordVersion": "6CB5C760-E145-4FFA-AC63-FAF5F36B6C80", "updatedBy": "Sini " }, { "id": 59, "code": ...

Font family 'anticon' is not recognized

While following a coding tutorial on YouTube, I encountered an error message that has me stumped. Despite having the correct import statement and dependency installed, the issue persists. Error message in iOS simulator: https://i.stack.imgur.com/LOVCQl. ...

Utilize AngularFire to generate a new User and invoke the factory function to showcase notifications

I recently started working with AngularJS and wanted to integrate it with AngularFire/Firebase for storing project data. I created a factory that manages notifications to be displayed in a centralized location. Here is the structure of my factory: myApp.f ...

My goal is to generate a collection of fullwidth ion-cards using data from an array of objects

I am attempting to iterate through an array of objects in order to generate a set of cards, either FULLWIDTH or halfwidth with the last one centered if the count is an odd number. I have created a component that contains the card layout and I am using *ng ...

Having trouble updating the ASP Dropdown that has list items added through a jQuery AJAX call? Keep receiving the error message "Invalid postback or callback"?

I encountered a simple problem that I ended up complicating. In my web application, I have an ASP Dropdown that I want to populate with values from the database. To achieve this, I used jquery and Ajax to dynamically add list items to the dropdown successf ...

Adjusting a Vue Slider with a changing value

Currently, I am utilizing the Vue Slider component from this source. My goal is to enhance the functionality of the slider by increasing it to the next index and beyond. The issue lies in the fact that the current implementation increments by 1, as the val ...

When trying to upload a file through input using WebDriver, the process gets stuck once the sendKeys

WebElement uploadInput = browser.findElementByXPath("[correct_identifier]"); uploadInput.sendKeys(elementPath); The script successfully initiates the file upload process, however, the custom JavaScript loading screen persists and fails to disappear as exp ...