marcoslin / angularAMD

Facilitate use of RequireJS in AngularJS
http://marcoslin.github.io/angularAMD
MIT License
734 stars 171 forks source link

Issue with angularAMD.route and dynamic controller loading #105

Closed stromnet closed 10 years ago

stromnet commented 10 years ago

Hi,

first of, thanks for your code!

I'm trying to use a dynamically loaded controller in a route, but I cannot get it to work. I've tried different variations on using controller+controllerUrl, only controller (and URL in require.config paths), without any success. The error shown is:

Error: [ng:areq] Argument 'SomepageController' is not a function, got undefined

The app.controller() code is called right before this error shows, verified with console.log in the rjs module.

Using only controllerUri and letting the rjs module return ['deps', function()..] DOES work, but I'd like to use regular app.controller() method instead, to keep to the same syntax for all my controllers, and not rely on special syntax for those loaded via angularAMD.route (it might not always be). And according to the docs, this should work, right?

Some pseudo code (I do not have a full isolated example on my hands unfortunately):

main.js

...
require.config({paths:{.... SomepageController: 'controllers/SomepageController', ...});
...

routes.js

....
when('/somepage', angularAMD.route({
    templateUrl: 'assets/html/partials/somepage.html',
    controller: 'SomepageController'
    /* Alterative tested, same result:
    , controllerUrl: 'controllers/SomepageController'
    */
})
...

controllers/SomepageController.js

define(['app', '...deps'], function(app) {
    app.controller('SomepageController', ['$scope', ..., function($scope){
        ....
    }]);
    // no return value here; I expect SomepageController to be avaialble through normal means
});

Stepping through the angularAMD code below, I noticed that the defer is resolved with undefined (since my module does not return anything). Not sure if this has something to do with the problem?

        resolve['__AAMDCtrl'] = ['$q', '$rootScope', function ($q, $rootScope) { // jshint ignore:line
            var defer = $q.defer();
            require([load_controller], function (ctrl) {
                defer.resolve(ctrl);
                $rootScope.$apply();
            });
            return defer.promise;
        }];

Angular version 1.2.26 angularAMD version: master

I'm not sure if I missunderstand the docs somehow, or if there is a bug, but any help is appreciated!

marcoslin commented 10 years ago

The defer.resolve(ctrl) returning undefined is not a problem because the key here is to:

  1. Load the controller on demand
  2. Resolve the promise

If you are reaching the point of defer.resolve(ctrl), the controller should be loaded and your code should be working. Few things worth checking:

  1. Make sure that SomepageController.js file is indeed loaded using devtools when you get to defer.resolve(ctrl)
  2. Are you using resolve inside angularAMD.route?
  3. Double check that your angularAMD is indeed 0.2.x

If problem persists, see if you can re-create the error using the gist below: https://gist.github.com/marcoslin/df4b741e92b2829eeae8

You can run the gist using: http://bl.ocks.org/marcoslin/df4b741e92b2829eeae8

stromnet commented 10 years ago

Thanks for your quick answer! Unfortunately, I'm sorry to say I wasted your time.. Pure user error.. :)

The issue was that I used app.controller(..) on the plain angular module, instead of the AMD-bootstraped module. I was bootstraping it in a later stage (which was a result of trying to solve another problem).

For reference, before:

app.js

define(['angular', 'angularRoute'], function(angular){
"use strict";
    var app = angular.module(
        'myApp',
        ['ngRoute']
    );
    return app;
});

(SomepageController as above, using plain 'app')

main.js

require(['angularAMD', 'app', 'angular'," routes", "controllers", ...],     function(angularAMD, app, angular) {
    angularAMD.bootstrap(app);
});

.. and after (this working):

app.js

define(['angularAMD', 'angular', 'angularRoute'], function(angularAMD, angular){
"use strict";
    var app = angular.module(
        'myApp',
        ['ngRoute']
    );

    return angularAMD.bootstrap(app);
});

(SomepageController as above, using same app, but now it's wrapped in the AMD bootstrap.)

main.js

require.config({
...
deps: ['angularAMD', 'app', 'angular', "routes", "controllers", ....]

});

And the reason for my previous, odd, setup, was that I was trying to hunt down another problem which only occurred while minified, which turned out to be controllers defined like this:

app.controller('SomeController', function($scope) {

Which does not play very well with minified code... Naturally, one must have ['$scope', function($scope) { Hope it may help anyone else having weird loading issues :)

stromnet commented 10 years ago

An additional note, in case anyone else have the same problem. I had some weird angularAMD related issues when route change was called from code, inside a function running inside a scope.apply():

scope.$apply(function() {
    $location.path(attrs.clickLink);
});

The first time the controller is loaded, it works fine. If loaded a second time however, it failed.

It boiled down to me using an old requirejs version, 2.0.4, which seems to load modules non-async if it was already loaded. With 2.1 this is no problem.

Demo: http://bl.ocks.org/stromnet/ef0bcab589317c7f0fbe/a4547d34515ba7e5b605b4c11755bb344c4f13a1

This uses a directive to support $location.path-changes.

Click on the "View1, via directive". then click to go to Home, and then click "View1, via directive" again. An error will be logged:

Error: [$rootScope:inprog] $digest already in progress

A note on minimum requirejs version might be appropriate.