Attempting to inject an object using an InjectionToken.
In the AppModule I have:
export const tokenConfigKey = new InjectionToken('config');
const tokenBasedConfig = {
provide: tokenConfigKey,
useValue: {
key: 'value'
}
}
And in the AppComponent:
#Component({
selector: 'my-app',
template:`<h1>Hello Angular Lovers!</h1>`
})
export class AppComponent {
constructor(#Inject('config') config,
#Inject(tokenConfigKey) configByToken) {
}
}
This is a complete stacblitz example
Injection using the string key is passing, but injection with the token is failing. Any ideas why?
Article
Here's an article in case anyone wants to play with this
A circular dependency issue could exist due to the fact that the AppModule imports the AppComponent and the AppComponent imports the InjectionToken from the AppModule.
Moving token to separate resolvs the issue:
token.ts
import { InjectionToken } from '#angular/core';
export const BASE_URL = new InjectionToken<string>('BaseUrl');
app.module.ts
#NgModule({
providers: [{ provide: BASE_URL, useValue: { key: 'http://localhost' } }],
app.component.ts
constructor(#Inject(BASE_URL) configByToken) {
console.log(configByToken);
}
Related
How do I Access AppModule imports from Lazy-loaded Modules ?
My Angular10 App imports AngularMaterial and NXTranslate Modules in to the AppModule.
NxTranslate calls an ApiService to get a large Lookup object of thousands of translations.
This is translated at the initial loading of the AppModule.
The App has multiple lazy-loaded routes that also need to use the AnagularMaterial and NXTranslate Modules in their features.
If I use a SharedModule to load the Modules then the ApiService is called multiple times. This is obviously not good.
It should only call the ApiService & AngularMaterial once and be available for all modules.
How do I resolve this? I am struggling.
Thanks.
Update
(sorry for the long post)
This is the NXTranslate implementation - it uses a custom class.
import { environment } from './../../../../environments/environment';
import { OSCITranslateService } from './translate.service';
import { NgModule, Injector } from '#angular/core';
import { CommonModule } from '#angular/common';
import {TranslateLoader, TranslateModule} from '#ngx-translate/core';
import {TranslateHttpLoader} from '#ngx-translate/http-loader';
import {HttpClient, HttpClientModule} from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
export class CustomLoader implements TranslateLoader {
localeResourcesUrl =
`${environment.baseUrl}${environment.apiUrl.localeResources}`;
constructor(private http: HttpClient) {}
getTranslation(lang: string): Observable<any> {
let options;
const uri = `${this.localeResourcesUrl}${options && options.key ?
'/' + options.key : ''}`;
let mapped = this.http.get(uri).pipe(
map((response: any) => {
let localeData = {};
let languageCode = response?.languageVariantCode;
response.resources.forEach(item => {
localeData[item.keyName] = item.keyValue;
});
return localeData;
})
);
return mapped;
}
}
#NgModule({
declarations: [],
imports: [
CommonModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: CustomLoader,
deps: [HttpClient]
}
})
],
exports: [ TranslateModule ]
})
export class NxTranslateModule {
constructor(private http: HttpClient) {
}
}
This is the sharedRootModule that imports the AngularMaterial & NXTranslate
import { SharedModule } from './shared.module';
import { NgModule, ModuleWithProviders } from '#angular/core';
#NgModule({
})
export class SharedRootModule {
static forRoot(): ModuleWithProviders<SharedModule> {
return {
ngModule: SharedModule
};
}
}
In AppModule SharedRootModule is imported
...
#NgModule({
declarations: [
AppComponent
],
imports: [
...
SharedRootModule.forRoot()
],
exports: [
...
SharedRootModule
]
....
Are you concerned about the multiple ApiService instances you might end up with? Provide the ApiService within AppModule only, or even better, use the providedIn property right in your service's decorator so it gets injected at application level. (https://angular.io/api/core/Injectable#providedIn)
I would just use a SharedModule that exports the mentioned lazy loaded modules.
I am new to angular and I am trying to pass data from one component(HomeComponent) to another component(ProfileComponent) after navigation.
I created a shared service(DataService).
I injected the service in both the HomeComponent and ProfileComponent but when I set the value of the message property in HomeComponent and try to retrieve it in the ProfileComponent the value is undefined because the DataService is not the same instance.
The DataService was registered in the AppModule in the providers array so it should be a shared service and always the same instance right?
Thanks in advance
DataService.ts
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class DataService {
message:string;
constructor() { }
}
HomeComponent.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../services/data/data.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(private data:DataService) { }
ngOnInit() {
this.data.message = "hello world";
}
}
ProfileComponent.ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../services/data/data.service';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
private message : string;//undefined
constructor(private data:DataService) { }
ngOnInit() {
this.message = this.data.message;
}
}
AppModule.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DataService } from './services/data/data.service';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
#NgModule({
declarations: [
AppComponent,
HomeComponent,
ProfileComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [DataService],
bootstrap: [AppComponent]
})
export class AppModule { }
I know it's a 2 year question, but Google puts it at the top of search results
Now, Angular docs are clearer about this (or just we can find out easier), it's called "Singleton Services"
The section that explains this "bug" is The ForRoot Pattern and it says:
"If a module defines both providers and declarations (components, directives, pipes), then loading the module in multiple feature modules would duplicate the registration of the service. This could result in multiple service instances and the service would no longer behave as a singleton."
To sum up, if you define this in your services (DataService.ts) the providedIn: root as follows
#Injectable({ providedIn: 'root' })
you need to avoid define the service as a provider on your components or modules.
AppModule.ts
...
imports: [
BrowserModule,
AppRoutingModule
],
providers: [DataService], // This line is the problem
bootstrap: [AppComponent]
....
Hope that helps to somebody and if need more documentation refer to Singleton Services' link
Each time you inject the service to your component, new instance is generated. However in this case i would recommend you to use BehaviorSubject as follows,
#Injectable()
export class SharedService {
private messageSource = new BehaviorSubject<string>("default message");
currentMessage = this.messageSource.asObservable();
constructor() { }
changeMessage(message: string) {
this.messageSource.next(message)
}
STACKBLITZ DEMO
I'm using apollo client for graphql. I set up the client in AppApolloModule that I'm importing in AppModule. I'm making a query in a service which is also imported right in the AppModule. Although the service runs before the AppApolloModule runs and hence apollo is not initialized when the query is made and I get this error
Error: Client has not been defined yet
AppApolloModule
imports ....
export class AppApolloModule {
constructor(
apollo: Apollo,
httpLink: HttpLink,
private userService: UserService
) {
console.log("apollo module")
apollo.create({
link: httpLink.create({ uri: `${environment.apiBase}/graphql?${this.myService.token}`}),
cache: new InMemoryCache()
})
}
}
App Module
import { AppApolloModule } from './app.apollo.module';
import { MyService } from './services/my.service';
export class AppModule {
constructor() {
console.log("app module")
}
}
I don't get the two consoles app module and apollo module, since the service runs first, it doesn't find any initialized apollo app and thus breaks the code.
How can I make apollo run before the service or any services for that matter in an efficient and standard way?
This will solve the issue nicely:
import {NgModule} from '#angular/core';
import {HttpClientModule} from '#angular/common/http';
import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
import {HttpLink, HttpLinkModule} from 'apollo-angular-link-http';
import {InMemoryCache} from 'apollo-cache-inmemory';
export function createApollo(httpLink: HttpLink) {
return {
link: httpLink.create({uri: 'https://api.example.com/graphql'}),
cache: new InMemoryCache(),
};
}
#NgModule({
imports: [HttpClientModule, ApolloModule, HttpLinkModule],
providers: [
{
provide: APOLLO_OPTIONS,
useFactory: createApollo,
deps: [HttpLink],
},
],
})
class AppModule {}
The answer by #wendellmva didn't work for me. What did work was the solution suggested in this repo:
https://github.com/patricknazar/angular-lazy-loading-apollo-client
which is basically to put Apollo initialization in a separate, shared module, and include it in your main app module with forRoot().
I have the same issue an the docs from Apollo helped me. Go to 'https://www.apollographql.com/docs/angular/basics/setup/' or copy this:
import { HttpClientModule } from "#angular/common/http";
import { ApolloModule, APOLLO_OPTIONS } from "apollo-angular";
import { HttpLinkModule, HttpLink } from "apollo-angular-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
#NgModule({
imports: [
BrowserModule,
HttpClientModule,
ApolloModule,
HttpLinkModule
],
providers: [{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
return {
cache: new InMemoryCache(),
link: httpLink.create({
uri: "https://o5x5jzoo7z.sse.codesandbox.io/graphql"
})
}
},
deps: [HttpLink]
}],
})
export class AppModule {}
What worked for me was deleting the .angular folder and serving the application again.
I started learning Angular 4 and got to the part with HTTP Client. Right now I'm trying to make an http call from the component (yes, I know I should transfer it to service, but still)
But for some reason, when I try to inject HttpClient into my Component I get the next error:
Uncaught Error: Can't resolve all parameters for PromocodeComponent:
(?).
Here's the code of my component:
import { Ticket } from '../../classes/Ticket.class'
import { Component, Input } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'promocode',
templateUrl: './promocode.template.html',
styleUrls: ['./promocode.styles.scss']
})
export class PromocodeComponent {
#Input() ticket: Ticket;
state: String = "normal";
promocode: String = "";
constructor(private http: HttpClient) {}
promocodeValidate(event): void{
console.log(this.promocode);
console.log(event);
this.http.get('/promocode/asdasda').subscribe(data => {
console.log(data);
});
}
}
And my app.module.ts:
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { MovieBadgeComponent } from './movie-badge/movie-badge.component';
import { TicketComponent } from './ticket/ticket.component';
import { PromocodeComponent} from './promocode/promocode.component';
import { FormsModule } from '#angular/forms';
import { HttpClientModule } from '#angular/common/http';
#NgModule({
declarations: [
AppComponent,
MovieBadgeComponent,
TicketComponent,
PromocodeComponent
],
imports: [
BrowserModule,
HttpClientModule,
FormsModule
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
It turned out, for me, that I forgot the #Injectable decorator on my service. In earlier angular (>=2), i remember the #Injectable as being optional... Guess not anymore?
Turns out, I missed
"emitDecoratorMetadata": true
in tsconfig
fml...
I got the same error when I ran unit testing (a spec.ts file) with Angular.
The following is what I did:
import { HttpClientModule, HttpClient } from '#angular/common/http';
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ],
imports: [
HttpClientModule
],
schemas: [NO_ERRORS_SCHEMA],
providers: [
HttpClient,
]
})
.compileComponents();
}));
and the error was gone.
I'm writing an AWS security groups module clone with Angular and ngrx. I'm trying to pass a method into the dumb component for creating a new security group.
...
const schemas: any[] = [];
#NgModule({
imports: [
CommonModule
],
declarations: [
SecurityGroupsComponent,
SecurityGroupsTableComponent,
SecurityGroupsListComponent
],
schemas: schemas,
exports: [SecurityGroupsComponent]
})
export class SecurityGroupsModule { }
import { Component } from '#angular/core';
#Component({
selector: 'app-security-groups-table',
templateUrl: './security-groups-table.component.html',
})
export class SecurityGroupsTableComponent {
onCreateSecurityGroup() {
console.log('Create Security Group');
}
}
<app-security-groups-list
[createSecurityGroup]="onCreateSecurityGroup"></app-security-groups-list>
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-security-groups-list',
templateUrl: './security-groups-list.component.html',
})
export class SecurityGroupsListComponent {
#Input()
createSecurityGroup: Function;
selectSecurityGroup(securityGroupId: string) {
this.securityGroupSelected.next(securityGroupId);
}
}
and when the template is rendered I get this exception:
Can't bind to 'createSecurityGroup' since it isn't a known property of 'app-security-groups-list'.
What am I doing wrong?
It seems you have missed to add SecurityGroupsListComponent to NgModule.
Please make sure you do below step before use it:
#NgModule({
imports: [...],
declarations: [SecurityGroupsListComponent]
})
export const DECLARATIONS = [SecurityGroupsComponent,
SecurityGroupsTableComponent,
SecurityGroupsListComponent]
and then
declarations: DECLARATIONS,
schemas: schemas,
exports: DECLARATIONS