Table of content

Project Overview

General

├── webapp
│   ├── app/
│   │   └── ...
│   ├── docs/
│   │   └── ...
│   ├── node_modules/
│   │   └── ...
│   ├── public/
│   │   └── ...
│   ├── .gitignore
│   ├── gulpfile.js
│   ├── LICENSE
│   ├── package.json
│   └── README.md
  • app/ contains all AngularJS app files.
  • docs/ contains all documentation app files [generated automatically with npm start].
    • This directory is generated by the gulpfile.js.
    • Files in this directory are generated based on @ngdoc comments in app/ files.
  • node_modules/ contains all NodeJS modules [generated automatically with npm install].
    • Modules in this directory are installed based on the package.json dependencies.
  • public/ contains all finals documents to be rendered by the client browser [generated automatically with npm start].
    • Files in this directory are generated by the gulpfile.js.

More information about Gulp in the Public project generation process section.

App architecture

├── app
│   ├── components/
│   │   ├── component1/
│   │   │   ├── component1.js
│   │   │   ├── component1.html
│   │   │   └── Component1Ctrl.js
│   │   ├── component2/
│   │   │   ├── component2Sub/
│   │   │   │   ├── component2Sub.js
│   │   │   │   ├── component2Sub.html
│   │   │   │   └── Component2SubCtrl.js
│   │   │   ├── component2.js
│   │   │   └── Component2Ctrl.js
│   │   └── ...
│   ├── config/
│   │   ├── Config1.js
│   │   └── ...
│   ├── services/
│   │   ├── models/
│   │   │   ├── Model1.js
│   │   │   └── ...
│   │   ├── Service1.js
│   │   ├── Service2.js
│   │   └── ...
│   ├── app.js
│   ├── index.html
│   ├── index.ngdoc
│   └── style.scss
  • components/ contains all app components files.
  • config/ contains all app configuration files.
  • services/ contains all app services files.

Global app files

app.js

It is the main file of the app. It requires all app dependencies (such as firebase, angular-material, etc…) and loads all javascript files present in the app/ directory (components, services…).

This is also where we create the app module (module setter) :

// first parameter is the module name and the second is the array of module dependencies
angular.module('app', ['ngMaterial', 'firebase'])

Then we can access to the module in every files by using (module getter):

angular.module('app') // juste one parameter

So the app.js must follow this logic:

// Require external dependencies
require('angular-material')
require('firebase')

// Module setter
angular.module('app', ['ngMaterial', 'firebase'])

// Require app files (components, services...)
require('./**/*.js')

index.html

This is the index html file of our app which will be rendered by the client browser.

A basic html template would be:

<!DOCTYPE html>
<html ng-app="app">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
        <title>APP</title>
        <link rel="stylesheet" href="css/style.css">
        <script src="js/main.js"></script>
    </head>
    <body ng-cloak>
        <xx-app></xx-app>
    </body>
</html>
  • You may notice the use of a custom component: xxApp.
  • ngApp directive links your AngularJS project with the current html file. - Documentation
  • ngCloak directive waits that your AngularJS app is loaded before rendering the view. - Documentation

index.ngdoc

This is the index file of the documentation part. More details in the corresponding section.

style.scss

This is the index file of the documentation part. More details on Sass on the official website.

Components

An AngularJS component can be decomposed in three parts : a view, a controller and the component itself.

The component itself

In AngularsJS a component is a simple object which can be declared like this:

angular.module('app').component('xxComponentName', {
    // component properties and methods...
})

Then this component can be used in an html file like a classic html tag:

<div>
  <!-- whatever html .... -->
  <xx-component-name></xx-component-name>
  <!-- whatever html ....-->
</div>

:white_check_mark: BEST PRACTICE: When you define your component be sure to add a prefix like the xx to the name of your component to avoid having the same name than an existing html tag.

Some important component’s properties:

  • controller

To link the component with its controller

angular.module('app').component('xxComponentName', {
    controller: 'ComponentNameCtrl'
    // other component properties and methods...
})
  • bindings

To link component’s html attributes to the controller. Input and output attributes are respectively declared with < and &. Inputs are used to pass data to the component from the parent component and output are used to transmit internal component event to the parent component.

If you have something like this:

angular.module('app').component('xxComponentName', {
  bindings: {
      attrInput: '<', // You will have access to the attrInput property inside your controller
      attrOutput: '&' // You will have access to the attrOutput method inside your controller
  }
    // other component properties and methods...
})

Then you can add corresponding attributes:


<xx-component-name attrInput="$ctrl.something" attrOutput="$ctrl.onEvent()"></xx-component-name>

  • templateUrl

This is the path of the html file corresponding to the component’s view.

angular.module('app').component('xxComponentName', {
  templateUrl: 'componentName.html'
    // other component properties and methods...
})

What if we want add some attributes to our component like a CSS class for example? Can we do it? YES! The solution is to inject $element into our templateUrl property and the add the class to the element (do not forget to return the name of the html template at the end).

angular.module('app').component('xxComponentName', {
  templateUrl: ['$element', function($element) {
      angular.element($element).addClass('someCSSClass')
      return 'componentName.html'
  }]
    // other component properties and methods...
})
  • $canActivate

$canActivate is a function called before the component is rendered to the user. It can be really useful to show a component only if a user is connected for example (by injecting the corresponding service in the function). This function must return a Promise, for more information about Promise see the next section.

angular.module('app').component('xxComponentName', {
  $canActivate: ['AuthService', function(AuthService) {
        return AuthService.requireAuth()
  }]
    // other component properties and methods...
})
  • $routeConfig

We will see this property in details in the Routing by components section.

So finally a complete component could look like that:

angular.module('app').component('xxComponentName', {
  controller: 'ComponentNameCtrl',
  bindings: {
      attrInput: '<',
      attrOutput: '&'
  },
  templateUrl: ['$element', function($element) {
      angular.element($element).addClass('someCSSClass')
      return 'componentName.html'
  }],
  $canActivate: ['AuthService', function(AuthService) {
        return AuthService.requireAuth()
  }]
    // other component properties and methods...
})

There are more properties and methods available for a component so read the official AngularJS documentation.

The component’s controller

In AngularsJS a controller is a simple function which can be declared like this:

angular.module('app').controller('ComponentNameCtrl', ComponentNameCtrl)

function ComponentNameCtrl() {
  var vm = this

  vm.someProperty = "Hey Gautch!"

  vm.someMethod = function() {
    console.log("I'm a method of ComponentNameCtrl and my single property is "+vm.someProperty)
  }

  // other properties and methods...
}

A controller is in charge of a view and links the view with app services. It ensure the UI’s data is sync with the services’s data.

:white_check_mark: BEST PRACTICE: Never use $rootScope or $scope inside a controller (or even worth inside a service). You can always take another way.

The component’s view

This is a simple html file which render controllers data. To use controller’s properties and methods we have access to the $ctrl object.

<button ng-repeat="item in $ctrl.list" ng-click="$ctrl.clicked()">
  {{item.name}}
</button>
  • ngRepeat directive creates one template per item . - Documentation
  • ngClick directive raises the event when the element is clicked - Documentation

Services

In AngularsJS a service is a singleton which can be declared like this:

angular.module('app').factory('SimpleService', SimpleService)

function SimpleService() {
    var SimpleService = {}

    SimpleService.someProperty = "Hey you!"

    SimpleService.someMethod = function() {
      console.log("I'm a method of SimpleService and my single property is "+SimpleService.someProperty)
    }

    // other properties and methods...

    return SimpleService

Services can be injected into a controller in order to take off some business logic in the controller. The controller must always have the fewest business logic possible.

An example of service injection into a controller:

angular.module('app').controller('ComponentNameCtrl', ComponentNameCtrl)
ComponentNameCtrl.$inject = ['SimpleService']

function ComponentNameCtrl(SimpleService) {
  var vm = this

  vm.someProperty = "Hey Gautch!"

  vm.someMethod = function() {
    SimpleService.someMethod()
  }

  // other properties and methods...
}

:white_check_mark: BEST PRACTICE: Always use a dedicated service to create your models or to execute HTTP requests.

Config

This is only for configuration stuff. It can be useful to customize external modules.

Routing

The routing is the way of making a link between some path and app’s views. In an AngularJS Component Oriented Design project we use obviously a routing based on components.

Routing by components

Routing by components is really simple in Angular. You just have to add the $routeConfig property in a component and explicit all routes.

A route is characterized by a path, a name and a component. A typical route declaration looks like that:

{path: '/login', name:'Login', component: 'xxLogin'}

So the declaration looks like that:

angular.module('app').component('xxComponentName', {
  $routeConfig: [
      {path: '/login', name:'Login', component: 'xxLogin'}
      // other routes...
    ]
    // other component properties and methods...
})

Then you just have to add the ngOutlet directive to your template where you want add the routed component.

<div>
  <h3>The component under me will be updated on route change.</h3>
  <ng-outlet></ng-outlet>
</div>

During the routing the parent component share the $router object to the current component so we just have to bind it like that:

angular.module('app').component('xxLogin', {
  controller:'LoginCtrl',
  bindings: {
      $router: '<'
  }
    // other component properties and methods...
})

Then in the controller we can use the $router object to navigate in the app:

angular.module('app').controller('LoginCtrl', LoginCtrl)

function LoginCtrl() {
  var vm = this

  vm.goHome = function() {
    vm.$router.navigate(['Home'])
  }
  // other properties and methods...
}

Another way is to inject the $rootRouter into the controller and then to give the complete route name from the root component like this:

angular.module('app').controller('LoginCtrl', LoginCtrl)
LoginCtrl.$inject = ['$rootRouter']

function LoginCtrl($rootRouter) {
  var vm = this

  vm.goHome = function() {
    $rootRouter.navigate(['App', 'ParentComponent', 'Home'])
  }
  // other properties and methods...
}

:heavy_exclamation_mark: NOTICE: If the routed component has also a $routeConfig property then you must add three dots at the route’s path like that:

{path: '/login/...', name:'Login', component: 'xxLogin'}

:white_check_mark: BEST PRACTICE: Always add the attribute useAsDefault to a route in order to choose this route if any routes is matched with the given path.

{path: '/error', name:'Error', component: 'xxError', useAsDefault: true}

Now we can build a great and complete component like this:

angular.module('app').component('xxComponentName', {
  controller: 'ComponentNameCtrl',
  bindings: {
      attrInput: '<',
      $router: '<',
      attrOutput: '&'
  },
  templateUrl: ['$element', function($element) {
      angular.element($element).addClass('someCSSClass')
      return 'componentName.html'
  }],
  $canActivate: ['AuthService', function(AuthService) {
        return AuthService.requireAuth()
  }],
  $routeConfig: [
      {path: '/login', name:'Login', component: 'xxLogin'},
      {path: '/error', name:'Error', component: 'xxError', useAsDefault: true}
    ]
})

Miscellaneous

Public project generation process

Gulp website

Javascript concepts

Callback

Promise

Documentation

Documentation

Write Documentation

Build documentation