0

I am creating a game where the first thing that needs to happen is some state is loaded in from an external JSON file - the contents of one of my directives are dependent on this data being available - because of this, I would like to delay applying the directive until after the data has loaded. I have written the following:

window.addEventListener('mythdataLoaded', function (e) { 
// Don't try to create characters until mythdata has loaded
quest.directive('character', function() {
      return {
        restrict: 'A',
        scope: {
          character: '@'
        },
        controller: 'CharacterCtrl',
        templateUrl: 'partials/character.html',
        replace: true,
        link: function(scope, element) {
          $(document).on('click', '#'+scope.character, function () {
              $('#'+scope.character+'-popup').fadeToggle();
          });
        }
      };
   });
});

// Load in myth data
var myth_data;
$.getJSON("js/mythdata_playtest.json", function(json) {
   myth_data = json;
   window.dispatchEvent(new Event('mythdataLoaded'));
});

However, it appears that my directive's link function never runs - I'm thinking this is because angular has already executed the part of it's cycle where directives are compiled/linked by the time this directive gets added. Is there some way to force angular to compile this directive after it is created? I googled around a bit, and some people suggested adding $compile to the link function for similar issues - but the link function is never run, so that doesn't work for this case. Thanks!

Nicole Stein
  • 925
  • 1
  • 9
  • 23
  • 4
    Why the downvote? I'd rather have a comment explaining how I can word my question better than a generic "this sucks" that will prevent me from getting the answer I need. – Nicole Stein May 02 '14 at 20:36

2 Answers2

0

It seems to me it would be better to always configure the directive, to do the JSON call in the directive, and attach logic to the element in the JSON call's success handler. This would, if I understand you correctly, do what you want.

AngularJS is meant as a framework, not a library, so using it in the way you mentioned is not recommended. Exactly as you mentioned, AngularJS does a lot of things for you when it runs. AngularJS, by default, runs on document loaded, and your $.getJSON callback arrives after that. When AngularJS runs it does all its magic with compiling the content and all that.

As a sidenote, it's also more the Angular way to use $http over $.getJSON.

Steve Klösters
  • 9,427
  • 2
  • 42
  • 53
  • Thanks - the thing is, this directive is attached to several elements ("characters" in my game world) - and I have data I want to load once... I don't think the JSON call belongs in the directive. – Nicole Stein May 02 '14 at 20:57
  • The best I could come up with is to do the JSON call in my login controller (not shown), and then have that set a URL in a variable so I could ng-include all of my content (including the characters, which have the directive) after the JSON has already loaded... but this doesn't seem great, because there is plenty of static content in my world that could be loading in the meantime while waiting for the JSON to come back. – Nicole Stein May 02 '14 at 20:58
  • If that's shared logic in an AngularJS system, it belongs in a service. A service is an AngularJS component that can be dependency-injected into directives, controllers, et cetera, and provide functionality or data. See [services](https://docs.angularjs.org/guide/services). – Steve Klösters May 02 '14 at 20:59
  • What belongs in a service? The JSON call? It only needs to happen once. – Nicole Stein May 02 '14 at 21:00
  • Yes, the JSON call, and perhaps the game data that is shared between components. A service is created once, and passed to every component that injects it (in short: a dependency-injectable singleton). It can do that JSON call in its initialisation. – Steve Klösters May 02 '14 at 21:01
  • Perhaps [this post](http://stackoverflow.com/a/15012542/2630455) clarifies some AngularJS concepts, with regards to how you are using it in your post. Especially the part titled: "Don't augment jQuery with AngularJS". – Steve Klösters May 02 '14 at 21:03
0

I think you're thinking about this the wrong way. A major ideology in angular is that you set up declarative elements and let it react to the state of the scope.

What I think you might want to do is pass in what you need through the directive scope, and use other angular built in directives to hide or show your default ("non directive") state until the scope gets set from the controller for example.

Example: You want a box to be hidden until an api call comes back. Your directive sets special styles on your element (not hidden). Instead of delaying to dynamically set your directive, you can pass in a scope var with a default value and use something like ng-show="data.ready" in your directive template to handle the actual dom stuff.

tommybananas
  • 5,718
  • 1
  • 28
  • 48
  • Yes - but in my controller, there are several functions that all depend on this data being available. I was hoping there was a simple way to prevent them all from running (since I don't actually need the directives to be bound to the HTML until after the data is loaded - it's kind of useless without) rather than having to go into each one and giving it a default value. – Nicole Stein May 02 '14 at 21:23