4

I would like to have few global variables that are UI related (i.e. open menu state). I decided to put these in $rootScope so they're always accessible.

This is the code I've written:

(function (angular) {

    angular
        .module("App", [])
        .run(["$rootScope", function ($rootScope) {
            angular.extend($rootScope, {
                ui: {
                    menu: false,
                    ...
                }
            });
        }]);

})(angular);

I've deliberately used angular.extend, to make sure that consecutive manipulations to the same object are retained. And I think it's also more clean and safe than adding several properties to any object one by one.

Problem

Upper code doesn't do anything though. When I run my application and check root scope by calling:

$("body").scope().$root.ui

I get undefined until some property within ui gets manipulated by ng-click directives on my application. Only then my ui gets a reference... But it's still not the one I've defined in the run() function but rather angular generated object property that ng-click directive generated as per expression.

What am I doing wrong?

Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
  • JQLite doesn;t support selectors. I am betting that is your problem. Also, have you looked at using Batarang? – Mike Cheel Jan 08 '14 at 01:56
  • 1
    Any reason why you don't want to use an angular service instead of rootscope for these kind of values? Being a singleton, values will persistent across controllers/directives etc. – Darren Reid Jan 08 '14 at 01:58
  • @MikeCheel: I'm loagind full jQuery before Angular, so it uses that instead and that works... – Robert Koritnik Jan 08 '14 at 02:00
  • @Layoric: I'm thinking of creating a `Global` service for these kind of data (state, constants etc.) but I'm not sure how to bind to those values in my markup without any additional code? Using `$rootScope` al lmy `ng-click`, `ng-bind`, `ng-?` work. I'm not sure how would I implement the same using a global service instead? – Robert Koritnik Jan 08 '14 at 02:02
  • @Robert Doesn't hurt to check! – Mike Cheel Jan 08 '14 at 02:05
  • @RobertKoritnik I would avoid putting on global for the need to put more into markup. For menu related situations, I've used $broadcast and $on inside controllers that care. Not the nicest solution, but could be an option. – Darren Reid Jan 08 '14 at 02:23
  • @MikeCheel: Of course. Doesn't hurt to explain either when being asked. :) Thanks for asking anyway. It could be the point in the right direction though. – Robert Koritnik Jan 08 '14 at 02:28
  • @Layoric: I've put it on $rootScope within a single `ui` property to not pollute scope. I have a hidden menu that gets displayed when clicked a particular link (similar to what [medium.com](http://www.medium.com) does with its main menu). I have a toggle with `ng-click="ui.menu = !ui.menu"` and menu with `ng-show="ui.menu"`. I suppose creating a service would make it clean architecture wise, but would require much more code (or at least I don't know how to accomplish it anyway). **How would you go about this scenario?** – Robert Koritnik Jan 08 '14 at 02:38
  • @RobertKoritnik that is very clean, my only dislike is the assignment happening in markup, but that is probably just a personal preference. I would get the 'hidden menu' controller have a property on it's own scope that is assigned only on an event, eg $on('showMenu',...). This does require a lot more code though to get around what is probably just a personal preferences, eg, scope and markup assignments. Nice solution! – Darren Reid Jan 08 '14 at 02:50

1 Answers1

0

Resolved - module got overwritten

Ia managed to resolve it myself. Code contained in the upper question isn't sufficient to show the actual problem in my application. The problem was the way I was loading module requirements and file ordering.

I'm loading files in this order:

app.js
routing.js
service.something.js
...
filter.something.js
...

So I thought to add module requirements to my app module in subsequent files (make ti actually modularized). This would make it simple to either include or exclude particular files without any runtime errors. It would also allow loading additional files dynamically as application runs.

The problem with my code was that I've overridden my original module in subsequent files. When I added all module requirements into app.js everything everything started working.

Possible workaround

I can see that I'm not the only person that would like this kind of functionality. Lasy module requirements loading is possible as per this Google groups' post.

What it does is it creates a new injector function

instanceInjector.loadNewModules = function (mods) {
    forEach(loadModules(mods), function(fn) { instanceInjector.invoke(fn || noop); });
};

that can later be used by individual modules to add themselves as requirement to the main application module by doing this:

$injector.loadNewModules(["Module1", "Module2", ...]);

Although it would be much better (my opinion) if there was an additional function on Module type called requires() so one could do this instead:

angular.module("MainModule").requires(["AdditionalModule", "OtherModule", ...]);

I think that would make it more concise and easy to use and understand.

Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404