How to declare sub-modules in AngularJS - javascript

When working on large projects in AngularJS, I found that I like organizing code by functionality.
That means that when I have some recognizable functionality X (especially if it is reusable) I create directory X and put into it all controllers, services and other parts that belong to that functionality. I also declare a new module called X and assign everything in directory X to that module.
Directory structure would then look something like this:
scripts/
app.js
controllers/
services/
directives/
filters/
X/
controllers/
services/
directives/
filters/
In app.js there is a main module declaration:
angular.module('myApp', ['X']);
All controllers etc. in X/ belong to module 'X', which means I fetch module 'X' like this in those files:
var X = angular.module('X');
What I am not sure how to do is where to declare module 'X'?
Some ideas I had:
I could declare it in one of the controllers/services/... and fetch it in other controllers etc. but that does not sound right because I do not see any logic how to pick that controller/service/... among others, and also then I have to take care to include it in index.html before others. I find this idea bad.
I can put declaration in app.js, so app.js would now look like
angular.module('myApp', ['X']);
angular.module('X', [/*some dependencies could go here*/]);
I find this idea better than the previous one, however I do not like that module declaration is outside of directory X.
In directory X/ I create file main.js and put declaration of module X in it. I have to be careful to include this file in index.html before other files from directory X/.
I like this solution the best.
Is there any better way to do this?

Yes. Third option is the best solution. First option will definitely give a pain in the neck. Second option adds a dependency of module X in the main app, which is not desirable. You would want your module to be self-contained. So third option is the best solution.
Here is the best practice recommendations from Google which you are actually trying to adhere to. :) Moreover, it will also be great for you (as suggested by Google's best practice) to not separate the files by artifacts, which means you do not need controllers, directives, etc dir structure as below. Also note partials, CSS and even tests are in the component are contained in the component/module directory. Hence the module is totally contained and independent, which helps in reusability:
sampleapp/
app.css
app.js
app-controller.js
app-controller_test.js
components/
bar/ "bar" describes what the service does
bar.js
bar-service.js
bar-service_test.js
bar-partial.html
foo/ "foo" describes what the directive does
foo.js
foo-directive.js
foo-directive_test.js
foo-partial.html
index.html
I hope this will be helpful.

Related

How can I use Gulp and Browserify for my javascript app?

I am finally trying to bring a modern build system to my app, and I'm hoping someone can help. I think I need a few paradigm shifts.
So this is how my app is structured:
/src
/components
/Base
/App.jsx
/Pages.jsx
/...
/Page1
/Page1Component1.jsx
/Page1Component2.jsx
/...
/Page2
/Page2Component1.jsx
/Page2Component2.jsx
/...
/...
/libs
/bootstrap.js
/jquery.js
/react.js
/...
/scripts
/index.js
/utils.js
/styles
/main.css
/html
/index.html
Right now I have gulp set up to do this:
Make a new folder /dest to put everything
Combine everything in /scripts, name it main.js, put it in dest
Combine everything in /libs, name it libs.js, put it in dest
Combine everything in /components, run it through babel, name it comps.js, put it in dest
Copy the one /html file and one /styles file into dest
Then here is how the app runs:
Open index.html
That page requests main.js
main.js requests libs.js and comps.js
Everything works
But here is the issue I'm running into: A lot of stuff here relies on other stuff being global. index.js waits for comps.js and libs.js to load, then calls ReactDOM.render(<App />...), which means both ReactDOM and App need to be global.
Now I'm trying to add something that needs require(), and I try to use Browserify for it. But Browserify takes the code that needs the require and wraps it up in a way that, I believe, makes nothing global.
I realize that I need to turn my app into actual modules, instead of just a bunch of files that concatenate and call each other. And I know that avoiding global variables will be a good thing in the long run. But I'm having a really hard time figuring out how.
For example, I have >50 React modules. It seems wrong to add module.exports to every single one of those, and then import them all to the main file. Further, some of the things in /lib are libraries that don't export as modules, they're made to be run in the <head> tag, like Google Charts.
So I guess my questions are:
Where should my module exports be, and how do they fit into my gulp tasks? Do I concatenate then export?
How do I deal with libraries that aren't modules?
Is my app really poorly laid out, and I just need to restructure from scratch?
Thanks, and sorry about the rambley question.
First, there's nothing wrong with your file structure.
Second, the best thing you can do is follow the "one module, one file" rule. That does mean adding module.exports or export default to every single file. That's just good JavaScript. But it doesn't mean importing them all into your main file, which brings us to:
Third, think in modularity. Files should require or import precisely what they need and nothing they don't. For example, if your App uses Page1 and Page1 uses Page1Component1, then that's how your imports should work:
App -> Page1 -> Page1Component1
-> Page1Component2
-> Page2 -> Page2Component1
-> ...
This ensure separation of concerns and protects your code from easy-to-trigger errors later on (like those from nested dependency changes). And your build system should generate one file (but you can tackle performance later if needed with chunking and so forth).
And you're correct that in this kind of structure, using Browserify or Webpack will ensure that nothing is global - and that's a good thing (though I will note that you can tell them explicitly to expose components, which is sometimes necessary for libraries).
And that leaves libraries that you don't control that you can't import. This does not apply to Bootstrap, jQuery, or React, which all have require-able modules from NPM. But assuming that you have a library you didn't mention that is not available through NPM, you can still include it globally in your HTML with a script tag and tell Browserify or Webpack to expose it for requiring.

Where to specify module dependencies?

I'm following the standard practice of organizing my angular assets by feature; e.g. AngularJS Folder Structure and AngularJS Best Practices: Directory Structure.
Which file should I put my module / dependency declaration in?
I'm trying to solve the following problems:
I'd like to be able to sort my <script> references alphabetically for maintenance reasons, but I can't because that breaks my Angular bootstrap (for some modules).
I've tried keeping them in the alphabetically-first *.js file in the module, but I spend a lot of time as my app grows moving my dependency declarations around.
I often have to hunt around to find module declarations.
I end up staring at Angular's relatively uninformative module error too often for related reasons.
Regardless, attaching the module declaration to a specific controller seems to imply a direct correlation that doesn't exist.
Here's an example:
metric/
_module.js // Should I create this file?
detail-controller.js
detail.html
search-filter.js
selector-controller.js
selector-directive.js
selector.html
Currently, for this module, that line of code exists in one of my module's controllers, you guess which one! ;)
As a possible solution that I'm not entirely happy with, should I put each module definition in its own tiny, one-line file?
angular.module('metric', ['lib', 'ngSanitize', 'ui.select', 'data']);
How do you do this? Am I missing some other clever or obvious solution?
p.s. as a related problem, if you feel like it, how do to track which components of your module are the source(s) of the dependency?
I would break it up even further.
metric/
metric.js
controllers/
detail-controller.js
selector-controller.js
directives/
selector-directive.js
filters/
search-filter.js
templates/
detail.html
selector.html
Now that I've been working with it for a while, and because I've started pre-compiling my javascript with gulp, the one-line module declaration file seems to be the best solution for me.
I name that file <special-character>module.js, so that it sorts visually and at compile-time to the top. Because my layout convention is one folder = one module, this works schematically. My special character is dash, YMMV. My individual .js file names don't show up in the production compiled version anyway.
It initially bothered me that there was a one-line file in my project, but now I appreciate it. It gets compiled in to my application javascript with gulp, so it's not a performance issue. Also, there's an obvious place to look for dependencies, clear trail in revision control logs of dependency changes, and simple process to document dependencies from my sources with my own custom tools.

Angular.js namespacing modules controllers

I have following structure in my application directory:
scripts/
modules/
module1/
controllers/
MainController.js
module2/
controllers/
MainController.js
main.js
What I want to achieve is to put controllers in each module to its own namespace, for example:
module1.MainController
module2.MainController
So when i use in html ng-controller="MainController" directive it knows from which module to serve it. Also it would be good that modules can communicate with each other.
Please explain to me how I can achieve this in the best way as possible, and if it's at all possible?
I've found something like this:
http://jsfiddle.net/luisperezphd/j5jzsppv/ but I'm not sure if this is good solution. It uses angular.ng-modules.js.
EDIT:
I'm trying to use Angular.js v.1.3.6. On version 1.2.x there is no problem with namespaces.
Inject one of the modules into the other using regular dependency injection:
var moduleTwo = angular.module('moduleTwo', ['otherModule']);
This allows moduleTwo to have awareness of otherModule. Then you can use a service to share state between controllers. Services are singletons (only one instance will exist), so if multiple controllers use the same service they will share that state.

Require pattern Browserify / Angular

I'm working in a project with angular and browserify, this is the first time for me to use this two tools together, so I would like some advice on which is the way to require files with browserify.
We may import those files in different ways, Until now I experimented this way:
Angular App:
app
_follow
- followController.js
- followDirective.js
- followService.js
- require.js
- app.js
For each folder with in the files for a plugin I created an require.js file and in it I require all the files of that folder. Like so:
var mnm = require('angular').module('mnm');
mnm.factory('FollowService', ['Restangular',require('./followService')]);
mnm.controller('FollowController',['$scope','FollowService',require('./followController')])
mnm.directive('mnmFollowers', ['FollowService',require('./followDirective')]);
and then require all require.js files in a unique file called app.js that will generate the bundle.js
Question:
This way to require the files can be a good structure, or it will have some problem when I need to test? I would like to see your way to achieve good structure with angular and browserify
AngularJS and browserify aren't, sadly, a great match. Certainly not like React and browserify, but I digress.
What has worked for me is having each file as an AngularJS module (because each file is already a CommonJS module) and having the files export their AngularJS module name.
So your example would look like this:
app/
app.js
follow/
controllers.js
directives.js
services.js
index.js
The app.js would look something like this:
var angular = require('angular');
var app = angular.module('mnm', [
require('./follow')
]);
// more code here
angular.bootstrap(document.body, ['mnm']);
The follow/index.js would look something like this:
var angular = require('angular');
var app = angular.module('mnm.follow', [
require('./controllers'),
require('./directives'),
require('./services')
]);
module.exports = app.name;
The follow/controllers.js would look something like this:
var angular = require('angular');
var app = angular.module('mnm.follow.controllers', [
require('./services'), // internal dependency
'ui.router' // external dependency from earlier require or <script/>
// more dependencies ...
]);
app.controller('FollowController', ['$scope', 'FollowService', function ...]);
// more code here
module.exports = app.name;
And so on.
The advantage of this approach is that you keep your dependencies as explicit as possible (i.e. inside the CommonJS module that actually needs them) and the one-to-one mapping between CommonJS module paths and AngularJS module names prevents nasty surprises.
The most obvious problem with your approach is that you're keeping the actual dependencies that will be injected separate from the function that expects them, so if a function's dependencies change, you have to touch two files instead of one. This is a code smell (i.e. a bad thing).
For testability either approach should work as Angular's module system is essentially a giant blob and importing two modules that both define the same name will override each other.
EDIT (two years later): Some other people (both here and elsewhere) have suggested alternative approaches so I should probably address them and what the trade-offs are:
Have one global AngularJS module for your entire app and just do requires for side-effects (i.e. don't have the sub-modules export anything but manipulate the global angular object).
This seems to be the most common solution but kind of flies in the face of having modules at all. This seems to be the most pragmatic approach however and if you're using AngularJS you're already polluting globals so I guess having purely side-effect based modules is the least of your architectural problems.
Concatenate your AngularJS app code before passing it to Browserify.
This is the most literal solution to "let's combine AngularJS and Browserify". It's a valid approach if you're starting from the traditional "just blindly concatenate your app files" position of AngularJS and want to add Browserify for third-party libs, so I guess that makes it valid.
As far as your app structure goes this doesn't really improve anything by adding Browserify, though.
Like 1 but with each index.js defining its own AngularJS sub-module.
This is the boilerplate approach suggested by Brian Ogden. This suffers from all the drawbacks of 1 but creates some semblance of hierarchy within AngularJS in that at least you have more than one AngularJS module and the AngularJS module names actually correspond to your directory structure.
However the major drawback is that you now have two sets of namespaces to worry about (your actual modules and your AngularJS modules) but nothing enforcing consistency between them. Not only do you have to remember to import the right modules (which again purely rely on side-effects) but you also have to remember to add them to all the right lists and add the same boilerplate to every new file. This makes refactoring incredibly unwieldy and makes this the worst option in my opinion.
If I had to chose today, I would go with 2 because it gives up all pretense of AngularJS and Browserify being able to be unified and just leaves both to do their own thing. Plus if you already have an AngularJS build system it literally just means adding an extra step for Browserify.
If you're not inheriting an AngularJS code base and want to know which approach works best for starting a new project instead: don't start a new project in AngularJS. Either pick Angular2 which supports a real module system out of the box, or switch to React or Ember which don't suffer from this problem.
I was trying to use browserify with Angular but found it got a bit messy. I didn't like the pattern of creating a named service / controller then requiring it from another location, e.g.
angular.module('myApp').controller('charts', require('./charts'));
The controller name / definition is in one file, but the function itself is in another. Also having lots of index.js files makes it really confusing if you lots of files open in an IDE.
So I put together this gulp plugin, gulp-require-angular which allows you write Angular using standard Angular syntax, all js files which contain angular modules and dependencies of angular modules which appear in your main module dependency tree are require()'d into a generated entry file, which you then use as your browserify entry file.
You can still use require() within your code base to pull in external libraries (e.g lodash) into services / filters / directives as needed.
Here's the latest Angular seed forked and updated to use gulp-require-angular.
I've used a hybrid approach much like pluma. I created ng-modules like so:
var name = 'app.core'
angular.module(name, [])
.service('srvc', ['$rootScope', '$http', require( './path/to/srvc' ))
.service('srvc2', ['$rootScope', '$http', require( './path/to/srvc2' ))
.config...
.etc
module.exports = name
I think the difference is that I don't define individual ng-modules as dependencies to the main ng-module, in this case I wouldn't define a Service as an ng-module and then list it as a dep of the app.core ng-module. I try to keep it as flat as possible:
//srvc.js - see below
module.exports = function( $rootScope, $http )
{
var api = {};
api.getAppData = function(){ ... }
api.doSomething = function(){ ... }
return api;
}
Regarding the comment of code-smell, I disagree. While it's an extra step, it allows for some great configurability in terms of testing against mock-services. For instance I use this quite a bit for testing agains services that might not have an existing server-API ready:
angular.module(name, [])
// .service('srvc', ['$rootScope', '$http', require( './path/to/srvc' ))
.service('srvc', ['$rootScope', '$http', require( './path/to/mockSrvc' ))
So any controller or object dependent on srvc doesn't know which it is getting. I could see this getting a bit convoluted in terms of services being dependent on other services, but that to me is bad design. I prefer to use ng's event system to communicate betw. services so that you keep their coupling down.
Alan Plum's answer is just not a great answer or at least not a great demonstration of CommonJS modules and Browserify with Angular. The claim that Browserify does not mix well with Angular, compared to React is just not true.
Browserify and a CommonJS module pattern work great with Angular, allowing you to organize by features instead of types, keep vars out of global scope and share Angular Modules across apps easily. Not to mention you do not need to ever add a single <script> to your HTML ever again thanks to Browserify finding all your dependencies.
What is particular flawed in Alan Plum's answer is not letting requires in each index.js for each folder dictate dependencies for Angular modules, controllers, services, configurations, routes etc. There is no need for a single require in the Angular.module instantiation, nor a single module.exports as in the context that Alan Plum's answer suggests.
See here for a better module pattern for Angular using Browserify: https://github.com/Sweetog/yet-another-angular-boilerplate

How to structure a JavaScript project?

I'm currently working on a big JavaScript project for which we want to define our own API. I'm using RequireJS as my dependency loader and it suits me just fine, allowing me to define modules in their respective file. I do not make use of my own namespace, a module returns an instance, which can be used in other modules, i.e.:
define(
['imported_module'],
function(module){
module.doSomething();
}
)
However as the number of files grows, I'd like to decide how to structure these files in folders. Currently I use the following scheme to name my files:
[projectname].[packagename].[ModuleName]
An example could be stackoverflow.util.HashMap.js. I would like to introduce a project folder, a folder per package and rename the files to the module name, like:
stackoverflow/util/HashMap.js
This structures my code quite neatly into folders, however the filename reflects only the module now. I'd like to define some kind of routing to be able to define how RequireJS should look for files. Example:
The file
stackoverflow/util/stackoverflow.util.HashMap.js
Should be importable by the statement
define(['stackoverflow.util.HashMap'],function(HashMap){});
Has anyone experience with structuring large JavaScript projects and if so, could you share your approach?
You shouldn't specify the routing info on your js file names, those are the namespace and folder paths' jobs. So stackoverflow/util/HashMap.js is just fine. And you can use define("stackoverflow/util/HashMap", ....) to tell the dependency.
If you need to put your modules in a different folders, you can config paths for your loader, see this manual from RequireJS API.
There's no best way for structure your js files. But put the root namespace in a src folder is always a good practice. You can see the dojo source code and YUI source code and use similar ways for your project. They both are large scale Javascript projects.
actually it's better to get js lib routing to load all js using standard interface: "js.yoursite.com/lib-0.2.js" there should be a router (php or other, and able to cache queries). So there you could determine and control whole pathes that you use. Because common jquery plugin should stay at one dir, with jquery, and your own custom plugins not.
And there you control each project by it's own rules:
jquery/
plugins/
jquery.prettyPhoto.js
jquery.min.js
mySuperJS/
stable.0/ -- there your production version for 1.0 branch
module.js
0.1/
module.js
0.2/
module.js
0.3/
module.js
myOtherlib/
stable.0/ -- production version for all 0.* versions
stable.1/ -- production version for all 1.0 versions
0.1/
0.2/
0.3/
0.4/
0.4.1/
0.4.1.18/
We're using such structure around a year and it's the best for us. But sometimes we use more complex solution and separate all modules for libs, plugins, tools, components and apps.

Categories