JavaScript + Angular 

Part 9

Author: Andrey Kucherenko

bootstrapping, structure, code styles

bootstrapping

git clone https://github.com/angular/angular-seed.git

npm install

npm start
app/                    --> all of the source files for the application
  app.css               --> default stylesheet
  components/           --> all app specific modules
    version/              --> version related components
      version.js                 --> version module declaration and basic "version" value service
      version_test.js            --> "version" value service tests
      version-directive.js       --> custom directive that returns the current app version
      version-directive_test.js  --> version directive tests
      interpolate-filter.js      --> custom interpolation filter
      interpolate-filter_test.js --> interpolate filter tests
  view1/                --> the view1 view template and logic
    view1.html            --> the partial template
    view1.js              --> the controller logic
    view1_test.js         --> tests of the controller
  view2/                --> the view2 view template and logic
    view2.html            --> the partial template
    view2.js              --> the controller logic
    view2_test.js         --> tests of the controller
  app.js                --> main application module
  index.html            --> app layout file (the main html template file of the app)
  index-async.html      --> just like index.html, but loads js files asynchronously
karma.conf.js         --> config file for running unit tests with Karma
e2e-tests/            --> end-to-end tests
  protractor-conf.js    --> Protractor config file
  scenarios.js          --> end-to-end scenarios to be run by Protractor
npm install -g yo 

npm install -g generator-angular-fullstack

npm install -g gulp-cli generator-gulp-angular
➜ yo gulp-angular


Make sure you are in the directory you want to scaffold into.
This generator can also be run with: yo gulp-angular


     _-----_
    |       |    .--------------------------.
    |--(o)--|    |         Welcome!         |
   `---------´   |                          |
    ( _´U`_ )    |     You're using the     |
    /___A___\    |  fantastic generator for |
     |  ~  |     |      scaffolding an      |
   __'.___.'__   | application with Angular |
 ´   `  |° ´ Y ` |         and Gulp!        |
                 '--------------------------'

? May generator-gulp-angular anonymously report usage statistics to improve the tool over time? Yes
? Which version of Angular do you want? 1.5.x (stable)
? What Angular modules would you like to have? (ngRoute and ngResource will be addressed after) (Press <space> to select)angular-animate.js (enable animation features), angular-cookies.js (handle cookie man
agement), angular-touch.js (for mobile development), angular-sanitize.js (to securely parse and manipulate HTML), angular-messages.js (enhanced support for displaying messages within templates), angular-ari
a.js (support for common ARIA attributes)
? Do you need jQuery or perhaps Zepto? None (Angular will use its own jqLite)
? Would you like to use a REST resource library? ngResource, the official support for RESTful services
? Would you like to use a router? UI Router, flexible routing with nested views
? Which UI framework do you want? Foundation, "The most advanced responsive front-end framework in the world"
? How do you want to implement your Foundation components? Angular Foundation, a set of native AngularJS directives based on Foundation's markup and CSS
? Which CSS preprocessor do you want? None, only the good old CSS
? Which JS preprocessor do you want? ES6 (Babel formerly 6to5), ECMAScript 6 compiled with Babel which requires no runtime.
? Which HTML template engine would you want? None, I like to code in standard HTML.
├──  bower_components/
├──  e2e/
├──  gulp/
├──  nodes_modules/
├──  src/
│   ├──  app/
│   │   ├──  components/
│   │   │   └──  githubContributor/
│   │   │   │   └──  githubContributor.service.js
│   │   │   └──  navbar/
│   │   │   │   ├──  navbar.directive.js
│   │   │   │   ├──  navbar.html
│   │   │   │   └──  navbar.css
│   │   │   │
│   │   │   └──  webDevTec/
│   │   │       └──  webDevTec.service.js
│   │   ├──  main/
│   │   │   ├──  main.controller.js
│   │   │   ├──  main.controller.spec.js
│   │   │   └──  main.html
│   │   │
│   │   └──  index.config.js
│   │   └──  index.constants.js
│   │   └──  index.module.js
│   │   └──  index.route.js
│   │   └──  index.run.js
│   │   └──  index.css
│   ├──  assets/
│   ├──  favico.ico
│   └──  index.html
├──  .bowerrc
├──  .editorconfig
├──  .gitignore
├──  .eslintrc
├──  bower.json
├──  gulpfile.js
├──  karma.conf.js
├──  package.json
└──  protractor.conf.js
gulp serve
(node:16503) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
[19:07:16] Using gulpfile ~/workspace/tmp/angular-yo/gulpfile.js
[19:07:16] Starting 'scripts'...
[19:07:17] Time: 1438ms
          Asset     Size  Chunks             Chunk Names
index.module.js  14.4 kB       0  [emitted]  main
[19:07:17] Finished 'scripts' after 1.57 s
[19:07:17] Starting 'scripts:watch'...
[19:07:17] Starting 'inject'...
[19:07:17] gulp-inject 3 files into index.html.
[19:07:17] gulp-inject 1 files into index.html.
[19:07:17] Finished 'inject' after 61 ms
[19:07:18] Time: 496ms
          Asset     Size  Chunks             Chunk Names
index.module.js  34.8 kB       0  [emitted]  main
[19:07:18] Finished 'scripts:watch' after 515 ms
[19:07:18] Starting 'watch'...
[19:07:18] Finished 'watch' after 15 ms
[19:07:18] Starting 'serve'...
[19:07:18] Finished 'serve' after 21 ms
[19:07:18] webpack is watching for changes
[BS] [BrowserSync SPA] Running...
[BS] Access URLs:
 --------------------------------------
       Local: http://localhost:3000/
   
 --------------------------------------
          UI: http://localhost:3001
yo angular-fullstack

     _-----_
    |       |
    |--(o)--|   .--------------------------.
   `---------´  |    Welcome to Yeoman,    |
    ( _´U`_ )   |   ladies and gentlemen!  |
    /___A___\   '__________________________'
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

Out of the box I create an AngularJS app with an Express server.

# Client

? What would you like to write scripts with? Babel
? Would you like to use Flow types with Babel? Yes
? What would you like to write markup with? HTML
? What would you like to write stylesheets with? CSS
? What Angular router would you like to use? uiRouter
? Would you like to include Bootstrap? No

# Server

? What would you like to use for data modeling? Sequelize (MySQL, SQLite, MariaDB, PostgreSQL)
? Would you scaffold out an authentication boilerplate? No
? Would you like to use socket.io? No

# Project

? What would you like to write tests with? Mocha + Chai + Sinon
? What would you like to write Chai assertions with? Should
You're using the fantastic NgComponent generator.

Initializing yo-rc.json configuration.
npm install -g cordova ionic

ionic start angular-ionic sidemenu
more recent version.
Creating Ionic app in folder /home/apk/workspace/tmp/angular-ionic based on sidemenu project
Downloading: https://github.com/driftyco/ionic-app-base/archive/master.zip
[=============================]  100%  0.0s
Downloading: https://github.com/driftyco/ionic-starter-sidemenu/archive/master.zip
[=============================]  100%  0.0s
Updated the hooks directory to have execute permissions
Update Config.xml
Initializing cordova project

♬ ♫ ♬ ♫  Your Ionic app is ready to go! ♬ ♫ ♬ ♫

Make sure to cd into your new app directory:
  cd angular-ionic

To run your app in the browser (great for initial development):
  ionic serve

To run on iOS:
  ionic run ios

To run on Android:
  ionic run android

To test your app on a device easily, try Ionic View:
  http://view.ionic.io

Create an ionic.io account to send Push Notifications and use the Ionic View app?
(Y/n): n

+---------------------------------------------------------+
+ Extra! Extra! Fresh Ionic updates for August 2016
+
+ Ionic 2 Beta is out! Try the next generation of Ionic
+ http://ionicframework.com/docs/v2/getting-started/installation/
+
+ Test and share your Ionic apps easily with Ionic View
+ http://view.ionic.io
+
+ Building enterprise apps? Ionic Enterprise Framework comes with the support and features you need
+ http://ionic.io/enterprise
+
+---------------------------------------------------------+

Tasks

  • Create different project setups with yeoman, angular-seed and ionic
  • Try run each setups

tools

Builds

  • grunt
  • gulp
  • webpack

linting

  • jslint - outdated
  • jshint
  • eslint

CI

  • travis
  • jenkins
  • teamcity

testing

  • jasmine
  • mocha+chai
  • protractor

debugging

  • batarang
  • ng-inspector

Miscellaneous

  • jscpd
  • jsinspect
  • editorconfig
  • jsdoc
  • jscs

Tasks

  • Inspect your application with ng-inspector
  • Inspect your application with batarang

style guides

https://github.com/johnpapa/angular-styleguide

https://github.com/mgechev/angularjs-style-guide

https://github.com/airbnb/javascript

.
├── app
│   ├── app.js
│   ├── controllers
│   │   ├── home
│   │   │   ├── FirstCtrl.js
│   │   │   └── FirstCtrl.spec.js
│   │   │   └── SecondCtrl.js
│   │   │   └── SecondCtrl.spec.js
│   │   └── about
│   │       └── ThirdCtrl.js
│   │       └── ThirdCtrl.spec.js
│   ├── directives
│   │   ├── home
│   │   │   └── directive1.js
│   │   │   └── directive1.spec.js
│   │   └── about
│   │       ├── directive2.js
│   │       ├── directive2.spec.js
│   │       └── directive3.js
│   │       └── directive3.spec.js
│   ├── filters
│   │   ├── home
│   │   └── about
│   └── services
│       ├── CommonService.js
│       ├── CommonService.spec.js
│       ├── cache
│       │   ├── Cache1.js
│       │   ├── Cache1.spec.js
│       │   └── Cache2.js
│       │   └── Cache2.spec.js
│       └── models
│           ├── Model1.spec.js
│           ├── Model1.js
│           └── Model2.spec.js
│           └── Model2.js
├── partials
├── lib
└── e2e-tests
.
├── app
│   ├── app.js
│   ├── common
│   │   ├── controllers
│   │   ├── directives
│   │   ├── filters
│   │   └── services
│   ├── home
│   │   ├── controllers
│   │   │   ├── FirstCtrl.js
│   │   │   ├── FirstCtrl.spec.js
│   │   │   └── SecondCtrl.js
│   │   │   └── SecondCtrl.spec.js
│   │   ├── directives
│   │   │   └── directive1.js
│   │   │   └── directive1.spec.js
│   │   ├── filters
│   │   │   ├── filter1.js
│   │   │   ├── filter1.spec.js
│   │   │   └── filter2.js
│   │   │   └── filter2.spec.js
│   │   └── services
│   │       ├── service1.js
│   │       ├── service1.spec.js
│   │       └── service2.js
│   │       └── service2.spec.js
│   └── about
│       ├── controllers
│       │   └── ThirdCtrl.js
│       │   └── ThirdCtrl.spec.js
│       ├── directives
│       │   ├── directive2.js
│       │   ├── directive2.spec.js
│       │   └── directive3.js
│       │   └── directive3.spec.js
│       ├── filters
│       │   └── filter3.js
│       │   └── filter3.spec.js
│       └── services
│           └── service3.js
│           └── service3.spec.js
├── partials
├── lib
└── e2e-tests
/* avoid */
angular
    .module('app', ['ngRoute'])
    .controller('SomeController', SomeController)
    .factory('someFactory', someFactory);

function SomeController() { }

function someFactory() { }

/* recommended */
// app.module.js
angular
    .module('app', ['ngRoute']);

/* recommended */
// some.controller.js
angular
    .module('app')
    .controller('SomeController', SomeController);

function SomeController() { }

/* recommended */
// some.factory.js
angular
    .module('app')
    .factory('someFactory', someFactory);

function someFactory() { }

Define 1 component per file, recommended to be less than 400 lines of code.

/* avoid */
var app = angular.module('app', [
    'ngAnimate',
    'ngRoute',
    'app.shared',
    'app.dashboard'
]);


/* recommended */
angular
    .module('app', [
        'ngAnimate',
        'ngRoute',
        'app.shared',
        'app.dashboard'
    ]);

Declare modules without a variable using the setter syntax.

/* avoid */
angular
    .module('app')
    .controller('DashboardController', function() { })
    .factory('logger', function() { });

/* recommended */
// dashboard.js
angular
    .module('app')
    .controller('DashboardController', DashboardController);

function DashboardController() { }

// logger.js
angular
    .module('app')
    .factory('logger', logger);

function logger() { }

Use named functions instead of passing an anonymous function in as a callback.

<!-- avoid -->
<div ng-controller="CustomerController">
    {{ name }}
</div>


<!-- recommended -->
<div ng-controller="CustomerController as customer">
    {{ customer.name }}
</div>

Use the controllerAs syntax over the classic controller with $scope syntax

/* avoid */
function CustomerController($scope) {
    $scope.name = {};
    $scope.sendMessage = function() { };
}

/* recommended */
function CustomerController() {
    this.name = {};
    this.sendMessage = function() { };
}

Use the controllerAs syntax over the classic controller with $scope syntax.

/* avoid */
function CustomerController() {
    this.name = {};
    this.sendMessage = function() { };
}

/* recommended */
function CustomerController() {
    var vm = this;
    vm.name = {};
    vm.sendMessage = function() { };
}

Use a capture variable for this when using the controllerAs syntax. Choose a consistent variable name such asvm, which stands for ViewModel.

/* avoid */
function SessionsController() {
    var vm = this;

    vm.gotoSession = function() {
      /* ... */
    };
    vm.refresh = function() {
      /* ... */
    };
    vm.search = function() {
      /* ... */
    };
    vm.sessions = [];
    vm.title = 'Sessions';
}

Place bindable members at the top of the controller, alphabetized, and not spread through the controller code.

/* recommended */
function SessionsController() {
    var vm = this;

    vm.gotoSession = gotoSession;
    vm.refresh = refresh;
    vm.search = search;
    vm.sessions = [];
    vm.title = 'Sessions';

    ////////////

    function gotoSession() {
      /* */
    }

    function refresh() {
      /* */
    }

    function search() {
      /* */
    }
}
/* avoid */
function OrderController($http, $q, config, userInfo) {
    var vm = this;
    vm.checkCredit = checkCredit;
    vm.isCreditOk;
    vm.total = 0;

    function checkCredit() {
        var settings = {};
        // Get the credit service base URL from config
        // Set credit service required headers
        // Prepare URL query string or data object with request data
        // Add user-identifying info so service gets the right credit limit for this user.
        // Use JSONP for this browser if it doesn't support CORS
        return $http.get(settings)
            .then(function(data) {
             // Unpack JSON data in the response object
               // to find maxRemainingAmount
               vm.isCreditOk = vm.total <= maxRemainingAmount
            })
            .catch(function(error) {
               // Interpret error
               // Cope w/ timeout? retry? try alternate service?
               // Re-reject with appropriate error for a user to see
            });
    };
}

Defer logic in a controller by delegating to services and factories.

/* recommended */
function OrderController(creditService) {
    var vm = this;
    vm.checkCredit = checkCredit;
    vm.isCreditOk;
    vm.total = 0;

    function checkCredit() {
       return creditService.isOrderTotalOk(vm.total)
          .then(function(isOk) { vm.isCreditOk = isOk; })
          .catch(showError);
    };
}
/* avoid - when using with a route and dynamic pairing is desired */

// route-config.js
angular
    .module('app')
    .config(config);

function config($routeProvider) {
    $routeProvider
        .when('/avengers', {
          templateUrl: 'avengers.html'
        });
}
<!-- avengers.html -->
<div ng-controller="AvengersController as vm">
</div>

When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes.

/* recommended */

// route-config.js
angular
    .module('app')
    .config(config);

function config($routeProvider) {
    $routeProvider
        .when('/avengers', {
            templateUrl: 'avengers.html',
            controller: 'Avengers',
            controllerAs: 'vm'
        });
}
<!-- avengers.html -->
<div>
</div>

Tasks

  • Refactor application with style guides

Pet Project

JavaScript + Angular Course (part 9)

By Andrey Kucherenko

JavaScript + Angular Course (part 9)

  • 2,043