Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncaught TypeError: angular.module is not a function #2049

Closed
ace-han opened this issue Feb 15, 2016 · 19 comments
Closed

Uncaught TypeError: angular.module is not a function #2049

ace-han opened this issue Feb 15, 2016 · 19 comments
Labels

Comments

@ace-han
Copy link

ace-han commented Feb 15, 2016

My webpack.config.js as below

var path = require('path')
, webpack = require('webpack')

module.exports = {
    context : __dirname,

    resolve : {
        modulesDirectories : [ 'node_modules', 'bower_components' ],
        extensions : [ '', '.js', '.jsx' ]
    },

    entry : {
        app: './static/scripts/app.js'
    },
    output : {
        path : path.resolve('./assets'), // bridge point to django
        filename : '[name].js'
    },

    plugins : [
        /*
        new webpack.ResolverPlugin(
            new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main'])
        )
        */
    ],

    devtool : 'source-map'
}

My demo project looks like
image

if I enable webpack.ResolverPlugin, I will get below error when open index.html in the browser
image

Running webpack output(with webpack.ResolverPlugin or not) looks like below:
image

Where have I done wrong?
Plz kindly help me out, thx.

@ace-han
Copy link
Author

ace-han commented Feb 15, 2016

Listing other file sources, for quick test start

// app.js
define([
    'angular'
]
, function (angular) {
    'use strict';

    var app = angular.module('app', []);
    return app;
});
<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Angular Test</title>
  </head>

  <body>
    angular test
    <script src="assets/app.js"></script>
  </body>
</html>

@punmechanic
Copy link

Angular doesn't seem to support CommonJS. You'll need to shim it with exports-loader.

      { loader: 'exports?window.angular', test: require.resolve('angular') },

They did put in their changelog that they were supporting CommonJS, but I've always had this issue with the library.

@ace-han
Copy link
Author

ace-han commented Feb 19, 2016

@danpantry , I have already add this exports-loader, angular still an empty Object after require('angular').
No luck...

image

@punmechanic
Copy link

Ahh, you're using AMD. I'm not sure how that format works with Webpack. I'll see if I can get a working example for you.

@mlent
Copy link

mlent commented Feb 19, 2016

If it helps, you can have a look at a blogpost I wrote on working with Angular and Webpack. I definitely had a hard time getting Angular to load properly. They key ended up being something like:

module: {
    loaders: [
      { test: /[\/]angular\.js$/, loader: "exports?angular" }
    ]
  },
  resolve: {
    alias: {
      angular: __dirname + '/app/vendor/angular/angular'
    }
  }

This works for us in files using AMD as well as those in ES6 module syntax.

@ace-han
Copy link
Author

ace-han commented Feb 19, 2016

@mlent Your config snippet is working in AMD, thx!

Just out of curiosity, why adding a webpack.ResolverPlugin will has this side effect on angular?

@punmechanic
Copy link

@ace-han the main field in package.json points to a commonjs file that uses require and assumes it to be synchronous. This won't work for AMD (I guess webpack doesn't do any fancy conversions between the two paradigms).

The alias @mlent gives points directly to the raw angular.js script without the commonjs wrapping; The exports loader then exports the angular variable from that module, which will be defined after that point.

When you use angular without the alias it will try and use the require from AMD (which is asynchronous), so the angular you get back from that module hasn't been fully defined yet.

Sorry if this doesn't make sense. English is my first language, I'm just better at javascript than words..

@ace-han
Copy link
Author

ace-han commented Feb 20, 2016

@danpantry for the part

When you use angular without the alias it will try and use the require from AMD (which is asynchronous), so the angular you get back from that module hasn't been fully defined yet.

Is this the default behaviour in AMD? I mean the require asynchronously part

As far as I know, in AMD below codes should work fine

define(['dep1', 'dep2'], function(dep1, dep2){ 
    // dep1, dep2 should be already fully loaded
    dep1.doSth1(); 
    dep2.doSth2();
})
require(['dep3', 'dep4'], function(dep3, dep4){ 
    // dep3, dep4 should be already fully loaded
    dep3.doSth3(); 
    dep4.doSth4();
})

Do I understand well?
I am referring to this page https://addyosmani.com/writing-modular-js/

Is webpack's AMD require some sort of below require?

define(function ( require ) {
    var isReady = false, foobar;

    // note the inline require within our module definition
    require(['foo', 'bar'], function (foo, bar) {
        isReady = true;
        foobar = foo() + bar();
    });

    // we can still return a module
    return {
        isReady: isReady,
        foobar: foobar
    };
});

@punmechanic
Copy link

The code you've got there is completely asynchronous. The function you pass to require is executed at some point in the future after the require call (as early as the next tick), and is therefore asynchronous. And yes, this is the default behaviour of AMD.

@ace-han
Copy link
Author

ace-han commented Feb 23, 2016

Thx @danpantry. Two more quick questions

define(['angular'], function(angular){ 
    // here angular is just a reference, might not be fully loaded
    // Point_1
    angular.module('app', []);
    // other stuff...
})
  1. Is there any mechanism to ensure the variable retrieved by AMD is fully loaded at Point_1?
  2. Who is responsible for setting angular as an empty Object / {} at Point_1 under current situation?

@punmechanic
Copy link

Sure.

  1. The value retrieved by AMD would be defined by the calling script calling define(deps, function() { return value }). The value returned the function (denoted by return value here) is what is passed to consumers of that defined library. At the point that the define or require function you have there is executed, any of the dependencies you've listed should be completely resolved. As such, by Point_1, the library is fully loaded.
  2. Angular is. Angular's code does not have a UMD (Universal Module Definition) in it (at least not of writing), and it doesn't support AMD either, so it never calls define() - so AMD has no idea about what it returns. You should be able to 'shim' this using various loaders or plugins in Webpack. I'm not sure how AMD works in Webpack, but I would imagine exports-loader would be the path you'd take here.

I'd recommend that unless you're bound to AMD (via requirejs) that you use CommonJS or ES6 modules and only fall back on asynchronous loading if you absolutely have to. It just makes code easier to read about.

Recommended reading:

http://requirejs.org/docs/start.html
https://webpack.github.io/docs/loaders.html

@ace-han
Copy link
Author

ace-han commented Feb 24, 2016

@danpantry Thx for the explanation!

@ace-han ace-han closed this as completed Feb 24, 2016
@texonidas
Copy link

I feel this should be mentioned for anyone else who has found themselves here for the same reason, AMD stands for Asynchronous Module Definition.

@lili21
Copy link

lili21 commented Sep 1, 2016

@mlent It works. just wonder the reason. will require('angular') pass the /[\/]angular\.js$/ test ?

@lili21
Copy link

lili21 commented Sep 1, 2016

@danpantry

{ loader: 'exports?window.angular', test: require.resolve('angular') },

The solution does not work .

Cannot find module 'angular'

@lili21
Copy link

lili21 commented Sep 1, 2016

@mlent and Your solution will have side effect. Consider a third-part dependency contains a file name angular.js, like raven-js,

require('raven-js/plugins/angular') will give me angular instance rather then what suppose to be.

@punmechanic
Copy link

@lili21 that indicates you've not got angular installed properly, my code snippet would not break that.

FWIW I actually use raven-js and @mlent's approach works fine.

@lili21
Copy link

lili21 commented Sep 1, 2016

@danpantry I can use angular by @mlent's approach, I just can't use it with raven-js togher.So I think I installed angular properly. I mean it just npm install angular --save right ?

are you using raven-js CommonJs way ?? like this

var angular = require('angular');
var Raven = require('raven-js');

Raven
  .config('https://<key>@app.getsentry.com/<project>')
  .addPlugin(require('raven-js/plugins/angular'), angular)
  .install();

I output the result of require('raven-js/plugins/angular') , and it just angular.

@lili21
Copy link

lili21 commented Sep 1, 2016

@danpantry by the way, I rename raven-js/plugins/angular.js file to something else, like raven-js/plugins/ra-ng.js, and It works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants