I've encountered a problem with injecting services in angular 5 as in tutorial.
I have a simple service
#Injectable()
export class SimpleService {
...
}
And a simple component:
#Component({...})
export class SimpleComponent {
constructor(private simpleService SimpleService) {}
}
A service is set to providers in module:
#NgModule({
...
providers: [SimpleService]
...
})
export class SimpleModule {}
I see the following error in console:
Error: Can't resolve all parameters for SimpleComponent
However, if I inject the SimpleService with #Inject like
#Inject(SimpleService) private simpleService: SimpleService
the error disappears.
What am I doing wrong?
UPDATE:
I saw several answers where people advise to add emitDecoratorMetadata: true to tsconfig file. But this line is already there
It turned out that the reason for this error was an incorrect import of polyfills.ts. Previously I ejected my cli app and removed polyfills entry point from my webpack config. After that I imported them in my main.ts. But I imported polyfills after I imported AppModule. After I moved import './polyfills.ts' statement to the top of the file the error disappeared.
Another option for fixing this error is moving to AOT compilation:
module: {
rules: [
...
{
"test": /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
"use": [
"#ngtools/webpack"
]
}
]
}
plugins: [
...
new AngularCompilerPlugin({
mainPath: 'main.ts',
platform: PLATFORM.Browser,
hostReplacementPaths: {
'environments/environment.ts': 'environments/environment.ts'
},
sourceMap: true,
tsConfigPath: 'src/app/tsconfig.json',
skipCodeGeneration: false // this line is responsible for AOT
})
]
If you use AOT compilation instead JIT you won't need emitDecoratorMetadata in your tsconfig.
I had a similar issue and this may help others, as this was the closest match I found while searching.
I had an error in my constructor function. I forgot to declare the type of the Service:
I had written:
constructor(
private myService
){}
instead of:
constructor(
private myService: MyService
){}
Related
Current behavior
The documentation states here that:
...we passed the RolesGuard type (instead of an instance), leaving responsibility for instantiation to the framework and enabling dependency injection.
So I'm expecting to "override" a guard with another, via the module's providers.
This doesn't work as expected.
Interestingly enough,
injecting the same service via the controller's constructor does yield the correct service, though.
Input Code
Simple: https://github.com/dima-gusyatiner/nestjs-guards-override/tree/only-app-module
With Another module, using exports: https://github.com/dima-gusyatiner/nestjs-guards-override/tree/master
Controller:
#Controller()
#UseGuards(AuthGuard)
Module:
#Module({
controllers: [AppController],
providers: [
{
provide: AuthGuard,
useClass: AuthOverrideGuard,
}
],
})
Expected behavior
I would expect AuthGuard to never even be constructed.
Instead, both classes are constructed, and AuthGuard is used as the guard.
Environment
- Nest version: 8.0.6
- Node version: 16.8.0
- Platform: Linux
Bug Report
I've also opened a ticket for this:
https://github.com/nestjs/nest/issues/8011
Question
Am I doing something wrong here, or is this a bug?
Anybody can suggest a workaround?
I am not sure I understand it that well either but from what I saw:
With UseGuards, the passed in class is not resolved as provider. Only when you declare the guard as a dependency. Just like in AppController, is it looked up in the provider scope.
Sure the guard's dependencies are resolved as providers within the calling module's scope but it itself is not. Just regular instantiation with dependency injection.
I think that is why both guards are still created, just that AuthOverrideGuard, is registered as a provider.
That is why you get the following output:
[Nest] 77074 - 05/09/2021, 05:01:39 LOG [NestFactory] Starting Nest application...
Construct AuthOverrideGuard
Construct AuthGuard
AuthGuard
AppController AuthOverrideGuard {}
This is why this.guard is correctly resolved as AuthOverrideGuard from the provider scope
To summarize, in my understanding, the docs are inaccurate.
The documentation states here that:
...we passed the RolesGuard type (instead of an instance), leaving responsibility for instantiation to the framework and enabling dependency injection.
This is inaccurate, since RolesGuard won't be instantiated with proper dependency injection. Instead, it will be instantiated using this exact class only.
To achieve proper dependency injection, one should use another class, injected via the guard's constructor. For example:
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { Observable } from 'rxjs';
import { AuthService } from '../services';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(
private readonly auth: AuthService,
) {}
canActivate(context: ExecutionContext) {
return this.auth.canActivate(context);
}
}
You can never provide another AuthGuard, it won't work. But you can provide another AuthService, which this guard uses.
#Module({
controllers: [AppController],
providers: [
{
provide: AuthService,
useClass: AuthServiceOverride,
}
],
})
I'm trying to make a Angular2 app (bootstrapped with angular-cli) work with Rails's ActionCable by integrating this lib on the frontend https://github.com/mwalsher/actioncable-js
I npm installed the lib,
added this to angular-cli-build.js
'actioncable-js/index.js',
and this in system-config.ts:
/** Map relative paths to URLs. */
const map: any = {
'moment': 'vendor/moment/moment.js',
'ng2-bootstrap': 'vendor/ng2-bootstrap',
'lodash': 'vendor/lodash',
'actioncable-js': 'vendor/actioncable-js'
};
/** User packages configuration. */
const packages: any = {
'ng2-bootstrap': {
format: 'cjs',
defaultExtension: 'js',
main: 'ng2-bootstrap.js'
},
'actioncable-js':{
main: 'index.js'
},
'moment':{
format: 'cjs'
},
'lodash':{
main: "lodash.js"
}
};
added this to my component:
import { ActionCable } from 'actioncable-js';
but the build errors with this message:
Cannot find module 'actioncable-js'.
anyone has any idea why?
My guess is typings are missing, but I'm not sure how to fix this.
Rails has since released an official actioncable package here:
https://www.npmjs.com/package/actioncable
No there is no problem with typings. What you are missing is how to use Javascript library in angular 2 typescript application. If you want to use JavaScript library in your TypeScript application, then you need to import the library import 'actioncable-js' and then you have to declare the variable. declare let ActionCable:any This tells typescript we have a global variable ActionCable present in our application. Now you can access it in your angular 2 component implementations and do whatever you want to do. You can read the discussion here.
angular-cli.build.js
vendorNpmFiles: ['actioncable-js/**/*.js']
systemjs.config.js
map:{ 'actioncable-js':'vendor/actioncable-js/dist/action_cable.js'}
package:{'actioncable-js': defaultExtension: 'js'} }
`
app.component.ts
import 'actioncable-js';
declare let ActionCable:any;
#Component({
....
})
export class AppComponent implements OnInit{
constructor(){}
ngOnInIt(){
//can access *ActionCable* object here
}
}
I have problem with injecting singleton service into a directive.
I have service:
#Injectable()
export class AuthService{ ... }
I put it into bootstrapper.
bootstrap(AppComponent, [AuthService, ...]);
I made directive, that protects my component:
#Directive({
selector: '[protected]'
})
export class ProtectedDirective {
constructor(private authService:AuthService) { ... }
}
... and added to one of components
#Component({
selector: 'dashboard',
directives: [ProtectedDirective],
template: '<div protected></div',
})
export class DashboardCmp { }
In console i see an error:
ORIGINAL EXCEPTION: No provider for AuthService!
If I add a provider to DashboardCmp, everything works fine, but it's not a singleton service. I set its properties in other component and I don't see them when I'm in directive.
I resolved my problem. Everything was fine but
import {AuthService} from '../services/auth.service'; (in protected.directive.ts)
is not equal to
import {AuthService} from '../Services/auth.service'; ( in main.ts)
Yes, it's stupid, but it made the dependency injection impossible.
I have a NavBar Component which loads the QApi Service, the QApi Service loads the UserService, but I get the following error:
EXCEPTION: No provider for UserService! (NavBarComponent -> QApi -> UserService)
Either I simply don't get the concept of dependency injection, I made a stupid error, or this is just way to complicated compared to native development... Thanks for your help.
Here my code:
UserService:
import {Injectable} from 'angular2/core';
//import {User} from '../data-source-mocks/users';
#Injectable()
export class UserService {
public isAuthenticated = true;
}
QApi Service:
import {Injectable} from 'angular2/core';
import {UserService} from '../user/user.service';
#Injectable()
export class QApi {
constructor(private _userService: UserService) {}
}
NavBar Component:
import {Component} from 'angular2/core';
import {QApi} from '../../services/q-api/q-api';
#Component({
selector: 'nav-bar',
template: `Test NavBar`,
providers: [QApi]
})
export class NavBarComponent {
private _isAuthenticated = false;
constructor(private _QApi: QApi) {}
}
EDIT:
First of all: Thanks for alle the great answers each and every single one helped me to understand dependency injection better, especially this article: https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html
I changed my QApi class to this:
import {Injectable, Inject, Injector} from 'angular2/core';
import {UserService} from '../user/user.service';
import {CardService} from '../card/card.service';
#Injectable()
export class QApi {
constructor() {
var _injector = Injector.resolveAndCreate([UserService,
CardService]);
this.userService = _injector.get(UserService);
this.cardService = _injector.get(CardService);
}
}
Now it works like I hoped it would. Cant thank you guys enough!!
Add UserService to the component providers:
#Component({
selector: 'nav-bar',
template: `Test NavBar`,
providers: [QApi, UserService] // <- add UserService here
})
export class NavBarComponent { /* ... */ }
Here are two good articles to better understand Angular2 Dependency Injection:
blog.thoughtram.io: Dependency Injection in Angular2
blog.thoughtram.io: Injecting services in services in Angular 2
In fact both previous responses are true! ;-)
You need to define the services:
Application level. Within the second parameter of the bootstrap function. It contains the list of the providers that are available for the whole application.
bootstrap(App, [UserService, QApi, ...]);
Component level. Within the providers attribute of the Component annotation. In this case, this is only configured for this component and you need to define this for each component where the QApi service.
#Component({
selector: 'nav-bar',
template: `Test NavBar`,
providers: [QApi, UserService]
})
You also mix things. I mean you can put the UserService provider at the application level and QApi at the component level. In fact what is important is that Angular can find providers for all the involved elements in the processing chaining (with dependency injection). They can come from either component level (1st) or application level (2nd).
Hope that it gives you some additional hints following alexpods and MichaelOryl great answers ;-)
Thierry
List the services in your bootstrap call (wherever you are handling that). Something like the following should work:
bootstrap(App, [UserService, QApi, COMMON_DIRECTIVES, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, HTTP_PROVIDERS]);
providers// directives added here are available to all children
Then you will have a single instance of each of those services available to the rest of your application.
I'm messing around with Angular2 and I'm wanting the ability to inject one component into another based on the bootstrapped bindings.
class HelloComponent {
name: string;
}
#Component({
selector: 'hello'
}
#View({
template: `<h3>Hello {{ name }}</h3>`
})
class HelloBobComponent extends HelloComponent {
constructor() {
this.name = 'Bob';
}
}
#Component({
selector: 'app'
}
#View({
directives: [HelloComponent]
template: `<h1>Welcome to my Angular2 app</h1>
<hello></hello>`
}
class AppComponent {
}
bootstrap(AppComponent, [
bind(HelloComponent).toClass(HelloBobComponent)
]);
Here I'm using HelloComponent as a token that I want Angular2's Injector to resolve HelloBobComponent. I'm doing this so that I can swap components in and out based on the current app configuration. The above example obviously doesn't work. Is this possible using one of the frameworks decorators? I haven't found an answer yet digging though blogs or the source.
edit: To clarify, how do I get the directives property on the View decorator to treat HelloComponent as a di token instead of a type.
This is currently not supported as of alpha37. The compiler resolves directives passed in the View decorator by either type or binding but does not look up from the parent injector.
For example:
#View({
url: '...',
directives: [
Directive1,
bind(Directive2).toClass(Directive2Impl),
]
})
The intention for the "directives" property here was only to prevent selector naming collision. Later bind support was added to aid in testing.
The only solution I can think of without editing the compiler function would be to maintain an external Injector and resolve types on component declaration.