NOTE:
This article assumes that you are familiar with bothrequirejs
andangularjs
Live demo is available here : http://thaiat.github.io/directory-angular-ionic-require
Hi all,
I’ve been working with angularjs
for the past 3 months.
I like very much the framework, but i had a few issues with what would be the best way to organize the code when writing a large application. Especially i didn’t like editing the index.html each time i would write a new directive, controller or service. So i looked at requirejs
and started to think how the 2 libraries would play together.
In a nutshell here is my attempt to combine both angularjs
and requirejs
.
Some may argue that this is unnecessary, but i wanted to explain my approach and see what is the feedback.
The main reason i needed that is that i’m still using Visual Studio as my editor (yes… i know…).
Running the unit test with jasmine in Visual Studio works without an html page, so i needed a way with pure javascript files to link together all my unit tests. requirejs
does exactly that…
So, first, let’s see the file structure.
File and folder structure
As per the best practices in angular, i wanted to organize my code by features.
So the folder structure looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
At the root we have scripts and styles, nothing surprising there.
In the subfolder scripts/libs
, i usually put all the external javascript libraries.
I like to create a subfolder underneath which is named as the current version number of the library (x.y.z).
For example:
1 2 3 4 5 6 7 |
|
This makes it super easy to try a new version of the library and see if it works, while falling back to the previous one if the new version generates too many bugs.
Right at the root we have the index.html
.
Here is the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
As you can see it is also very simple.
It references the needed css, and has only a single script tag, pointing to requirejs
and defining scripts/main.js
as the entry point for the application.
NOTE:
Note that the.js
extension is omitted when referencing a javascript file withrequirejs
main.js
This file first defines the libraries needed by the application. We will see later on how we start the application using require entry point function.
So for the moment the content is pretty simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
feature folder
For the purpose of the explanation let’s call this feature folder area
.
Before we review how we can start the application, let’s first see how a feature is organized.
For each feature we will define a folder containing the following:
area/namespace.js
This file is a returning the namespace of the feature.
1 2 3 4 |
|
Note that I usually put in the app folder a root namespace looking like this:
1 2 3 |
|
In the namespace.js of a specific feature i can require this top root namespace to chain the namespaces. area/namespace.js becomes:
1 2 3 4 5 6 |
|
I find this elegant because the actual name of the namespace are only hard coded once. I could decide later on to change the namespace and this will not impact any other part of the application…
Next to namespace.js we define 2 others files : module.js
and module.require.js
.
module.js
simply defines a specific module for the feature:
1 2 3 4 5 6 7 8 9 10 |
|
See how we reference the namespace for the feature instead of duplicating the hard coded namespace.
module.require.js
is the file responsible for binding together all the files we will put in our feature module (filters, directives, services, controllers, etc…)
To achieve a clean separation between those files, we will create sub folders per nature inside the feature folder:
1 2 3 4 5 6 7 |
|
Each of these sub folders contains a file per component, plus an _index.js
file referencing all of them.
To better understand let see how the services folder looks like:
area/services/_index.js
1 2 3 4 5 |
|
area/services/myService.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Note that we are again referencing the namespace in order to prefix the name of the service with it.
If you do not want your service to be in a specific namespace you can just ignore that and write:
1
|
|
area/module.require.js
This file simply references all the _index.js per component type
1 2 3 4 5 6 7 8 9 10 11 |
|
Now lets starts our application.
We will need an app.js file in scripts/app
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
We are requiring the feature module.require.js
file to make sure all the files making the feature are loaded. And in addition we are also referencing the namespace of the application and the namespace of the feature in order to prevent hard coding again their values.
The only thing left is to bind main.js
with app.js
using the following code
at the end of main.js
1 2 3 4 5 6 7 8 9 10 |
|
This may seem over complicated, but once you understand how it works it is really easy to expand.
Each time i need a new “feature”, i’m copying a dummy feature structure, set the namespace, and reference the module.require of the feature in the main app.js
.
Whenever i need a new component inside the feature, i put it in the correct location based on its type, and edit the corresponding _index.js
.
To better illustrate these concepts you can look at this repository.
Live demo is available here : http://thaiat.github.io/directory-angular-ionic-require
I have re-written the Sample Mobile Application of Christophe Coenraets using these technics (see http://coenraets.org/blog/2014/02/sample-mobile-application-with-ionic-and-angularjs/)
Let me know what you think and happy coding.
Avi Haiat