New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Injecting Angular 2 services into Angular 1's $controller #8757

Closed
SonofNun15 opened this Issue May 20, 2016 · 7 comments

Comments

Projects
None yet
2 participants
@SonofNun15

SonofNun15 commented May 20, 2016

Related issue: #6655

Per this related issue, in Angular 1 components the Angular 2 injector is resolved via require and walking the DOM tree.

We unit test all of our Angular 1 controllers completely separate from the DOM for simplicity. We build them using $controller, passing in the necessary bindings. Then we trigger interactions on the controller and assert the results.

Our tests for controllers that inject downgraded Angular 2 components are all failing with Error: [$injector:unpr] Unknown provider: ng2.InjectorProvider <- ng2.Injector <- rlObjectService

After reading the related issue, it appears that this is caused because ngUpgrade can't find the Angular 2 injector by walking the DOM tree because there IS no DOM tree.

Is it possible to still test controllers in isolation like this when ngUpgrade is in play?

I assume we will still need to call bootstrap on the document before running tests?

@SonofNun15 SonofNun15 changed the title from Injecting Angular 2 services into $controller to Injecting Angular 2 services into Angular 1's $controller May 20, 2016

@SonofNun15

This comment has been minimized.

SonofNun15 commented May 20, 2016

To start with, we weren't actually calling bootstrap on the upgrade adapter, which looks necessary. Trying that now.

@yjaaidi

This comment has been minimized.

Contributor

yjaaidi commented May 21, 2016

Hi!

Interesting issue. Actually, you should not use angular mocks' module and inject but the upgradeProvider.

It's not documented yet but there are some clues in angular 2's source code.

1 - Your UpgradeAdapter instance should be a singleton.
2 - You should add your providers to the UpgradeAdapter singleton using UpgradeAdapter.addProvider.
3 - You should start the app using UpgradeAdapter.bootstrap then grab the UpgradeAdapterRef instance given to ready's callback function.
4 - Get the ng1Injector attribute from the UpgradeAdapterRef instance.
5 - Instantiate your service using the injector's synchronuous get method.

Here's a plunker with the solution:
https://embed.plnkr.co/EauYut/

We also published a blog post about angular 2 migration on Wishtack. We will soon be filling the blog with more posts about angular 2 testing.

http://www.blog.wishtack.com/#!AngularJS-vs-Angular-2-Should-I-Stay-or-Should-I-Go/cuhk/573b59710cf233ef713771b2

@SonofNun15

This comment has been minimized.

SonofNun15 commented May 21, 2016

We'll try this out this week.

@SonofNun15

This comment has been minimized.

SonofNun15 commented May 27, 2016

So you are telling me that we can't use $controller when ngUpgrade is in place? Started to implement this solution, but we are calling $controller in hundreds of tests, so changing every test is going to be really hard. Can't we just take the upgradeRef.ng2Injector and give it to the provider somehow since the original error was Unknown provider: ng2.InjectorProvider <- ng2.Injector <- rlObjectService?

@SonofNun15

This comment has been minimized.

SonofNun15 commented May 27, 2016

We tried doing this:

angular.module(testModule, [moduleName])
    .provider('ng2.Injector', function (): angular.IServiceProvider {
        return {
            $get: (): Injector => ng2Injector,
        };
    });

Didn't appear to help at all.

@SonofNun15

This comment has been minimized.

SonofNun15 commented May 27, 2016

ng2Injector was a closure that we set to upgradeRef.ng2Injector in upgradeAdapter.ready()

@SonofNun15

This comment has been minimized.

SonofNun15 commented May 27, 2016

We seem to have gotten by this problem. First, we had to use:

beforeEach(() => {
    angular.mock.module(moduleName)
});

To instantiate the module that is being bootstrapped before each test.

Then we added the ng2Injector to this module using a factory and a closure:

let ng2Injector: Injector = null;

angular.module(moduleName)
    .factory('ng2.Injector', function (): Injector {
        return ng2Injector;
    });

upgrade.bootstrap(document.body, [moduleName])
    .ready((ref: UpgradeAdapterRef) => {
        ng2Injector = ref.ng2Injector;
    });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment