Angular 6: TypeError - The function you are trying to use is not recognized as a valid function, even though it should be

I'm currently facing a puzzling issue where I'm encountering the

ERROR TypeError: "_this.device.addKeysToObj is not a function"
. Despite having implemented the function, I can't figure out why it's not functioning properly or callable. This error persists when testing the code on both Firefox and Chrome.

The problematic line causing the error is

this.device.addKeysToObj(this.result.results[0]);

Below is the excerpt from my class:

export class Device {
    id: number;
    deviceID: string;
    name: string;
    location: string;
    deviceType: string;
    subType: string;
    valueNamingMap: Object;

    addKeysToObj(deviceValues: object): void {
        for (let key of Object.keys(deviceValues).map((key) => { return key })) {
            if (!this.valueNamingMap.hasOwnProperty(key)) {
                this.valueNamingMap[key] = '';
            }
        }
        console.log(this, deviceValues);
    }
}

And here is how the function is called:

export class BatterieSensorComponent implements OnInit {
    @Input() device: Device;
    public result: Page<Value> = new Page<Value>();

    //[..]

    ngOnInit() {
      this.valueService.list('', this.device).subscribe(
        res => {
          console.log(this.device);  
          this.result = res;
          if (this.result.count > 0) 
          {
            this.device.addKeysToObj(this.result.results[0]);
          }
        }
      )
    }
}

Update:

Upon logging this.device, we get the following output:

{
    deviceID: "000000001" 
    deviceType: "sensor"    
    id: 5    
    location: "-"
    name: "Batteries"    
    subType: "sensor"    
    valueNamingMap:
      Object { v0: "vehicle battery", v1: "Living area battery" }
    prototype: Object { … } 
}

Extra Information:

An excerpt from the device.service code:

list(url?: string, deviceType?: string, subType?: string): Observable<Page<Device>> {
  if(!url) url = `${this.url}/devices/`;
  if(deviceType) url+= '?deviceType=' + deviceType;
  if(subType) url+= '&subType=' + subType;

  return this.httpClient.get<Page<Device>>(url, { headers: this.headers })
    .pipe(
      catchError(this.handleError('LIST devices', new Page<Device>()))
    );
}

The call in the parent component:

ngOnInit() {
  this.deviceService.list('', 'sensor', ).subscribe(
    res => { 
      this.devices = res.results;
    }
  )
}

Template:

<div class="mdl-grid">
  <div class="mdl-cell mdl-cell--6-col mdl-cell--6-col-tablet" *ngFor="let device of devices">
    <app-batterie-sensor [device]="device"></app-batterie-sensor>
  </div>
</div>

Answer №1

Unique answer

This issue is a common pitfall in Typescript. Even though you define the type of device as Device, it may not actually be an instance of Device. While it may have all the properties of a Device, it lacks the expected methods because it's not truly a Device.

To resolve this, make sure to instantiate an actual Device for each entry in your Page. One way to do this is by utilizing the ngOnInit method in the parent component:

If your Page is an array, consider implementing the following:

ngOnInit() {
  this.deviceService.list('', 'sensor').subscribe(
    res => { 
      this.devices = res.results.map(x => Object.assign(new Device(), x));
    }
  )
}

Further insight

Let's explore a typescript example that delves into how this behavior is independent of Angular. We'll use localStorage to simulate data retrieval from an external source, which applies similarly to HTTP calls.

interface SimpleValue {
    a: number;
    b: string;
}

function loadFromStorage<T>(): T {
    const storedValue = localStorage.getItem('MyKey') as string;
    return JSON.parse(storedValue);
}

const valueToSave: SimpleValue = { a: 1, b: 'b' };
localStorage.setItem('MyKey', JSON.stringify(valueToSave));

const loadedValue = loadFromStorage<SimpleValue>();

console.log(loadedValue);

In TypeScript, interfaces serve as compile-time structures and offer no direct representation in JavaScript. This means that if you misuse an interface, the compiler can't catch it at compile time.

Comparatively, loading a class from an external source differs. Classes transpile to their JavaScript equivalents, persisting beyond compilation. The absence of methods when loading a class instance indicates a loss of behavior due to JSON serialization.

Addressing this requires careful design considerations, demonstrated using a common scenario involving dates in Angular with JSON data.

class SimpleClass {
    constructor(public a: number, public b: string) { }

    printA() {
        console.log(this.a);
    }
}

const valueToSave: SimpleClass = new SimpleClass(1, 'b');
localStorage.setItem('MyKey', JSON.stringify(valueToSave));

const loadedValue = loadFromStorage<SimpleClass>();

// TypeError occurs due to lost methods
loadedValue.printA();

An effective pattern for managing frontend models sourced from flat backend contracts involves explicit mapping strategies within constructors or leveraging tools like Object.assign for flexibility and robustness.

Answer №2

Arriving here may present you with a different issue compared to the solution provided: If you are utilizing Angular's services and happen to overlook adding @Injectable, under Angular Ivy, you will encounter a runtime error similar to this:

ERROR TypeError: ConfigurationServiceImpl.\u0275fac is not a function

The proper resolution is to include @Injectable when defining implementations, for instance:

// be sure to include @Injectable(), or else an error will arise!
@Injectable()
export class ConfigurationServiceImpl implements ConfigurationService {
...
}

@Injectable({
  providedIn: "root",
  useClass: ConfigurationServiceImpl,
})
export abstract class ConfigurationService {
...
}

For more information, refer to Angular 7 TypeError: service.x is not a function.

Answer №3

After experimenting with different approaches, I found two solutions that worked well for me

One method involved wrapping the code in a setTimeout

ngOnInit() {
  setTimeOut({ // START OF SETTIMEOUT
    this.deviceService.list('', 'sensor', ).subscribe(
      res => { 
        this.devices = res.results.map(x => Object.assign(new Device(), x));
      }
    )
  }); // END OF SETTIMEOUT
}

Alternatively

The other solution required adding a condition

ngOnInit() {
  if(typeof this.deviceService.list === 'function'){ // START OF CONDITION
    this.deviceService.list('', 'sensor', ).subscribe(
      res => { 
        this.devices = res.results.map(x => Object.assign(new Device(), x));
      }
    )
  } // END OF CONDITION
}

Answer №4

As mentioned by @UncleDave earlier, the process of mapping values with corresponding names to a Typescript object does not create the expected class object. This can be quite confusing.

Object.assign() can resolve the current issue, but it may not work effectively for nested objects. In such cases, you would need to apply Object.assign() recursively for each nested object, which could become cumbersome if done in multiple instances within your codebase.

An alternative solution is recommended: using class-transformer. With this approach, you can annotate nested fields to instruct the compiler on how to generate the nested objects as required. By using the plainToClass() method, you can map the top-level object and ensure that all underlying fields possess the correct types/objects.

Example

Consider two classes:

class Parent {
    name: string;
    child: Child;

    public getText(): string {
        return 'parent text';
    }
}

class Child{
    name: string;

    public getText(): string {
        return 'child text';
    }
}

In the initial case, direct assignment doesn't function correctly:

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = parentJson; // Compiler allows this due to the type being any.  
// Directly assigning the json structure to 'parent' would result in an error due to missing methods like getText().

console.log(parent.getText()); // Error occurs as expected since parent.getText() is not a valid function

In the second situation utilizing Object.assign():

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = Object.assign(parentJson); 

console.log(parent.getText()); // Works fine
console.log(parent.child.getText()); // Results in an error stating that parent.child.getText() is invalid

To make it function properly, the following steps are necessary:

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = Object.assign(parentJson);
parent.child = Object.assign(parentJson.child);

console.log(parent.getText()); // Works correctly
console.log(parent.child.getText()); // Functions as expected now

In the third scenario employing class-transformer:

Begin by modifying the parent class to define the child mapping:

class Parent {
    name: string;
    @Type(() => Child)
    child: Child;

    public getText(): string {
        return 'parent text';
    }
}

Subsequently, map to the parent object accordingly:

let parentJson: any = {name: 'parent name', child: {name: 'child name'}};
let parent: Parent = plainToClass(Parent, parentJson);

console.log(parent.getText()); // Functions correctly
console.log(parent.child.getText()); // Executes as intended

Answer №5

After watching a YT video, I found the answer to my problem in this post. It mentioned checking if the method of that specific class exists.

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

Nativescript encountered an issue while attempting to generate the application. The module failed to load: app/main.js

I'm currently experimenting with the sample-Groceries application, and after installing NativeScript and angular 2 on two different machines, I encountered the same error message when trying to execute: tns run android --emulator While IOS operations ...

Three.js - implementing a billboard effect that preserves orientation through camera pans

My situation involves a plane geometry that always faces the camera using the following line of code in the update loop: plane.lookAt(camera.position); While I am utilizing OrbitControls to manipulate the camera, the plane successfully maintains its orie ...

Ways to prevent other users from clicking or modifying a particular row

I have a data table in my project that will be accessed by multiple users simultaneously. My requirement is that once a row is selected and edited by one user, it should become unclickable for other users who are also viewing the same page or data table. ...

Cannot locate module: Error: Unable to find the path '../containers/Layout' in '/home/yusquen/Projectss/react-shop/src/components'

https://i.stack.imgur.com/U1097.png description of image goes here Issue with module: Error: The file '../containers/Login' could not be found in the 'react-shop/src/components' directory. ...

Angular subscription and observable continuously fetch information

I'm encountering an issue with utilizing subscriptions and observables Here is my code This is my inventory.service.ts getInventoryList = (page: string, pageSize,size) => { const userLocation = this.authService.getUserLocation(); let que ...

What techniques can be used to resize an image to perfectly fit a square on a webpage?

A challenge on the web page is to display images in a square format of 90 * 90 pixels. However, the sizes of these images are not consistent and may vary from 80*100 to 100*80 or even 90 * 110. The requested solution is to stretch the image as follows: ...

Searching for a name in JSON or array data using jQuery can be accomplished by utilizing various methods and functions available

Having trouble searching data from an array in jQuery. When I input Wayfarer as the value for the src_keyword variable, it returns relevant data. PROBLEM The issue arises when I input Wayfarer Bag as the value for the src_keyword variable. It returns em ...

Unable to launch React Native project on emulator now

Something seems off with my App as it won't start up on my AS Emulator. Everything was running smoothly yesterday, but today it's not working - possibly due to me moving the npm and npm-cache folders, although they are configured correctly with n ...

Tips for ensuring the drop down button remains selected

My goal is to keep the sorting drop-down button selected after clicking on it, instead of resetting back to "All". Below are my HTML, CSS, and jQuery code. You can view the functionality on my website here: jQuery/Javascript: $(document).ready(function($ ...

JavaScript button with an event listener to enable sorting functionality

I am looking to implement a button that will reset all the filters on my page. Any ideas? Currently, I have multiple radio buttons for filtering items based on price, size, and color. My goal is to create a reset button that will remove all filters and r ...

Having trouble with downloading a node module?

I encountered an issue while trying to download the node-sass node module. The error message I received was as follows: To download the node-sass module, use the command: npm install --save-dev node-sass Error Binary has a problem: Error: \?\C: ...

Tips for concealing a chosen alternative from the menu of options when utilizing mat-select

I am currently working with the latest version of mat-select, version 16. I have a requirement where, when a specific option is selected and the select drop-down is clicked again, that selected option should not appear in the options list. Below is the HTM ...

Having trouble loading the linked CSS file in the Jade template

My directory structure is organized as follows: --votingApp app.js node_modules public css mystyle.css views test.jade mixins.jade In the file mixins.jade, I have created some general purpose blocks like 'bo ...

Finding the identifier for resources through excluding external influences

I am currently facing an issue with the full calendar plugin. In my set up, I have 3 resources along with some external events. The problem arises when I try to drop an external event onto the calendar - I want to retrieve the resource id from which the ev ...

Using FIND to search an array object results in an error: Type 'undefined' is not compatible with type ''

I'm currently attempting to search for an element within one array and then assign it to another array object. However, I keep receiving the following error message: Type 'ClosureSummary | undefined' is not assignable to type 'Closure ...

"Enhanced interactivity: Hover effects and selection states on an image map

Hello there, I need assistance with my code. Here it is: <img id="body_image" usemap="#body_map" src="assets/images/body.jpg" alt=""> <map name="body_map"> <area shape="poly" alt="d" href="#body_chart" name="ad" coords="153, 153, 145, 1 ...

Conceal mat-table column when form field is empty

As a newcomer to the world of programming, I am currently tackling a table that includes form fields for filtering purposes. My goal is to dynamically hide or show table columns based on whether a form field has a value or not. In my table.component.ts ...

Navigating redirects in Node.JS using HorsemanJs and PhantomJS

I've recently delved into using horseman.js for web scraping in node.js. However, I'm facing difficulty understanding its working mechanism and finding useful examples online. My primary objective is to log in to a platform and extract specific ...

Ways to access dropdown menu without causing header to move using jQuery

Greetings everyone, I am currently working on a dropdown language selection feature for my website. The issue I am facing is that when I click on the language dropdown in the header, it causes the height of the header to shift. $('.language- ...

Does this information operate on Vue or Node?

I have recently started learning programming and currently working on an HTML project for school. My professor mentioned that we should only use Node.js for this project. However, I am concerned that the function I used below might be Vue instead of Node ...