52

How do you use Webpack and AngularJS together, and how about template loading and on demand fetching of resources?

An example of a well written webpack.config.js file for this purpose would be very much appreciated.

All code snippets displayed here can be accessed at this github repo. Code has been generously adapted from this packetloop git repo.

webpack.config.json

var path = require('path');
var ResolverPlugin = require("webpack/lib/ResolverPlugin");

var config = {
  context: __dirname,
  entry: ['webpack/hot/dev-server', './app/app.js'],
  output: {
    path: './build',
    filename: 'bundle.js'
  },
  module: {
    loaders: [{
      test: /\.css$/,
      loader: "style!css-loader"
    }, {
      test: /\.scss$/,
      loader: "style!css!sass?outputStyle=expanded"
    }, {
      test: /\.jpe?g$|\.gif$|\.png$|\.svg$|\.woff$|\.ttf$/,
      loader: "file"
    }, {
      test: /\.html$/,
      loader: "ngtemplate?relativeTo=" + path.join(__dirname, 'app/') + "!raw"
    }]
  },
  // Let webpack know where the module folders are for bower and node_modules
  // This lets you write things like - require('bower/<plugin>') anywhere in your code base
  resolve: {
    modulesDirectories: ['node_modules', 'lib/bower_components'],
    alias: {
      'npm': __dirname + '/node_modules',
      'vendor': __dirname + '/app/vendor/',
      'bower': __dirname + '/lib/bower_components'
    }
  },
  plugins: [
    // This is to help webpack know that it has to load the js file in bower.json#main
    new ResolverPlugin(
      new ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
    )
  ]
};

module.exports = config;

To import AngularJS into the main app.js you do the following:

app/vendor/angular.js

'use strict';

if (!global.window.angular) {
  require('bower/angular/angular');
}
var angular = global.window.angular;
module.exports = angular;

And then use it in app.js like so,

app.js

...
var angular = require('vendor/angular');

// Declare app level module
var app = angular.module('myApp', []);

...

Is the following correct? Is there an easier way to do this? I've seen a few (not a lot by any standards) posts which listed another method.

From this reddit post comment

// Add to webpack.config.js#module#loaders array
    {
      test: /[\/]angular\.js$/,
      loader: "exports?angular"
    }

There is also another plugin which is in development right now, at stackfull/angular-seed. It seems to be in the right direction, but is really really hard to use right now.

Webpack is way awesome, but the lack of documentation and samples are killing it.

danwellman
  • 9,068
  • 8
  • 60
  • 88
Jibi Abraham
  • 4,636
  • 2
  • 31
  • 60
  • 1
    Angular itself works perfectly out of the box with webpack - `var angular = require('angular')`. You can also require your modules with the `name` property on each module - `var myModule = require('./myModule'); angular.module('foo', [myModule.name])`. There are a few exceptions - ui router being a notable one doesn't export the module object but the module name itself. – Dan Jul 15 '15 at 20:35
  • This may also be a good place to start. It helped me. https://egghead.io/lessons/angularjs-angular-with-webpack-introduction – michaelgmcd Sep 04 '15 at 16:39
  • 1
    angular does not work out of the box with the syntax: var angular = require('angular') without your app/vendor/angular.js. If you use exports loader and use angular, you can get it work without your vendor/angular.js. Also note that your dependencies should be npm dependencies. Bower dependencies will not work with exports loader. – randominstanceOfLivingThing Mar 13 '16 at 03:51

2 Answers2

13

You can just require angular in all modules (files) where you need it. I have a github repository with example how to do that (also using webpack for build). In the example ES6 import syntax is used but it shouldnt matter, you can use standard require() instead.

Example:

import 'bootstrap/dist/css/bootstrap.min.css';
import './app.css';

import bootstrap from 'bootstrap';

import angular from 'angular';
import uirouter from 'angular-ui-router';

import { routing} from './app.config';

import common from './common/common.module';

import featureA from './feature-a/feature-a.module';
import featureB from './feature-b/feature-b.module';

const app = angular
    .module('app', [uirouter, common, featureA, featureB])
    .config(routing);
tomastrajan
  • 1,728
  • 14
  • 28
7

I am starting with Angular + Flux with Webpack so may be I can help you with some things.

Basically I am installing everything with NPM, it has module export system, so it works like nothing. (You can use export-loader, but why if you do not need to.)

My webpack.config.js looks like this:

var webpack           = require('webpack');
var path              = require('path');
var HtmlWebpackPlugin = require("html-webpack-plugin");

var nodeModulesDir = path.resolve(__dirname, './node_modules');

// Some of my dependencies that I want
// to skip from building in DEV environment
var deps = [
  'angular/angular.min.js',
  ...
];

var config = {
  context: path.resolve(__dirname, './app'),

  entry: ['webpack/hot/dev-server', './main.js'],

  resolve: {
    alias: {}
  },

  output: {
    path: path.resolve(__dirname, './build'),
    filename: 'bundle.js'
  },

  // This one I am using to define test dependencies
  // directly in the modules
  plugins: [
    new webpack.DefinePlugin({
      ON_TEST: process.env.NODE_ENV === 'test'
    })
  ],

  module: {
    preLoaders: [
      {test: /\.coffee$/, loader: "coffeelint", exclude: [nodeModulesDir]}
    ],
    loaders: [
      {test: /\.js$/, loader: 'ng-annotate', exclude: [nodeModulesDir]},
      {test: /\.coffee$/, loader: 'coffee', exclude: [nodeModulesDir]},
        ...
    ],
    noParse: []
  },

  devtool: 'source-map'
};

if (process.env.NODE_ENV === 'production') {
  config.entry = {
    app: path.resolve(__dirname, './app/main.js'),
     vendors: ['angular']
  };
  // config.output.path = path.resolve(__dirname, './dist');
 config.output = {
   path: path.resolve(__dirname, "./dist"),
  filename: "app.[hash].js",
  hash: true
 };
 config.plugins.push(new webpack.optimize.UglifyJsPlugin());
 config.plugins.push(new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.[hash].js'));
 config.plugins.push(new HtmlWebpackPlugin({
   title: 'myApp',
   template: path.resolve(__dirname, './app/index.html'),
   inject: 'body'
 }));

 delete config.devtool;


}
  else {
    deps.forEach(function (dep) {
      var depPath = path.resolve(nodeModulesDir, dep);
      config.resolve.alias[dep.split(path.sep)[0]] = depPath;
      config.module.noParse.push(depPath);
    });
  }
module.exports = config;

My main.js looks like this:

var angular = require('angular');

if(ON_TEST) {
  require('angular-mocks/angular-mocks');
}

require('./index.coffee');

And index.coffee containt main angular module:

ngModule = angular.module 'myApp', []

require('./directive/example.coffee')(ngModule)
Har devgun
  • 533
  • 6
  • 25
Kamil
  • 1,633
  • 2
  • 21
  • 24
  • this is incredible. Congratulations. This code is open source? I would like to take a look at the project, I am having [some problems](http://stackoverflow.com/questions/33502112/webpack-with-bower-support) with Webpack. :) – ridermansb Nov 04 '15 at 23:26
  • @Kamil did you get around making that repo? I would really love to see it. At the very least, cna you show me roughly how `./directive/example.coffee` file may look like? – anbiniyar May 09 '16 at 03:34