I try to build a modular application using AngularJS. My first idea is to group each module by functionnality with this kind of folder structure :
/core
controllers.js
directives.js
app.js
/modules
/users
controllers.js
directives.js
/invoices
controllers.js
directives.js
/messages
controllers.js
directives.js
...
Notice that the "core" folder contains basics features that will always be in the app. Others modules can be add or remove independently.
As my application will be large, I also want to use lazy loading. I implemented this solution : http://ify.io/lazy-loading-in-angularjs/ which seems to me actually the easiest way. The problem is that it only does lazy loading for controllers, services, directives ... but not for AngularJS modules.
I know there is another way which permits to lazy load Angular modules (http://blog.getelementsbyidea.com/load-a-module-on-demand-with-angularjs/) but I think it's way too hacky as it uses Angular's core methods.
My question is : does it make sense in my case to use different AngularJS modules for each of my modules, like this :
angular.module('core', ['ngRoute', 'users', 'invoices', 'messages'])
angular.module('users')
angular.module('invoices')
angular.module('messages')
What is the advantage of this approach ? Are AngularJS modules usefull - for now - only for third-party modules for Angular ?
I'm asking this since AngularJS 2.0 will support natively lazy loading. Miško Hevery from Google says "[you] should group by view since views will be lazy loaded in near future", and that we should use one module per application, see it here : https://www.youtube.com/watch?v=ZhfUv0spHCY&t=34m19s
Is it correct for a large application to just use one module for my app like this :
angular.module('core', ['ngRoute']);
And then lazy load my controllers, services and directives based on a route or a view ?
We have a similar structure (on a very large app) to the one you are proposing here, except don't have controllers,services and the like bundled up into single .js files. Instead we just separate our concerns with views, services and the like all bundled into a single module. So each meaningful component might looks like:
/my-component
- i-do-something.js
- a-view.html
- a-view-that-is-a-child.html
- a-view-ctrl.js
- index.js //the thing that creates a module and returns it
/ tests
- i-do-something-spec.js
- a-view-ctrl-spec.js
This is all bundled up into a single app module (named for our org). Then a simple boot module angular.bootstrap(['app'])s the whole thing.
We use browserify to compile all this stuff and it has worked nicely so far.
Related
I am trying to serve a directive from a /common/ folder in my project when I have an app1 and app2 folders as siblings of /common/ that will ideally serve the same directive. With partials and services, it was easy to just include them with the correct path - but how do I use the same directive in two different apps?
I'm getting stuck where I need to declare what module the directive belongs to - but I don't want to do that because it can change based on the app (app1.directive() and app2.directive() etc.)
Okay I figured it out. Here is how it's done:
When you're building the service:
angular.module('app.directiveName').directive(...);
In the app you need to declare it in the dependencies:
['app.directiveName']
Lastly, in the app, you need to include the module in the app:
angular.module('app.directiveName', []);
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
In a very basic Angular app we have
<head>...
<script src="app.js"></script>
<script src="maincontroller.js"></script>
App defines the app module, and controller then hangs off app as app.controller("MainController..") If the order of the two scripts is reversed, the app will throw an error "app is not defined".
Is there a way around this load order dependency? My fear is that as it becomes more complex, I will get script order dependencies. And perhaps I might want to load my scripts async as well.
Thank you.
See, for example, this plunker: http://plnkr.co/edit/kqVqTHxl4tc6mIV5bDbQ
I've defined SomeService in an own file, svc.js. I've defined the module and the main controller in app.js. And even though the MainController depends on that service that "loads later", the dependency is figured out, and injected. All you should worry about is: put the module definition first.
Also: Don't store the application inside a global variable called app, instead, use angular.module with the name of the module to retrieve a reference to it:
angular.module('SomeModuleName').controller(...)
Any kind of global variables are generally not a good practice.
Look at using Angular and Browserify or Angular and RequireJS. Browserify and RequireJS are module loaders that let you keep 1 script reference in your index.html.
Browserify is based on a build step that will bundle all your JS into 1 file.
RequireJS is an Asynchronous Module Definition (AMD) loader, that can load your files asynchronously.
I'd like to know if possible to separate a yeoman app by modules (like MEANJS generator) basically with the following structure:
Module 1
views
controllers
services
css
config
module1.js
Module 2
views
controllers
services
css
config
module1.js
Module 3
etc
etc
same structure
App.js
For me is more convenient to do it this way and the official angular generator doesn't support that structure
is there any way to accomplish that with an existing yeoman generator?
Maybe this generator will do the trick?
https://www.npmjs.org/package/generator-cg-angular
It follows the Angular Best Practice Guidelines for Project Structure and allows you to create submodules with their own directories.
I m trying to understand the sample AngularJS app shipped with Packpub's book. the app.js file is defined under client/src/app folder and it's module definition looks like
angular.module('app', [
'ngRoute',
'projectsinfo',
'dashboard',
'projects',
'admin',
'services.breadcrumbs',
'services.i18nNotifications',
'services.httpRequestTracker',
'security',
'directives.crud',
'templates.app',
'templates.common']);
My question is how AngularJs will find these modules and uses in app?
All those modules need to be loaded in your browser as well. AngularJS does not provide a module loader such as RequireJS.
You can either add <script> tags in the index file or concatenate all your sources into one big file. Some of the modules can be from AngularJS (such as the ngRoute). These will always be available in Angular, you do not need to load the sources in separately.