Troubleshooting: Issue with Angular 2 Injectable Class not functioning properly during parameter creation and initialization

Looking to streamline API data sharing across a sample app, I attempted two versions of a Class creation but encountered issues with one of them:

Version 1 [./apiKeys.ts] - working

import {Injectable} from '@angular/core';
@Injectable()
export class SomeApi{
  key: string;
  url: string;
  constructor(){
    this.key = 'API_KEY';
    this.url = 'API_URL';
  }
}

Version 2 [./apiKeys.ts] - not working

import {Injectable} from '@angular/core';
@Injectable()
export class SomeApi{
  constructor(
    public key:string = 'API_KEY',
    public url:string = 'API_URL'
  ){}
}

The plan is to pass it as a provider at bootstrapping time

./main.ts

import {SomeApi} from './apiKeys.ts'
bootstrap(AppComponent, [SomeApi])

However, upon startup, the following error occurs (for version 2):

ORIGINAL EXCEPTION: No provider for String! (SomeApi -> String)

What might be causing version 2 to fail? Many thanks


EDIT

Following suggestions by Günter Zöchbauer, I consulted various resources and implemented what appears to be the optimal solution in my case. Here's what was done:

./apiKeys.ts

import { OpaqueToken } from '@angular/core';

export interface AppConfig{
  key: string;
  url: string;
}

export const DI_CONFIG: AppConfig = {
  key: 'AIzaSyAjC3U-CbKYm_4sYV90XqJ_Upe8ID9jlxk',
  url: 'https://www.googleapis.com/youtube/v3/search'
}

export let APP_CONFIG = new OpaqueToken('app.config');

./main.ts

import { bootstrap }    from '@angular/platform-browser-dynamic';

import { AppComponent } from './app.component';
import {DI_CONFIG, APP_CONFIG} from './apiKeys';

bootstrap(AppComponent, [{ provide: APP_CONFIG, useValue: DI_CONFIG }]);

./apiService.ts

import {Injectable, Inject} from '@angular/core';
import {APP_CONFIG, AppConfig} from './apiKeys';

@Injectable()
export class ApiService{
  constructor(
    @Inject(APP_CONFIG) private config: AppConfig
  ){}
  // Access properties using this.config.url or this.config.key
}

Sources:

Answer №1

When constructors parameters lack the @Inject(...) annotation, Angular's dependency injection uses the parameter type as a key to search for providers.

In your scenario, both parameters are of type string, making it ambiguous.
Moreover, you have not registered any providers at all.

To resolve this, you must provide an artificial key like

bootstrap(AppComponent, [
  {provide: 'myKey', useValue: 'A'}, 
  {provide: 'myUrl', useValue: 'B'}, 
  SomeApi
])
import {Injectable} from '@angular/core';
@Injectable()
export class SomeApi{
  key: string;
  url: string;
  constructor(){
    @Inject('myKey') this.key = 'API_KEY';
    @Inject('myUrl') this.url = 'API_URL';
  }
}

If you prefer not to provide these values, you can utilize @Optional() like

import {Injectable} from '@angular/core';
@Injectable()
export class SomeApi{
  @Optional() key: string;
  @Optional() url: string;
  constructor(){
    this.key = 'API_KEY';
    this.url = 'API_URL';
  }
}

In this case, DI only injects if a matching provider is found.

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

What is the reason that the protected keyword is not retained for abstract properties in TypeScript?

I'm uncertain whether this issue in TypeScript is a bug or intended functionality. In my Angular project, I have 3 classes - an abstract service, a service that implements the abstract service, and a component that utilizes the implementing service. ...

Issue with cookie deletion persists even after using remove('cookie_name') and removeAll() functions in ngx-cookie framework

Having trouble deleting a cookie using ngx-cookie. This is how I am setting the cookie: setCookie(cookie: string) { this.cookieService.put('userDetails', JSON.stringify(cookie), { domain: 'localhost' }); } Even after trying remove(), ...

Indicate the type of method without transforming it into a property

If I have a method in my application that delegates its functionality to an external library method, and I know the type for that external method (e.g. LibDoStuffMethodType), how can I specify the type for my method MyApp.doStuff() without making it a prop ...

How do I assign a default value to an optional parameter in a derived class in Typescript?

One of my classes is called ClientBase: export class ClientBase { constructor(private uri: string, private httpClient: HttpClient) { } // Contains Various Methods } I have multiple subclasses that are derived from the ClientBase For instance: @I ...

Importing multiple features in Angular

[UPDATE]: Oops, my mind is a bit muddled from fatigue and I've mixed up two ideas which resulted in a rather meaningless question... Let's just blame it on the coffee! :P This may not be a pressing issue but more of a quest for knowledge... ...

Ag-grid allows for the creation of multiple individual detail grids that are connected to a single

I noticed in the ag-grid documentation that all the master/detail examples feature only a single detail grid implementation. Is it possible to incorporate multiple (different) detail grids within one master grid, specifically in one row? ...

Ever since I switched to using Angular4, my CSS has been acting up and no longer seems to be functioning properly

Ever since I switched to Angular 4, my CSS seems to have stopped working. While constructing the basic template for my app, I am encountering some challenges with CSS not being applied correctly to DIVS due to the generated Component DOM element of Angula ...

How do I create a generic function in TypeScript that adjusts its behavior based on the input argument?

Is there a way to create a generic function that can accept generic arguments like Data<string, number>? Take a look at this code snippet: interface Data<T,R> { a:T; c:R; } function foo( data: Data<string, number> ){ return t ...

What is the best way to integrate Next.js with Strapi (or the other way around)?

My goal is to develop an application utilizing Next.js for the frontend, fetching data from a Strapi API hosted on the same server. The plan is to have Strapi handle API and admin routes, while Next.js manages all other routes. I intend to use fetch in Nex ...

Passing data from getServerSideProps to an external component in Next.js using typescript

In my Index.js page, I am using serverSideProps to fetch consumptions data from a mock JSON file and pass it to a component that utilizes DataGrid to display and allow users to modify the values. export const getServerSideProps: GetServerSideProps = async ...

Having trouble connecting Nextjs with ChromaDB?

I am encountering issues while trying to establish a connection with the Chromadb vector database in Nextjs. The objective is to store user-generated content in Chromadb. Below is the code snippet I am utilizing along with its dependencies: Dependencies V ...

What are the steps for utilizing the watch feature in Vue.js with TypeScript?

Currently, I am looking to convert this JavaScript script into TypeScript. However, I require the syntax for watchers. export default { props: ['branch_id'], watch: {} } ...

Deploying an Angular-cli project to Digital Ocean

I am encountering an issue while trying to run my Angular 2 app on Digital Ocean. Despite successfully deploying my app on a cloud server, I am unable to connect to it on the specified port as the server does not respond. This is puzzling me as I have be ...

What is the reason for storing a base64 string as an Object in a MongoDB database?

I am facing an issue with storing a product detail on the mongoDB database. When I try to save it, mongoDB stores a property imageSrc as an object instead of a string. Here is my database This is my angular service And this is my express server request ...

A guide on activating the <b-overlay> component when a child triggers an Axios request in vue.js

Is there a way to automatically activate the Bootstrap-vue overlay when any child element makes a request, such as using axios? I am looking for a solution that will trigger the overlay without manual intervention. <b-overlay> <child> ...

Tips for converting a string array constant into a union type

I have a string array that I want to use to create a new type where the properties correspond to the elements in the array. There are different types of arrays and I have a function that generates different output types based on the input array. const RG ...

Implementing Ahead of Time compilation in Angular 2 alongside lazy loading, all achieved without Angular CLI

Working on a straightforward Angular 2 application without utilizing the Angular CLI. The site functions flawlessly when using the JIT compiler, running eager loaded and lazy loaded modules smoothly. After successfully running the AOT compiler followed by ...

When working with multiple charts on Angular ChartJs, the data may not display properly

My goal is to display multiple Charts in a single page using Angular. I came across an Example that uses ViewChildren: const baseConfig: Chart.ChartConfiguration = { type: 'pie', options: { responsive: true, } }; @ViewChildren('c ...

The build process for the module was unsuccessful due to an error: FileNotFoundError - The specified file or directory does not

Everything was running smoothly in my application until I decided to run npm audit fix. Now, a troubling error keeps popping up: Failed to compile. ./node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/jit-polyfills.js Module build fai ...

The ngx-image-cropper's roundCropper attribute is not functioning correctly as it should. An error is being displayed stating: "Type 'string' is not assignable to type 'boolean'"

<image-cropper [imageChangedEvent]="imageChangedEvent" [maintainAspectRatio]="true" [aspectRatio]="4 / 4" format="jpg" (imageCropped)="imageCropped($event)" roundCropper = "true"> </i ...