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

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.

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.

How to keep Grunt dom_munger from stripping your CDN dependencies?

I have an object, Plaid, that is provided by a drop-in module from Plaid, called Plaid Link. Locally, the code works perfectly in my AngularJS front-end.
The module is loaded via my AngularJS app's index.html file, right next to all of the other script tags loaded for my application.
Index.html
<script src="https://cdn.plaid.com/link/stable/link-initialize.js"></script>
Here is the code where Plaid is called: (from an AngularJS controller)
$scope.addAccount = function(bankChosen) {
$log.debug("Plaid object: ", Plaid);
var linkHandler = Plaid.create({ // the troublesome line
env: 'tartan',
clientName: 'Example Project',
key: 'test_key',
product: 'connect',
onSuccess: function(public_token) {
Restangular.one('plaid').customPOST(
{
public_token: public_token,
user_id: $scope.currentUser.id,
institution_code: bankChosen
},
'addAccount'
);
},
onExit: function() {
$state.go('dashboard.successState');
},
});
linkHandler.open(bankChosen);
};
However, when I push to the production environment, currently hosted on Heroku, it gives the javascript error ReferenceError: Plaid is not defined when it tries to load. It is breaking error when deployed to production.
What could be causing this module to be unavailable during production?
The script that loads the CDN could be getting stripped away by a standard grunt task that does that sort of thing? Or maybe I am supposed to be loading the module from the CDN in some other way in a production-environment setting? I really don't know...
Update: I found one thing that might be stripping the loaded module
From the Grunt dom-munger docs
Use this task to read and transform your HTML documents. Typical use cases include:
Read the references from your script or link tags and pass those to
concat,uglify, etc automatically.
2. Update HTML to remove script
references or anything that is not intended for your production
builds.
Add, update, or remove any DOM elements for any reason.
dom_munger is part of my application's build process, which happens when it is deployed (!). Grunt is the likely culprit here.
Does anybody know how to load the script tag above, with dom_munger as still part of my app's grunt build step?
The problem was that during the build step Grunt strips away the script tags in my application. So I had to append the tag back on to my body tag using dom_munger's update --> options --> append option.
...only then could I get the CDN script to be linked to properly after the app was built using grunt build.
The line looks like this in my Gruntfile.
--> The key added line is this one
{selector:'body',html:'<script src="https://cdn.plaid.com/link/stable/link-initialize.js"></script>'},
dom_munger:{
[.....]
update: {
options: {
append: [
{selector:'body',html:'<script src="https://cdn.plaid.com/link/stable/link-initialize.js"></script>'},
]
},
src:'index.html',
dest: 'dist/index.html'
}
Quite the mysterious error for me. I hope this helps somebody else out at some point!

How can I pre load a template in Angular UI router?

I am using Angular UI router. I have many template calls like this:
var access = {
name: 'access',
templateUrl: 'app/access/partials/a1.html',
};
The page served by Access depends on the :content.
Is there a way that I could combine all these HTML files into one and pre-load the files so every action didn't involve another request? I realized I can put my HTML directly inside the template but can I combine the HTML from multiple templates into one file and have it all pre-loaded so it's ready when needed?
Is there a way that I could combine all these HTML files into one and pre-load the files so every action didn't involve another request?
Yes, by using html2js;
or, to be exact, a grunt/gulp plugin called, respectively, grunt-html2js / gulp-html2js
HOW IT WORKS
According to this grunt-html2js tutorial:
" html2js is a plugin (...) that parses all template files in your application and creates a javascript file that populates the $templateCache.
This is very useful trick to reduce the number of request your app needs to make to start the application.
It can also minify the html snippets saving some bandwitdh as well."
----------
According to the grunt-html2s Github repository:
"html2js converts a group of templates to JavaScript and assembles them into an Angular module that primes the cache directly when the module is loaded.
You can concatenate this module with your main application code so that Angular does not need to make any additional server requests to initialize the application."
----------
And according to me :)
What I like about html2js is that you don't need to change any of your angular code, but just configure gruntfile.js / gulpfile.js.
Your templateUrl files will then automagically be available in $templateCache.
So it does exactly what you wished for:
Combine all your templates into a module;
Write the JavaScript source for the module;
All you need to do is to specify the module as a dependency in your code.
Once the module is loaded, all your templates are available in the cache: no request needed!
EXAMPLES OF GRUNT / GULP CONFIG
grunt.initConfig({
html2js: {
options: {
base: 'app',
module: 'templates',
htmlmin: {
... many available options: see GitHub repo ...
}
},
main: {
src: ['app/**/*.html'],
dest: 'templates.js'
},
},
})
then just use the templates module as a dependency:
angular.module('myApp', [ 'templates']);
----------
Here is an example from the gulp-html2js GitHub repository:
gulp.task('scripts', function() {
gulp.src('fixtures/*.html')
.pipe(html2js({
outputModuleName: 'template-test',
useStrict: true
}))
.pipe(concat('template.js'))
.pipe(gulp.dest('./'))
})
TESTING
What's more, html2js is also a very good tool to test directives that use the templateUrl property.
FOR MORE INFORMATION
I came accross html2js after reading Joel Hooks' (Egghead instructor) book about automation, which I strongly recommend.
Watch a dedicated video about html2js in the pro section of the EggHead website.
Testing directives using the templateUrl property with Karma and karma-ng-html2js-preprocessor
NOTE
Technically, you could just use ng-html2js, without using Gulp or Grub; you could then use a command line like the following:
ng-html2js inputFile [outputFile] [-m moduleName] [--module-var ngModule]
However, as you would need to run the above command for each templateUrl file, Gulp/Grub automation seems to be a more efficient way to go.
DISCLAIMER
I have no particular interest or shares in the book/websites/tools I mentioned.
Note: I should also add, you also add this file to your html page:
<script src="path/to/templates.js"></script>
Why not use ng-include in your main html file to load all html's at once.
Something like this...
<div ng-repeat="template in templates">
<ng-include src="template.url"></ng-include>
</div>
So here templates is an array of objects which contains all the url's the other templates which you want to load at once in your main.html file.
Easy. Create them all as ng-template blocks:
<script type="text/ng-template" id="app/access/partials/a1.html">
Content of the template.
</script>
There is already a grunt-angular-templates grunt plugin specific to this angular task only. i have been using it for a while now. this plugin automatically caches your HTML templates using $templateCache in one file which you can add in your application.
grunt-angular-templates - https://www.npmjs.com/package/grunt-angular-templates
Use,
yourApp.run(['$templateCache', function($templateCache) {
$templateCache.removeAll();
}])
Your problem pointed in this site too,
http://javahow.net/questions/27321315/using-angulars-ui-router-how-can-we-make-sure-the-new-version-of-the-html-part

Grunt.js for Angular.js dependency injection management

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.)

What's the use of exports / module.exports?

Today I've adopting Browserify for my AngularJS project, but there is something that's very unclear to me. In all examples and blog posts I've seen stuff like this:
/app.js:
require('./messages');
angular.module('sling', ['sling.messages']);
/messages/index.js:
exports = angular.module('sling.messages', [])
.controller('MessagesListCtrl', require('./MessagesListCtrl'));
/messages/MessagesListCtrl.js:
module.exports = function() {
// ...
});
Sure, this works, but why do this? I've implemented it like this and that works absolutely fine too and feels more normal for an AngularJS project:
/app.js:
require('./messages');
angular.module('sling', ['sling.messages']);
/messages/index.js:
angular.module('sling.messages', []);
require('./MessagesListCtrl');
/messages/MessagesListCtrl.js:
angular.module('sling.messages').controller('MessagesListCtrl', function() {
// ...
});
In other words, I'm completely skipping the exports/module.exports, only using require to basically include the files with the controllers, services, filter, etc.
Am I doing this right? I mean it all works, but am I going to be in trouble later on?
The main (and honestly only) reason to use Browserify is if you want to have CommonJS modules (i.e. NodeJS modules) in the browser. A CommonJS module stays out of the global scope by having an implicit 'module' scope. You choose what to expose from the module scope (usually the entry point or primary function of your module) into by augmenting the 'exports' object that every module has.
So a 'real' CommonJS module looks like this.
File A:
// a.js
function doSomething() {
console.log("I am doing something");
}
module.exports = doSomething
File B:
// b.js
doSomething();
// Exception - attempt to call a non-existent function
File C:
// c.js
var doSomething = require('a');
doSomething();
// logs "I am doing something"
In the browser which has no module scope, a.js would be augmenting the global scope with the doSomething function, since it's declared as a global. Browserify works around this by wrapping each bundled module into a function wrapper, and supplying an 'exports' object to the included modules as an argument to this wrapper.
Enter AngularJS. You've got two approaches you can use here, of which I'm assuming from the fact that you're not using require('angular') is the first:
Include AngularJS as a hard-coded script available in your index.html before your Browserify-transpiled bundle script.
Shim AngularJS so that it is attached to an exports object rather than the window (using something like browserify-shim), then importing it using require like any other module.
I tend to prefer the second approach because it's kind of weird to use Browserify to give you module scope and then make the major dependency of your project a window global.
However, AngularJS already has its own dependency-injection driven module system. When you declare angularJS components, you're attaching them to module objects which themselves have been attached to the angular object. This means that the exports object for your angularJS module files is essentially redundant in the case of Angular, since as long as the file executes then the angular object will be augmented with your module and components.
You still need to 'require' the files since otherwise Browserify won't bundle them, they'll never execute, and they'll never augment the angular object with your modules. But you don't need to add anything to the exports for Angular, since the angular object is your exports.
So why did I spend all this time explaining how CommonJS modules and exports work? Because hopefully the other reason why you are using Browserify is to allow you to make use of modules hosted on NPM in your browser application. Most of those modules are non-angular commonJS modules, meaning their functionality is exposed via an exports. In this case it's important to capture their exports in a variable when you require them, such as I'm doing in c.js above. Similarly, if you write some modules and release them to NPM, your users are going to expect you'll be adding the entry point to your module to the exports object of the file declared as main in your package.json.

Resources