CJ Dennis
CJ Dennis

Reputation: 4356

What is causing a dependency injection failure in an AngularJS directive?

I'm getting a dependency injection error with the code below. I'm not sure why, because rosterStateHelper works in other files, including the one I am copying some code from.

angular.module("HmComponents")
.directive(
    "dhtmlxScheduler", [
        "$rootScope",
        "ModalService",
        "rosterStateHelper",    // <- This line is causing an error
        function (
            $rootScope,
            modal_service,
            rosterStateHelper   // <- This line is causing an error
        ) {

I've found this question which appears to be the same issue as mine: Dependency Injection Strict Mode (ng-strict-di) raises error even when array notation is used but I don't understand the accepted (and only) answer. Is it saying that there is an error on a different usage of rosterStateHelper? If that is the case, why does removing the two lines I've marked stop the error from appearing in the console?

There's something different about my new usage from the others, but I can't work out what it is and how to fix it. The only difference I can see is that the other usages are all in controllers, whereas mine is in a directive.

The error is:

rosterStateHelper ... is not using explicit annotation and cannot be invoked in strict mode

which is the more specific version of https://docs.angularjs.org/error/$injector/strictdi

At Phil's request, here is the full link using the non-minified library.

InitRosterHelper.ts (see updated version below)

/// <reference path="../../../../../js/lib/typeDefinitions/angular.d.ts" />
/// <reference path="RosterModel.ts" />
/// <reference path="RosterStateHelper.ts" />

import RosterModel = Roster.RosterModel;
import RosterStateHelper = Roster.RosterStateHelper;

angular.module('RosterHelpers', ['ngRoute'])
    .factory('rosterDataModel', ($http) => {
        return new RosterModel($http);
    })
    .factory('rosterStateHelper', ($location) => {
        return new RosterStateHelper($location);
    })
;

RosterStateHelper.ts

/// <reference path="../../../../../js/lib/typeDefinitions/angular.d.ts" />

module Roster {
    import ILocationService = angular.ILocationService;

    export class RosterStateHelper {
        private internalState = {};
        private $location: ILocationService;

        constructor($location) {
            this.$location = $location;
        }

        /**
         * Get the key from internal store
         * @param key
         * @returns {*}
         */
        getInternalState(key) {
            return this.internalState[key];
        }

        /**
         * Save the value for key in internal store.
         * Used to move values between separate controllers
         * @param key
         * @param value
         */
        keepInternalState(key, value) {
            this.internalState[key] = value;
        }

        /**
         * Persist key/values in the page URL.
         * @param key
         * @param value
         */
        keepState(key, value) {
            this.$location.search(key, value);
        }

        /**
         * Retrieve key/values from the page URL
         * @param key
         * @returns {*}
         */
        getState(key) {
            let state = this.$location.search();
            if (state[key] === 'false') {
                return false;
            }
            return state[key];
        }
    }
}

Updated InitRosterHelper.ts - still the same error:

/// <reference path="../../../../../js/lib/typeDefinitions/angular.d.ts" />
/// <reference path="RosterModel.ts" />
/// <reference path="RosterStateHelper.ts" />

import IHttpService = angular.IHttpService;
import ILocationService = angular.ILocationService;
import RosterModel = Roster.RosterModel;
import RosterStateHelper = Roster.RosterStateHelper;

angular.module('RosterHelpers', ['ngRoute'])
    .factory('rosterDataModel', ($http: IHttpService) => {
        return new RosterModel($http);
    })
    .factory('rosterStateHelper', ($location: ILocationService) => {
        return new RosterStateHelper($location);
    })
;

Upvotes: 3

Views: 242

Answers (1)

Claies
Claies

Reputation: 22323

This issue derives from the optional ng-strict-di annotation. ng-strict-di can be added to the ng-app element in your HTML, to trap potential errors that might occur due to Dependency Injection errors caused from JavaScript Minification. When ng-strict-di is enabled, any components that are not declared using minification safe methods will not be injected, and this error will be generated instead.

In your specific case, we are dealing with the line

.factory('rosterStateHelper', ($location) => { 

The .factory 'routerStateHelper' has a dependency on $location, which angular will need to find at runtime. Since the injection is supplied at runtime, TypeScript types don't provide enough information to know where this will be supplied from. Therefore, additional details must be provided, and this is what the array notation is used for:

.factory('rosterStateHelper',['location', ($location) => {

As an alternative, you could use a variation on the declaration:

.factory('rosterStateHelper', ($location) => {
    var helper = new RosterStateHelper($location);
    helper.$inject = [$location];
    return helper;
})

Upvotes: 2

Related Questions