My environment is VS2015 with TypeScript 1.7.3, Angular2 Beta 2, target ECMA 5. I've modified the csproj file to include TypeScriptExperimentalDecorators = true.
I'm working on the Angular2 tutorial and can not get dependency injection working as documented. I can only get DI to work when I include #Inject in the AppComponent class constructor. If I don't include #Inject I receive this error -
EXCEPTION: Cannot resolve all parameters for AppComponent(?). Make sure they all have valid type or annotations.
I did not include the code in this post because its the same as the tutorial. I did try modifying boot.ts as follows:
bootstrap(AppComponent, [HeroService]);
but I still get the error. Has anyone else run into this?
The HeroService is decorated with #Injectable:
import {Hero} from './hero';
import {HEROES} from './mock-heroes';
import {Injectable} from '../node_modules/angular2/core';
#Injectable()
export class HeroService {
heroes: Hero[];
getHeroes() {
return Promise.resolve(HEROES);
}
// See the "Take it slow" appendix
getHeroesSlowly() {
return new Promise<Hero[]>(resolve =>
setTimeout(() => resolve(HEROES), 2000) // 2 seconds
);
}
}
app.component.ts
import {Component, OnInit, Inject} from '../node_modules/angular2/core';
import {Hero} from './hero';
import {HeroDetailComponent} from './hero-detail.component';
import {HeroService} from './hero.service';
#Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<my-hero-detail [hero]="selectedHero"></my-hero-detail>
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="#hero of heroes"
(click)="onSelect(hero)"
[class.selected]="hero === selectedHero">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
`
,directives: [HeroDetailComponent]
,providers: [HeroService]
})
export class AppComponent implements OnInit {
public title = 'Tour of Heroes';
public heroes: Hero[];
public selectedHero: Hero;
constructor(private _heroService: HeroService) {
// constructor( #Inject(HeroService) private _heroService: HeroService) {
}
getHeroes() {
this._heroService.getHeroes().then(heroes => this.heroes = heroes);
}
ngOnInit() {
this.getHeroes();
}
onSelect(hero: Hero) { this.selectedHero = hero; }
}
I solved the problem by adding the following to the csproj file and was able to remove the #Inject from the code -
<TypeScriptEmitDecoratorMetadata>true</TypeScriptEmitDecoratorMetadata>
See here for how to set TS compiler options in Visual Studio
Related
I recently upgrade form Angular 8 to Angular 9 and it appears that nothing can be dependency injected.
I've commented out so much code in my project to something as basic as the following:
// configuration.state.ts
#State<ConfigurationStateModel>({
name: 'ConfigurationStateModel',
defaults: defaultConfiguration,
})
export class ConfigurationState {
constructor(
//private configurationService: ConfigurationService,
private configurationService: HeroService,
) { }
// .. irrelevant code
}
//hero.service.ts
// generated by ng g s heroservice
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class HeroService {
constructor() { }
}
Errors with
Error: Can't resolve all parameters for ConfigurationState: (?).
at getUndecoratedInjectableFactory (core.js:17311)
at injectableDefOrInjectorDefFactory (core.js:17295)
at providerToFactory (core.js:17363)
at providerToRecord (core.js:17345)
at R3Injector.processProvider (core.js:17161)
at core.js:17122
at core.js:1400
at Array.forEach ()
at deepForEach (core.js:1400)
at R3Injector.processInjectorType (core.js:17118)
Apparently with NGXS, all States must be now decorated with the #Injectable().
This does not work:
#Injectable()
#State<ConfigurationStateModel>({
name: 'ConfigurationStateModel',
defaults: defaultConfiguration,
})
export class ConfigurationState {
but adding it after #State does:
#State<ConfigurationStateModel>({
name: 'ConfigurationStateModel',
defaults: defaultConfiguration,
})
#Injectable()
export class ConfigurationState {
I've been trying to troubleshoot a strange problem with angular 2 where it isn't detecting my provider declaration, but nothing is working. I can't even replicate it in plunkr.
I'm using angular 2 rc 3 with router 3.0 alpha.8.
Error message is: ORIGINAL EXCEPTION: No provider for TestService!
app.routes.ts:
import { provideRouter, RouterConfig } from '#angular/router';
import { HomeComponent } from './app/home/home.component';
import { LogInComponent } from './app/log-in/log-in.component';
import { SignUpComponent } from './app/sign-up/sign-up.component';
export const routes: RouterConfig = [
{ path: '', component: HomeComponent },
{ path: 'log-in', component: LogInComponent },
{ path: 'sign-up', component: SignUpComponent }
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];
main.ts:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { enableProdMode } from "#angular/core";
import { AppComponent } from './app/app.component';
import { APP_ROUTER_PROVIDERS } from './app.routes';
// enableProdMode();
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS
])
.catch(error => console.log(error));
app/app.component.ts:
import { Component } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from './shared/test.service';
#Component({
selector: 'my-app',
template: `
<div id="menu">
<a [routerLink]="['/sign-up']"><button>Sign Up</button></a>
</div>
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES],
providers: [TestService]
})
export class AppComponent {
constructor() { }
}
app/sign-up/sign-up.component.ts:
import { Component } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from '../shared/test.service';
#Component({
selector: 'sign-up',
template: `<h1>Sign up!</h1>`,
directives: [ROUTER_DIRECTIVES]
})
export class SignUpComponent {
constructor(private testService: TestService) {
this.testService.test('works?');
}
}
app/shared/test.service.ts:
import { Injectable } from '#angular/core';
#Injectable()
export class TestService {
constructor() { }
test(message: string) {
console.log(message);
}
}
So, I'm providing the testservice in the base component (app.component.ts) because I want all my components to access the same instance. However, when I navigate to sign-up, I get the no provider for testservice error. If I provide the TestService within the sign-up component, this then works:
import { Component, OnInit } from '#angular/core';
import { ROUTER_DIRECTIVES } from '#angular/router';
import { TestService } from '../shared/test.service';
#Component({
selector: 'sign-up',
template: `<h1>Sign up!</h1>`,
directives: [ROUTER_DIRECTIVES],
providers: [TestService]
})
export class SignUpComponent implements OnInit {
constructor(private testService: TestService) { }
ngOnInit() { }
}
However, I need the same instance accessible throughout my app, so how can I inject this at the main component level?
I even tried replicating this app-level service providing with plunkr with the same version of everything, but it doesn't seem to give me the same error...
http://plnkr.co/edit/5bpeHs72NrlyUITCAJim?p=preview
Injecting something on the app level is done in bootstrap:
main.ts:
import { TestService } from '../shared/test.service';
bootstrap(AppComponent, [
APP_ROUTER_PROVIDERS, TestService
])
For me, some references to the "services" folder were "Services". When I made them all "services" (lower case), it worked.
For example:
import {ApiService} from "./Services/api.service";
didn't work, but this worked:
import {ApiService} from "./services/api.service";
To All future readers - and this is correct for angular 2.0.0 rc-4:
make sure that you follow the below folder structure:
root:
index.html
package.json
systemjs.config.js
tsconfig.json (if using TypeScript)
typings.json
app (folder):
- main.js (the root script for your app)
- app.component.js (the root component for the entire app)
This is crucial for the hierarchical injection to properly scope and identify providers.
Also, and this is very important - if still encountering problems and you are using TypeScript or any other transpiled language- delete any artifacts which your transpiler produces for every associated class in the problematic object graph - this, and the OP's answer eventually helped in my case (*.map.js and *.js files deleted and re-transpiled).
It was a configuration issue after all, and a completely elusive one at that.
Per the ang2 style guide, I had my main.ts one folder up from my main app folder, and in systemjs.config I had to declare the main for app as '../main.js'. When I moved the main file to the root app folder and changed the package declaration in systemjs to 'main.js' it worked.
The odd thing is everything else worked, right up until I try to utilize hierarchical dependency injection.
I don't understand When to use #Inject and when to use #Injectable ?
import {Component, Inject, provide} from '#angular/core';
import {Hamburger} from '../services/hamburger';
export class App {
bunType: string;
constructor(#Inject(Hamburger) h) {
this.bunType = h.bun.type;
}
}
And..
import {Injectable} from '#angular/core';
import {Bun} from './bun';
#Injectable()
export class Hamburger {
constructor(public bun: Bun) {
}
}
The #Injectable decorator aims to actually set some metadata about which dependencies to inject into the constructor of the associated class. It's a class decorator that doesn't require parameters. Without this decorator no dependency will be injected...
#Injectable()
export class SomeService {
constructor(private http:Http) {
}
}
The #Inject decorator must be used at the level of constructor parameters to specify metadata regarding elements to inject. Without it, the type of parameters is used (obj:SomeType is equivalent to #Inject(SomeType) obj).
#Injectable()
export class SomeService {
constructor(#Inject(Http) private http:Http, #Inject('sometoken') obj) {
}
}
You must read this difference- #Inject and #Injectable
#Inject()
is a manual mechanism for letting Angular know that a parameter must be injected.
When using TypeScript, #Inject is only needed for injecting primitives.
For eg:
export class AppComponent {
encryption = this.chatWidget.chatSocket.encryption;
constructor(#Inject(ChatWidget) private chatWidget) { }
}
#Injectable()
lets Angular know that a class can be used with the dependency
injector.
For eg:
#Injectable()
export class ChatWidget {
constructor(
public authService: AuthService,
public authWidget: AuthWidget,
public chatSocket: ChatSocket) { }
}
In the above example Angular's injector determines what to inject into ChatWidget's constructor by using type information
My AppComponent sample:
///other imports here
import { ApplyColor } from './../../shared/directives/applycolor';
import { SomeComponent} from './../../components/somecomponent';
#Component({
selector: 'my-app',
directives: [ApplyColor, ROUTER_DIRECTIVES],
providers: [ROUTER_PROVIDERS],
templateUrl: 'myurl.html'
})
#RouteConfig([
//routes here
{ path: '/main', name: 'Main', component: SomeComponent, useAsDefault: true },
])
export class AppComponent {
}
In order to instantiate ApplyColor in SomeComponent
Import ApplyColor
Add to directives: [ApplyColor]
Instantiate with a new keyword
Which is:
import {Component, AfterViewInit, ViewChild} from 'angular2/core';
import { ApplyColor } from './../../shared/directives/applycolor';
#Component({
selector: 'my-selector',
directives: [ApplyColor],
templateUrl: 'app/components/mycomponenturl.html'
})
export class MyComponent implements AfterViewInit {
constructor() { }
ngAfterViewInit() {
var color = new ApplyColor();
color.apply(2);
}
}
How can I instantiate/inject ApplyColor without there 3 steps above?
Directive instances are managed by Angular2. This means that you only need to specify it into the directives attribute. So if ApplyColor is a directive just add it into the directives attribute.
If ApplyColor isn't a directive, you can explicitly instantiate into the provide to the child component using #Input.
In your case, it's a bit particular since you leverage routing. In this case, you need to rely on a shared service. Such service needs to be defined when bootstrapping the application to be able to share a single instance for all components. You can set your instance of ApplyColor into a field of this service. So both component (AppComponent and SomeComponent) can access it.
Define the service
export class SharedService {
color:ApplyColor;
}
Bootstrapping the service
bootstrap(AppComponent, [ SharedService ]);
Set color from AppComponent
#Component({
(...)
})
export class AppComponent {
constructor(private service:SharedService) {
var color = new ApplyColor();
this.service.color = color;
}
}
Get color from SomeComponent
#Component({
(...)
})
export class AppComponent {
constructor(private service:SharedService) {
this.service.color.apply(2);
}
}
I have just started working angular2 but as I can understand:
import ApplyColor => you can't remove that, it required by the compiler to know which class you are referenced to
directives : [ApplyColor] => that means you will use the selector (the one you have defined in applycolor.ts) in the template (app/components/mycomponenturl.html). it is only to know where the component will be in the view.
new ApplyColor => you are creating the object yourself, it is not injected.
To inject your component,
export class MyComponent implements AfterViewInit {
constructor(private color:ApplyColor) { }
ngAfterViewInit() {
this.color.apply(2);
}
}
I hope it helped you ?
I have the following angular2 app with a simple dependency injected and it doesn't work. What am I missing?
Here's the error:
EXCEPTION: Cannot resolve all parameters for 'AppComponent'(?). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'AppComponent' is decorated with Injectable.
and the code:
import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
class DataService {
items: Array<any>;
constructor() {
this.items = [
{ name: 'Christoph Burgdorf' },
{ name: 'Pascal Precht' },
{ name: 'thoughtram' }
];
}
getItems() {
return this.items;
}
}
#Component({
selector: 'app',
providers: [DataService],
template: `
<ul>
<li *ngFor="#item of items">{{item.name}}</li>
</ul>
`
})
class AppComponent {
items: Array<any>;
constructor(dataService: DataService) {
this.items = dataService.getItems();
}
}
bootstrap(AppComponent, []);
Can't reproduce. I added your code to a Plunker
https://plnkr.co/edit/0DTjG5?p=preview
and it seems to work fine with or without #Injectable().
It is suggested to always add #Injectable() to services but it is only required when the service has constructor parameters.
.