Question

I have an angular app that uses ui-router as follows:

var app = angular.module('app', ['ui.router']).run(function ($rootScope, $state) {
  $rootScope.$state = $state;
});

I have defined states using ui-router:

$stateProvider
  .state('app', {
    abstract: true,
    url: '',
    templateUrl: 'partials/app.html',
    resolve: {
      appState: 'appState'
    },
    controller: 'appCtrl'
  })
  .state('app.state1', {
    ...
  })
  .state('app.state2', {
    ...
    controller: 'state2Ctrl'
  })
  .state('app.state2.detail', {
    ...
    templateUrl: <path to template>
    controller: 'state2DetailsCtrl'
  })
  .state('app.state3', {
    abstract: true,
    ...
  })
  .state('app.state3.tab1', {
    ...
  })
  .state('app.state3.tab2', {
    ...
  })
  .state('app.state4', {
    ...
  })

The appState service is defined as follows:

app.factory('appState', [ '$q', 'dataStore', function ($q, dataStore) {
  var deferred = $q.defer();

  console.log("----- Running appState ------")
  var appState = {
    user: null,
    item1: null,
    item2: null,
    item3: null
  };

  var userPromise = dataStore.getUser();
  userPromise.then( function (userIn) {
    appState.item1 = userIn;
    setItem1();
    updateItem2();
    updateItem3();

    // This resolves deferred.promise to the initialised appState.
    deferred.resolve(appState);
  });

  return deferred.promise;
}]);

I have defined a directive as follows:

app.directive('myDirective', [ 'appState', function (appState) {

  console.log("------- My Directive --------");

  console.log("appState: " + JSON.stringify(appState));

  return function (scope, element, attrs) {

  // Do stuff

  }
}]);

The directive is applied as an attribute in the template for state 'app.state2.detail':

<div ng-hide="id == null" my-directive="data" class="tab-content"></div>

I am able to inject appState into any of my controllers and access the properties in appState. However, when I inject appState into my directive as shown, it is an empty object i.e. {} within the directive and I can't access the properties that should be in it.

In order to get round this problem I have injected appState into my top level controller scope so that it is inherited down to the scope of state2DetailsCtrl. Then I access it in the directive via scope['appState'] and I can then access the appState properties as expected. However, I had thought I would be able to just inject appState into the directive?

Why is appState an empty object {} when I inject it into the directive? How do you inject factory/services into a directive?

Was it helpful?

Solution

Your issue is that you're using a promise for an asynchronous operation but you're treating it like it was a synchronous operation.

When you access appState and it gets created, it's creating a promise that needs to be resolved by the userPromise method. That means anything that relies on appState also needs to wait for that resolution.

Your directive code should be doing this:

app.directive(
  'myDirective',
  ['appState', function (appState) {

    console.log("------- My Directive --------");
    appState.then(function (appStateResolve) {
      console.log("appState: " + JSON.stringify(appStateResolve));
    });

    return function (scope, element, attrs) {
      // Do stuff
    };

  }]
);

That's the typical way you're going to be handling any async operations or anything that's waiting on data to show up.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top