Search code examples
angularangular-signals

Angular 16.2.0 Signal TypeError <mySignal> is not a function


I've a service with a signal owning a list of Customers (interface). When I call a method that get the signal content, an issue is raised:

ERROR TypeError: this.customers is not a function at Object.upsertCustomer [as next]

Below the source code:

//interfaces
export interface CustomerProxy {
    id?: number,
    name: string,
    category: string,
    phone?: string,
    mobile?: string,
    email?: string,
}

export interface Customer extends CustomerProxy {
    vat_number?: string,
    address?: string,
}
//declaration
customers = signal<CustomerProxy[]>([])
//API
onUpdateCustomer(customer: Customer): Observable<Customer> {
    console.log('onUpdateCustomer')
    return this.httpClient.put<Customer>(`${this.url}/${customer.id}`, customer)
      .pipe(
        tap(this.upsertCustomer),
        //catchError(this.handleError),
      )
  }
//Inner method
private upsertCustomer(customer: Customer) {
    console.log('upsertCustomer')
    const index = this.customers().findIndex(item => item.id === customer.id) //this line raises the error
    if (index === -1) {
      // Not in list -> add
      this.customers.mutate(customers => [...customers, customer as CustomerProxy])
    } else {
      // Already in the list, update
      this.customers.mutate(customers => customers[index] = customer as CustomerProxy)
    }
  }

I don't understand how to fix the issue.

Dependencies:

"dependencies": {
    "@angular/animations": "^16.2.0",
    "@angular/cdk": "^16.2.3",
    "@angular/common": "^16.2.0",
    "@angular/compiler": "^16.2.0",
    "@angular/core": "^16.2.0",
    "@angular/forms": "^16.2.0",
    "@angular/material": "^16.2.3",
    "@angular/platform-browser": "^16.2.0",
    "@angular/platform-browser-dynamic": "^16.2.0",
    "@angular/router": "^16.2.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  }

Solution

  • this.upsertCustomer is likely undefined in the runtime context.

    Use an arrow function to maintain the this context :

    tap(() => this.upsertCustomer),