6

Need some help with providing an OpaqueToken. Using Angular 2 beta-12. It works fine if provider key is a string, but doesn't work when using OpaqueToken. In Child class, SF is undefined.

Parent Class:

export let SF = new OpaqueToken('sf');

export class A {
  testMsg: string = 'hello';
}

@Component({
  template: `<child></child>`,
  providers: [
    provide(SF, {useValue: A}),
    provide('justString', {useValue: 'hi'}) 
  ]
})
export class App {}

Child class:

import {Component, Injector, Inject, OpaqueToken} from 'angular2/core'
import {SF, A} from './app'
console.log("******", SF); // undefined
@Component({
  selector: 'child',
  template: `
    $$CHILD Rendered$$ {{a}}
  `
})
export class Child {
  //constructor(@Inject(SF) private a: A) {} // doesn't work
  constructor(@Inject('justString') private a: string) {}
}

Exceptions I get:

angular2.min.js:17EXCEPTION: Cannot resolve all parameters for 'Child'(@Inject(undefined)). Make sure that all the parameters are decorated with Inject or have valid type annotations and that 'Child' is decorated with Injectable.

David L
  • 32,885
  • 8
  • 62
  • 93
Elijah
  • 1,252
  • 3
  • 21
  • 32

3 Answers3

9

It's because you have a cyclic dependency between modules that contain parent and child classes.

If you define your opaque token into a third module and include it in the other ones, it will work.

For example a constant module:

export let SF = new OpaqueToken('sf');

And in the two other modules:

import { SF } from './constants';
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
2

Use InjectionToken replace to OpaqueToken in angular 4 or newer.

Because OpaqueToken is deprecated

http://stackoverflow.com/questions/43419050/angular-2-opaquetoken-vs-angular-4-injectiontoken

Pradip
  • 315
  • 1
  • 4
  • 12
1

I assume that your parent and child classes are in different files, and that you're importing Child into the parent file so you can put it in the directives of the Parent decorator (you haven't done this in your example, but otherwise Child wouldn't be instantiated at all, which clearly is not the case).

In that case you need to use a forwardRef on anything defined in the parent class file that's used in the child class file, which has to be loaded first because it is imported into the parent class, and therefore is not privy to anything defined in the subsequently-loaded file (without the workaround that is forwardRef)

So,

import {forwardRef} from "angular2/core"; 
...
constructor(@Inject(forwardRef(()=>SF)) private a: A) {} 

should work where

constructor(@Inject(SF) private a: A) {} // doesn't work

fails.

Sidenote: provide(SF, {useValue: A}), where A is a reference to a class rather than to an instance, smells fishy. I won't speculate about what you're doing, but I will say that it's far more typical to see useValue used with a reference to an instance and useClass used with a reference to a class.

drew moore
  • 31,565
  • 17
  • 75
  • 112
  • Thanks for the help, @drewmoore That does work. Odd that it's never mentioned in any examples i've seen. In fact angular.io reference doc talks about that forwardRef is only needed when declaring a class after component in the same file. Now the injection site looks somewhat unfriendly. Ugh. Do you use OpaqueTokens this way? My use-case now is simply to place global objects into component context. – Elijah Apr 05 '16 at 20:37