Grunt.js for Angular.js dependency injection management - angularjs

I am using Grunt to automatically build my web app. I have run into an interesting issue. I have two options: 1)grunt:dev and 2)grunt:build
grunt:dev just does basic tasks related to development. My "main" Angular module looks like this:
var myApp= angular.module('myApp', [
"ui.router",
"ui.bootstrap",
"someDependency",
"someDependency2"
]);
When I want to build, I do grunt:build. I am using the html2js grunt plugin to prime the Angular cache. However, this method generates a new module not in my development workflow called templates-main.
In order for this to work, when I build, I need the "main" module to look like:
var myApp= angular.module('myApp', [
"ui.router",
"ui.bootstrap",
"templates-main", //<<< NEW DEPENDENCY
"someDependency",
"someDependency2"
]);
Is there a recommended way of accomplishing this? If you include the dependency, but it is not there, it causes an Angular error. I am hoping this can be automated with Grunt.
Thanks in advance

I figured out a workaround for this. I am using Grunt's Concat module. This allows you to have a custom process system with regular expressions:
build: {
options: {
process: function(src, filepath) {
return src.replace(/[/*<%]{4}.*[%>*/]{4}/g, '"templates-main",');
}
},
src: ['src/app/app.js', 'src/app/**/*.js'],
dest: 'build/app.js'
}
I then did the following in the code:
var eeApp = angular.module('eeApp', [
"ui.router",
"ui.bootstrap",
/*<% templates-main %>*/
"dashboard"
]);
In normal use, the block comment will prevent the code from throwing an error. When the template process goes through, the regular expression will match the entire comment block and substitute in the required dependency. Nice!

I think the easiest way is to create an empty fake module file for your dev build and overwrite it with the real template cache in the production build.
This way you shouldn't change the dependencies dynamically.
(Or you can copy this file with grunt as well in the development setup, if you wouldn't like to overwrite originals.)

Related

Webpack with angular 1.x and ES5

After reading hundreds of lines about browserify vs webpack and several how to of both I decided to go for webpack. The main reason it's because I liked the idea of bundling everything into a js file.
I have an angular project already working and I want to refactor it for webpack. The problem? My project is using angular 1.4.7, ng-animate and plain javascript (ES5) and all the tutorials and manuals are for ES6. I don't want to refactor my project so much. What's the way to go? I would like an example of each angular module : factory, directive, controller and so on. Many thanks
I typically have a feature.module.js file which has my module definition and requires all of the directives / services contained within the module. Also has the external dependancies.
/* module.js */
angular.module('my.module',['dependancy1', 'dependancy2']);
//External libraries
require('./dependancy1.module.js');
require('./dependancy2.module.js');
//Internal components
require('./thing.directive');
require('./thing.service';
/* service.js */
angular.module('my.module')
.directive('yourDir', function myDir(){...});
I'm dealing with the same problem now. And I found something that works (work in progress, but at least I can see progress). My steps:
Install yeoman
Run this angular-webpack generator. Select 'ES5' when asked (the other option is 'ES2015', which I guess is the same that 'ES6')
Start modifying the automatically generated boilerplate with your Angular code
Yes, you still need to learn about gulp and sass, but at least you can run a simple AngularJS app using the old ES5 syntax, and start modifying it.
I'm probably blogging about this. So, I'll update this answer then.
I tend to do this:
app.js:
require('/any/angular/deps');
var subModule = require('/some/sub/module');
var app = angular.module('myApp', []);
// pass the app module in sub modules to allow them to define their own config
subModule.configure(app);
/subModule/module.js:
var someSubDirective = require('./subDir/directive');
export function configure(app) {
someSubDirective.configure(app);
}
/subModule/subDir/directive.js:
export function configure(app) {
app.directive('myDir', myDir);
}
function myDir() {
}
My idea is to let all sub modules handle their own configuration, so declaring config or constant, factories or providers. Letting this then bubble up to the app.js. This means its really easy to delete a folder from your structure, because it is one line removal from it's parent module.
This also makes relevant file paths a lot shorter and easier to handle.

Sails.js + RequireJS configuration

Having all kinds of problems getting Sails to work with RequireJS, mainly because I can't find any definitive source on the best way to do this. There are several posts out there that discuss this, but they are older and all do things differently. Would really love to see the Creators enlighten the community on the proper way to do this given the changes to the Sails application structure, linker process etc. in the latter versions (.0.9.9, ,0.10)
So, first question would be...if I am planning on using an AMD/RequireJS + Backbone approach for my client-side code, and want to use the R.js Optimizer in grunt to build my production JS file and resolve all the nested dependencies automatically (rather than have to list them out manually), should I not create the application with the --linker option and manually manage the grunt build process myself?
Also, where in the directory structure should the "vendor" directory be placed that contains all the dependent JS libs like Underscore, jQuery, Backbone etc. reside?
I decided this problem:
Install the plugin for grunt-requirejs
wrote config to run build in a folder /tasks/config/requirejs.js
Example:
module.exports = function(grunt) {
grunt.config.set('requirejs', {
dev: {
options: {
baseUrl: "assets/",
name: 'main',
optimize: "uglify2",//'none',//"uglify2",
//wrap: true,
paths: {
// Major libraries
jquery: '../vendor/jquery',
underscore: '../vendor/underscore',
backbone: '../vendor/backbone',
// Require.js plugins
},
removeCombined: true,
inlineText: true,
useStrict: true,
out: "build/main.js",
waitSeconds: 200
},
}
});
grunt.loadNpmTasks('grunt-contrib-requirejs');
};
added to autostart in tasks/register/compileAssets.js
Example:
module.exports = function (grunt) {
grunt.registerTask('compileAssets', [
'clean:dev',
'jst:dev',
'less:dev',
'copy:dev',
'coffee:dev',
'requirejs:dev'
]);
};
You also have to adjust just grunt at yourself and do not be afraid to change anything. At design time, better to store scripts in the Assets folder because it is convenient.
For others having the same problem, a quick but only partial fix is to disable the script injection by removing the following from layout.ejs:
<!-- SCRIPTS -->
<!-- SCRIPTS END -->
Then just place direct links to your require.js file:
<script src="/linker/js/components/requirejs/require.js"></script>
I say this is only a partial fix because the GruntFile will need need to implement a require task in order to concatenate the files correctly.

jasmine complaining about dependencies in angular module where they aren't needed

I always give up on testing because I find it is more work than actually writing code that works well, but I'm working on a project I hope to open-source, so am committed to writing tests this time.
I have this angular app, and when I define it I include the dependencies
var app = angular.module('app', [
'ngResource',
'ngSanitize',
'ngRoute',
'ui.ace'
]);
When I try to test a controller, I start with
beforeEach(angular.mock.module('app'));
beforeEach(angular.mock.inject(function($rootScope,$controller){
scope = $rootScope.$new();
$controller('FileSystemCtrl',{$scope:scope});
})
);
When I run jasmine, I get
Failed to instantiate module app due to:
Failed to instantiate module ui.ace due to:
Module 'ui.ace' is not available!
I don't want to have to list all the dependencies every time I mock or instantiate 'app' in a test, as that would mean when I add a new dependency, I'd have to go back and change all the already existing test.
This seems very inefficient to me.
Can somebody explain why I'm getting this error, and how to get around it?
If you're not using a test runner such as Karma, I'd highly recommend using that. Here's the link. What karma allows you to do is to define all of your required files in one main configuration file so you can maintain it in one central location. Plus there's a jasmine plugin for karma.

Getting Angularjs with Requirejs working with restangular and twitter bootstrap

I am building a new angularjs app and want to use requirejs to manage my dependencies. I am struggling to get this to work and wonder if I'm just misunderstanding something. I also want to use twitter bootstrap and restangular to make restful calls. Trying to set this up leads to errors of not being able to find it. My main,js config is as follows:
require.config({
paths: {
angular: '../vendor/angular',
twitterbootstrap: '../vendor/bootstrap/ui-bootstrap-tpls-0.6.0',
restangular: '../vendor/restangular',
domReady: '../vendor/domReady'
},
shim: {
angular : {'exports' : 'angular'},
restangular: {
deps: ['underscore']
}
}
});
require([
'angular',
'app',
'domReady',
'twitterbootstrap',
//'underscore',
'restangular',
],
function (angular, app, domReady) {}...
I am getting errors with this:
angular is not defined
no module myApp
no module twitterbootstrap
I have tried several variations with this but just can't get it to play nicely together. Is there something else I'm missing - I've compared it to several examples and everything seems to be in order.
Mixing Angular and Require is tricky. I have an experimental project in GitHub (https://github.com/nikospara/angular-require-lazy) that works in some hacky way. You may take a look as an example.
Opinions comments on this project are also wellcome.
For your errors:
Open a net console (e.g. in Firebug) and check the paths called by RequireJS. Are these paths correct?
Angular modules (in contrast to Require/AMD modules) have dependencies of their own. You must make sure that .js files of Angular modules are loaded after angular. One way to do that is to shim them, e.g. for restangular it would be something like:
shim: {
...
restangular: {
deps: ["underscore", "angular"]
}
}
Otherwise you can load angular and the other scripts with standard <script> tags, in the correct order.
These are general directions. If you can't find the reason of the problems, maybe you should post some more code; a fiddle/plunkr would also be great, if possible.
Related post in SO: Inject module dynamically, only if required

Backbone.Marionette - Grunt Browserify - "require is not defined"

I'm using grunt-browserify and running into two issues in particular. The task is up and running successfully with the following config options. The variable jsFilesToConcat represents all of the javascript files for a Backbone.js + Marionette.js application, the main application defintion, the front-end utility assets (e.g. Bootstrap plugins), and all JS associated with the project. Is this the wrong approach? The thought was to load the entire 250k JS application (and all it's dependencies) at one time.
I want to offer the disclaimer that this is new territory for me, so I think my intended use case is available with the options already available with the plugin, but I'm confused by two errors:
1) Backbone not defined - which means that the script is in fact loading, however, when I inspect the call stack in Chrome Dev Tools it shows only the anonymous self-invoking function. So I'm not clear on how to pass the Backbone object to Marionette in order for it to be extended at load time.
2) require is not defined - error on the line where I'm declaring var SampleApp = require('SampleApp'). Do I need to do something special within my grunt config, or node.js server.js config to expose the require function?
3) Is the javascript executing asynchronously within itself, is this part of the browserify intended behavior that I'm not properly handling? I think since I'm wrapping alot of JS utilities in a global wrapper to protect namespacing, that's the reason some functions are not available, but I'm not clear on why that would affect require.
// uses grunt-browserify task
browserify: {
developmentJs: {
options: {
debug: true,
alias: ["./js/app.dev.js:SampleApp"],
},
src: [
'<%= pkg.jsFilesToConcat %>'
],
dest: 'public-dev/js/app.dev.js'
}
}
and then in the index.html of my single-page Marionette app, I have.
(function ($) {
$(document).ready( function() {
var sampleApp = require('SampleApp');
console.log( SampleApp );
});
})(jQuery);
Well for starters, the src attribute in your grunt file doesn't need to reference all of the files in your application. It only needs an entry point. So normally I have something similar to your anonymous self executing function in an index.js file, and set my src configuration option to ["./index.js"]. When browserify looks at that file, it will check for calls to require and grab all of the required dependencies.
That said, browserify will generate a file with an internal definition of require. The require function is not globally available on the page, nor are the dependencies that you include with require. You can use them in your application, but that doesn't make them available in the page. So if you are getting a Backbone is not defined error, the first thing I would check is that you have installed backbone via npm (npm install backbone --save).
Once everything is set up you should just have to include your compiled script on the page, and let your anonymous self executing function (which should now be in a file that grunt-browserify is processing) do the work to kick off your application.

Resources