3

I want to write some kind of Broadcast mechanism to dynamically create and use EventEmitters in my user context. To do so I inject the an EmitterService

@Injectable()
export class BroadcastService implements OnInit {
    private _emitters: { [channel: string]: EventEmitter<any> } = {};    
    constructor() {}    
    get(channel: string): EventEmitter<any> {
        if (!this._emitters[channel]) { 
            this._emitters[channel] = new EventEmitter();
        }
        return this._emitters[channel];
    }    
    ngOnInit() { }    
}

My components structure looks like this:

                [root]
                 |  |
           [comp-a][comp-b]

In my root component I inject the BroadcastService to make sure every sub-component uses the same Broadcast Channels. In addition other services like an AuthService are injected (in the root too, that's basically my user context)

@Component({
    provider: [BroadcastService, AuthService]
})

The AuthService (and others) are using the BroadcastService for emit events:

@Injectable()
export class AuthService extends BaseService {      
constructor(
        http: Http,
        @Inject(BroadcastService) private emitterService: BroadcastService)
    {              
        super(http);                 
    }
    ...
    // Example usage of Broadcast
    login(username: string, password: string) {
        // do the login, when successfully:
        this.emitterService.get("online").emit(true);
    }
}

The error I get: EXCEPTION: Cannot resolve all parameters for 'AuthService'(Http, undefined @Inject(undefined)). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'AuthService' is decorated with Injectable.

What I tried:

  1. Putting another root component on top the the current root component to inject the BroadcastService there, same result
  2. Adding/Removing the @Inject to the constructor parameter, no changes
  3. Begging and crying, didn't help

The strangest part is, that this works with one of my other services and doesn't with the rest of them (well, I didn't try all, but those I tried didn't work). Do you have any clues what the problem could be? Or is this maybe just a bug (I'm using angular2 in 2.0.0-beta.2)

letmejustfixthat
  • 3,379
  • 2
  • 15
  • 30

1 Answers1

5

Seems like a forwardRef is required

@Injectable()
export class AuthService extends BaseService {      
constructor(
        http: Http,
        @Inject(forwardRef(()  => BroadcastService)) private emitterService: BroadcastService)
    {              
        super(http);                 
    }
    ...
    // Example usage of Broadcast
    login(username: string, password: string) {
        // do the login, when successfully:
        this.emitterService.get("online").emit(true);
    }
}

could also (or instead of) be necessary in

@Component({
    provider: [BroadcastService, AuthService]
})

depending on how you code is organized. This is not necessary when each class is defined in its own file.

See also Angular 2 error:

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Great, thanks. Do you have a nice guide to understand **what exactly** is going on. I know what a forward reference in general is and why it is needed. But this special case is still a bit of a misteria for me – letmejustfixthat Feb 01 '16 at 06:30
  • 1
    Classes are not hoisted and therefore not yet known where they are used as type. With `forwardRef` the resolution of the type is delayed to later when the whole file content is known. See also http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html and https://angular.io/docs/ts/latest/api/core/forwardRef-function.html – Günter Zöchbauer Feb 01 '16 at 06:34