Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RequireJS + AngularJS + Module: Dependency issues

I had my angularjs app setup in local and everything was working fine till I upload my code to the staging server. I now have issue with dependencies that are not respected but I can't see why. I guess it was working in local because it was loading the library faster. I now modified my app to try to fix this issue but can't manage to make it work.

My application is loading a single page app, composed of 3 views (main, map and map-data). I'm using AngularJS modules structure to launch this app. Here is my directory structure:

enter image description here

The index.html is pretty basic:

<!DOCTYPE HTML>
<html lang="en-US">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, minimum-scale=1.0" />
        <title>Map Application</title>
        <link rel="icon" sizes="16x16" href="/favicon.ico" />

        <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key={{ map_key }}&sensor=false"></script>
        <script type="text/javascript" src="/js/bower_components/requirejs/require.js"></script>

        <link rel="stylesheet" href="/css/main.css" media="screen" type="text/css">
        <link rel="stylesheet" href="/js/bower_components/bootstrap/dist/css/bootstrap.css">
    </head>
    <body>
        <!-- Content -->
        <div id="content" data-ui-view></div>

        <script>
            // obtain requirejs config
            require(['require', 'js/require-config'], function (require, config) {

                // set cache beater
                config.urlArgs = 'bust=v{{ version }}';

                // update global require config
                window.require.config(config);

                // load app
                require(['main']);
            });
        </script>
    </body>
</html>

Then requirejs-config.js:

if (typeof define !== 'function') {
    // to be able to require file from node
    var define = require('amdefine')(module);
}

define({
    baseUrl: 'js', // Relative to index
    paths: {
        'jquery': 'bower_components/jquery/dist/jquery.min',
        'underscore': 'bower_components/underscore/underscore-min',
        'domReady': 'bower_components/requirejs-domready/domReady',
        'propertyParser': 'bower_components/requirejs-plugins/src/propertyParser',
        'async': 'bower_components/requirejs-plugins/src/async',
        'goog': 'bower_components/requirejs-plugins/src/goog',
        'angular': 'bower_components/angular/angular',
        'ngResource': 'bower_components/angular-resource/angular-resource',
        'ui.router': 'bower_components/angular-ui-router/release/angular-ui-router',
        'angular-google-maps': 'bower_components/angular-google-maps/dist/angular-google-maps',
        'moment': 'bower_components/momentjs/moment',
        'moment-timezone': 'bower_components/moment-timezone/moment-timezone',
        'moment-duration-format': 'bower_components/moment-duration-format/lib/moment-duration-format'
    },
    shim: {
        'angular': {
            exports: 'angular'
        },
        'ngResource': ['angular'],
        'ui.router' : ['angular']
    }
});

Then the main.js:

/**
 * bootstraps angular onto the window.document node
 * NOTE: the ng-app attribute should not be on the index.html when using ng.bootstrap
 */
define([
    'require',
    'angular',
    './app'
], function (require, angular) {
    'use strict';

    /**
     * place operations that need to initialize prior to app start here
     * using the `run` function on the top-level module
     */
    require(['domReady!'], function (document) {
        angular.bootstrap(document, ['app']);
    });
});

Then the app.js:

/**
 * loads sub modules and wraps them up into the main module
 * this should be used for top-level module definitions only
 */
define([
    'angular',
    'ui.router',
    './config',
    './modules/map/index'
], function (ng) {
    'use strict';

    return ng.module('app', [
        'app.constants',
        'app.map',
        'ui.router'
    ]).config(['$urlRouterProvider', function ($urlRouterProvider) {
        $urlRouterProvider.otherwise('/');
    }]);
});

Here you can see that the app.js depends on the ./modules/map/index, where I'm loading all available controllers:

/**
 * Loader, contains list of Controllers module components
 */
define([
    './controllers/mainCtrl',
    './controllers/mapCtrl',
    './controllers/mapDataCtrl'
], function(){});

Each controller are requesting the same kind of module, here is mapDataCtrl.js which is the one that is triggered by /:

/**
 * Map Data controller definition
 *
 * @scope Controllers
 */
define(['./../module', 'moment'], function (controllers, moment) {
    'use strict';

    controllers.controller('MapDataController', ['$scope', 'MapService', function ($scope, MapService)
    {
        var now = moment();

        $scope.data = {};
        $scope.data.last_update = now.valueOf();
        $scope.data.time_range = '<time range>';
        $scope.data.times = [];

        var point = $scope.$parent.map.center;

        MapService.getStatsFromPosition(point.latitude, point.longitude).then(function(data){
            $scope.data.times = data;
        });
    }]);
});

As you can see, the controller is requesting module.js where the states and module name are defined:

/**
 * Attach controllers to this module
 * if you get 'unknown {x}Provider' errors from angular, be sure they are
 * properly referenced in one of the module dependencies in the array.
 * below, you can see we bring in our services and constants modules
 * which avails each controller of, for example, the `config` constants object.
 **/
define([
    'angular',
    'ui.router',
    '../../config',
    'underscore',
    'angular-google-maps',
    './services/MapService'
], function (ng) {
    'use strict';

    return ng.module('app.map', [
        'app.constants',
        'ui.router',
        'angular-google-maps'
    ]).config(['$stateProvider', '$locationProvider', function ($stateProvider, $locationProvider) {
        $stateProvider
            .state('main', {
                templateUrl: '/js/modules/map/views/main.html',
                controller: 'MainController'
            })
            .state('main.map', {
                templateUrl: '/js/modules/map/views/main.map.html',
                controller: 'MapController',
                resolve: {
                    presets: ['MapService', function(MapService){
                        return MapService.getPresets();
                    }],
                    courses: ['MapService', function(MapService){
                        return MapService.getCourses()
                    }]
                }
            })
            .state('main.map.data', {
                url: '/',
                templateUrl: '/js/modules/map/views/main.map.data.html',
                controller: 'MapDataController'
            })
            ;
        //$locationProvider.html5Mode(true);
    }]);
});

It's in this file that I have an issue. I'm trying to load the module angular-google-maps because I need it in my MapCtr controller and most probably in MapDataCtrl. But I get the following message:

Uncaught Error: [$injector:modulerr] Failed to instantiate module app due to:
Error: [$injector:modulerr] Failed to instantiate module app.map due to:
Error: [$injector:modulerr] Failed to instantiate module angular-google-maps due to:
Error: [$inj...<omitted>...1) 

I have no idea what I am missing, for me everything looks tied correctly. What am I missing?


UPDATE 1

I think it's because angular-google-map is not AMD compliant, so I've modified my requirejs-config.js as follow:

if (typeof define !== 'function') {
    // to be able to require file from node
    var define = require('amdefine')(module);
}

define({
    baseUrl: 'js', // Relative to index
    paths: {
        ...
        'underscore': 'bower_components/underscore/underscore-min',
        'angular-google-maps': 'bower_components/angular-google-maps/dist/angular-google-maps',
        ...
    },
    shim: {
        'angular': {
            exports: 'angular'
        },
        'ngResource': ['angular'],
        'ui.router' : ['angular'],

        'angular-google-maps': {
            deps: ["underscore"],
            exports: 'angular-google-maps'
        }
    }
});

but I still have the same issue.

like image 609
maxwell2022 Avatar asked May 01 '14 01:05

maxwell2022


People also ask

What is RequireJS in AngularJS?

RequireJs website defines RequireJS as a JavaScript file and module loader. Using a modular script loader like RequireJS will improve the speed and quality of your code. The files will be loaded asynchrounously when needed, rather than loading all at once.

Should I use require in angular?

No - Don't use require. js OR browserify with Angular. JS there is simply no need to do that - AngularJS has a module system and using another module system above it will make your life unnecessarily hard. I've followed the answers in this thread and wasted too many hours on something that was completely needless.

How to use Require in AngularJS controller?

require ensures the presence of another directive and then includes its controller. ^require checks elements above the current one in addition to the current element. So you have to use the two directives together for this to work. Otherwise, just define a controller with app.


1 Answers

We can use an js library with require.js.There is no need to remove require.js. I still need to check why require.js config not working. Meanwhile you can try this way.

// requirejs-config.js
define({
    baseUrl: 'js', // Relative to index
    paths: {
        'jquery': 'bower_components/jquery/dist/jquery.min',
        'underscore': 'bower_components/underscore/underscore-min',
        'domReady': 'bower_components/requirejs-domready/domReady',
        'propertyParser': 'bower_components/requirejs-plugins/src/propertyParser',
        'async': 'bower_components/requirejs-plugins/src/async',
        'goog': 'bower_components/requirejs-plugins/src/goog',
        'angular': 'bower_components/angular/angular',
        'ngResource': 'bower_components/angular-resource/angular-resource',
        'ui.router': 'bower_components/angular-ui-router/release/angular-ui-router',
        'angular-google-maps': 'bower_components/angular-google-maps/dist/angular-google-maps',
        'moment': 'bower_components/momentjs/moment',
        'moment-timezone': 'bower_components/moment-timezone/moment-timezone'
    },
    shim: {
        'angular': {
            exports: 'angular'
        },
        'ngResource': ['angular'],
        'ui.router' : ['angular']
    }
});
// google-map.js
define(['underscore', 'angular-google-maps'], function(){
});
And then requiring the module each time I need the map:

require(['google-map'], function(){
});

https://github.com/angular-ui/angular-google-maps/issues/390

like image 177
sach Avatar answered Sep 20 '22 09:09

sach