• Stars
    star
    204
  • Rank 192,063 (Top 4 %)
  • Language
  • License
    MIT License
  • Created almost 9 years ago
  • Updated about 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Angular (v 1.5) with ES6 / ES2015 Style Guide: A starting point for Angular development teams to provide consistency through good practices. http://orizens.com

Angular 1.5 - ES6/ES2015 Style Guide

Endorsements

The idea for this styleguide is inspired from john papa's angular style guide which is focused on angular version 1.x, written with ES5. Some sections are currently taken from john papa's styleguide and are subject to change in the future (as this stylguide grows). This styleguide is also inspired from @angularclass's boilerplates for angular & es2015.

Purpose

Opinionated Angular.js written with ES6/ES2015 and module loading style guide for teams by @orizens

If you are looking for an opinionated style guide for syntax, conventions, and structuring Angular.js applications v 1.x with ES2015 and module loading, then this is it. These styles are based on my development experience with Angular.js and the Echoes Player, Open Source Project Development (currently the es2015 branch).

The purpose of this style guide is to provide guidance on building Angular applications with ES2015 and module loading by showing the conventions I use and in order to prepare angular v 1.x code to angular v 2.

Inspirations

NG6-Starter Angular, Gulp, Browserify


NEW Quick Start with SuperNova Starter

Start a new angular project with SuperNova Starter which is based on this styleguide.

See the Styles in a Sample App

This guide is based on my open source Echoes Player application that follows these styles and patterns. You can clone/fork the code at echoes repository.

##Translations TBD

Table of Contents

  1. Single Responsibility
  2. Modules
  3. Components
  4. Services
  5. Testing
  6. Testing Guidelines
  7. Write Tests with Stories
  8. Testing Library
  9. Test Runner
  10. Testing Controllers
  11. Organizing Tests
  12. Comments
  13. ES Lint
  14. Use an Options File
  15. Routing
  16. Contributing
  17. License
  18. Copyright

Single Responsibility

Rule of 1

  • Define 1 class per file.

The following example defines the several classes in the same file.

// now-playlist.services.js
/* avoid */
export class NowPlaylistProvider {}
export class NowPlaylistCreator {}

The same classes are now separated into their own files.

/* recommended */

// now-playlist-provider.service.js
export default class NowPlaylistProvider {}
/* recommended */

// now-playlist-creator.service.js
export default class NowPlaylistCreator {}

Back to top

Modules

Use Proposed Loader Specification (Former ES6/ES2015)

Why?: it assists in bundling the app and promotes the seperation of concerns. In Addition, Angular 2 is also based on Loader's standards.

import NowPlaylist from './now-playlist';
import { NowPlaylistComponent } from './now-playlist.component';

Module Loaders Tools

Module Folder Structure

  • each module directory should be named with a dash seperator (kebab notation).

Why?: it follows the web-component notation of seperating a tag name with a dash. It is easier to read and follow in the code editor.

// name of directory for <now-playlist></now-playlist> component
- now-playlist

Module files

  • each module should contain the following:
  1. index.js - it should contain:
  2. module-name.component.js - a component (directive) file defintion with class as a controller
  3. template: 1. Long html template: an html template file 2. Small html template: inline backtick template in component file
  4. a spec file

index.js - module entry point

should contain:

  • the module defintion
  • components/directives angular wrappers
  • its dependencies
  • config phase & function
  • angular's entities wrappers - services, factories, additional components/directives, other..

Why?: this is the file where we can hook vanilla js files into angular. This is the main point to see what this module is composed of.

import angular from 'angular';
import AngularSortableView from 'angular-sortable-view/src/angular-sortable-view.js';
import { NowPlaylistComponent } from './now-playlist.component';

export default angular.module('now-playlist', [
      'angular-sortable-view'
    ])
    .config(config)
    .component(NowPlaylistComponent.selector, NowPlaylistComponent)
    // OR - if you defined controllerAs similary to the component's element:
    .component(NowPlaylistComponent.controllerAs, NowPlaylistComponent)
;
// optional
/* @ngInject */
function config () {

}

module-name.component.js - component defintion with controller class

this file should contain:

  1. the component/directive definition as a literal object, with export.
  2. the "controller" property should be defined as a class.
  3. inlined with template string es6/es2015 syntax.
  4. if template is huge (30 lines), consider: 4.1 break the template to smaller reusable components. 4.2 fallback to a template that should be imported from external file.

Why?: It's easy to understand the bigger picture of this component: what are the inputs and outputs retrieved from scope. Everything is in one place and easier to reference. Moreover, this syntax is similar to angular 2 component definion - having the component configuration above the "controller" class.

import template from './now-playlist.tpl.html';

export let NowPlaylistComponent = {
  selector: 'nowPlaylist',
  template,
  // if using inline template then use template strings (es2015):
  template: `
    <section class="now-playlist">
        ....
    </section>
  `,
  // Optional: controllerAs: 'nowPlaylist',
  bindings: {
      videos: '<',
      filter: '<',
      nowPlaying: '<',
      onSelect: '&',
      onRemove: '&',
      onSort: '&'
  },
  controller: class NowPlaylistCtrl {
    /* @ngInject */
    constructor () {
        // injected with this.videos, this.onRemove, this.onSelect
        this.showPlaylistSaver = false;
    }

    removeVideo($event, video, $index) {
        this.onRemove && this.onRemove({ $event, video, $index });
    }

    selectVideo (video, $index) {
        this.onSelect && this.onSelect({ video, $index });
    }

    sortVideo($item, $indexTo) {
        this.onSort && this.onSort({ $item, $indexTo });
    }
  }
}

Components (as of Angular 1.5)

  • Use ES2015 class for controller
  • Use Object.assign to expose injected services to a class methods (make it public)

Why? - Object.assign is a nice one liner usage for overloading services on "this" context, making it available to all methods in a service (i.e., playVideo method). Why? - it follows the paradigm of angular 2 component class (controller), where you would define "public"/"private" on constructor's argument in order to create references on "this" context.

export let NowPlaylistComponent = {
  // ....
  controller: class YoutubeVideosCtrl {
    /* @ngInject */
    constructor (YoutubePlayerSettings, YoutubeSearch, YoutubeVideoInfo) {
      Object.assign(this, { YoutubePlayerSettings, YoutubeVideoInfo });
      this.videos = YoutubePlayerSettings.items;

      YoutubeSearch.resetPageToken();
      if (!this.videos.length) {
        YoutubeSearch.search();
      }
    }

    playVideo (video) {
      this.YoutubePlayerSettings.queueVideo(video);
      this.YoutubePlayerSettings.playVideoId(video);
    }

    playPlaylist (playlist) {
      return this.YoutubeVideoInfo.getPlaylist(playlist.id).then(this.YoutubePlayerSettings.playPlaylist);
    }
  }
}

Component Options

controllerAs

if starting a new component, use the default $ctrl. if migrating and want to keep the controllerAs custom name, define a 'selector' property the same as the camelCased controllerAs value, which will be used with the angular.component factory.

WHY?: 'selector' in angular 2 indicates the "css" selector which will be used in html. When migrating to Angular 2, it will be easier to transform it to the kebab case.

// when controllerAs isn't explicitly defined
// and used as the default "$ctrl"
export let NowPlaylistComponent = {
  selector: 'nowPlaylist'
  ...
}

// when controllerAs is explicitly defined
// and NOT used as the default "$ctrl"
export let NowPlaylistComponent = {
  // when migrating to angular 2 => selector: 'now-playlist'
  selector: 'nowPlaylist'
  controllerAs: 'nowPlaylist'
}

Services

It's a best practice to write all logic in services.

Why?: logic can be reused in multiple files. logic can be tested easily when in a service object.

angular.service

Use angular.service api with a class for a service.

Why?: Services in angular 2 are classes. it'll be easier to migrate the code.

angular.factory

use angular.service instead.

angular.provider

export a function as provider as in angular with ES5.

Application Structure

Overall Guidelines

[-] - a folder an * - a file

[-] src
  [-] components
  [-] core
    [-] components
    [-] services
  [-] css
  * app.js
  * index.html

src/components

This directory includes SMART components. It consumes the app.core services and usually doesn't expose any api in attributes. It's like an app inside a smart phone. It consumes the app's services (ask to consume it) and knows how to do its stuff.

Usage of such smart component is as follows:

<now-playing></now-playing>

Example definition in index.js can be:

import angular from 'angular';
import AppCore from '../core';
import { NowPlayingComponent } from './now-playing.component.js';
import nowPlaylist from './now-playlist';
import nowPlaylistFilter from './now-playlist-filter';
import playlistSaver from './playlist-saver';
import YoutubePlayer from '../youtube-player';

export default angular.module('now-playing', [
      AppCore.name,
      nowPlaylist.name,
      nowPlaylistFilter.name,
      playlistSaver.name,
      YoutubePlayer.name
    ])
    .config(config)
    .component(NowPlayingComponent.selector, NowPlayingComponent)
;
/* @ngInject */
function config () {

}

src/core/components

This directory includes system wide DUMB components. A Dumb Component gets data and fire events. It is communicating only through events. This is example:

<dropdown items="vm.presets" on-select="vm.handlePresetSelect(item, index)"></dropdown>

Testing

Unit testing helps maintain clean code, as such I included some of my recommendations for unit testing foundations with links for more information.

Testing Guidelines

These are guidelines to follow for setup an environment for testing.

Write Tests with Stories

  • Write a set of tests for every story. Start with an empty test and fill them in as you write the code for the story.

Why?: Writing the test descriptions helps clearly define what your story will do, will not do, and how you can measure success.

it('should have a collection of media items', function() {
    // TODO
});

it('should find fetch metadata for a youtube media', function() {
    // TODO
});

Testing Library

Why?: Both Jasmine and Mocha are widely used in the Angular community. Both are stable, well maintained, and provide robust testing features.

Note: When using Mocha, also consider choosing an assert library such as Chai. I prefer Mocha.

Test Runner

  • Use Karma as a test runner.

Why?: Karma is easy to configure to run once or automatically when you change your code.

Why?: Karma hooks into your Continuous Integration process easily on its own or through Grunt or Gulp.

Back to top

Testing Controllers

Since controllers are written as classes, unit tests should be written to check functionality. Follow this post for testing angular 1.x components written with ES2015 Steps to follow:

  1. import the component javascript file
  2. setup a mocked module
  3. setup spies (if needed)
  4. use $controller to instanciate a new controller.
  5. 1st argument is the component's controller ("class").
  6. 2nd argument is an object with a new scope and possibly anh mocked objects/spies from previous steps.
  7. use scope.$digest to apply compilation and mock an "angular" data digest cycle (optional in some cases - TBD)

Organizing Tests

  • Place unit test files (specs) side-by-side within the component's code.
  • Place mocks in a tests/mocks folder

Why?: Unit tests have a direct correlation to a specific component and file in source code. Why?: Mock files (json) should be agnostic to the component which is using them. Multiple components specs might use the same jsom mocks.

Back to top

Comments

TBD

ES Lint

Use an Options File

  • Use eslint.org to define es2015 support
  • Use .eslintrc.json file for linting and support es2015 features

Why?: Provides a first alert prior to committing any code to source control. Why?: Provides consistency across your team.

{
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 6,
    "ecmaFeatures": {
      "modules": true,
      "arrowFunctions": true,
      "blockBindings": true,
      "destructuring": true,
      "classes": true
    }
  },
  "rules": {
    "indent": [
      "error",
      2
    ],
    "quotes": [
      2,
      "single"
    ],
    "linebreak-style": [
      2,
      "unix"
    ],
    "semi": [
      2,
      "always"
    ]
  },
  "env": {
    "es6": true,
    "browser": true,
    "jasmine": true,
    "commonjs": true
  },
  "globals": {
    "angular": true,
    "inject": true
  },
  "extends": "eslint:recommended"
}

More To Come...

Back to top

Routing

TBD

Contributing

  1. Open an issue for discussion
  2. Create a pull request to suggest additions or changes

License

Share your thoughts with an issue or pull request

Copyright

Copyright (c) 2015-2016 Oren Farhi

Back to top

More Repositories

1

ngx-infinite-scroll

Infinite Scroll Directive for Angular
TypeScript
1,196
star
2

echoes-player

Echoes Player: the missing Media Player experience for Youtube - Built with Angular (9), ngrx (9), Angular CLI, Boostrap (SASS), Youtube api
TypeScript
834
star
3

echoes

Echoes is a media player based on youtube which provides a great user experience (no ads!). Overall, it's a web application that is based on angular.js & bootstrap.
JavaScript
334
star
4

ngrx-styleguide

a collection of best practices / commonly used patterns with ngrx extensions - store, effects, router and others
140
star
5

ngx-youtube-player

(ngx) A youtube component wrapped with Angular (typescript)
TypeScript
91
star
6

supernova-angular-1.5.x-es6-starter

starter kit for angular > 1.5.x, > es6, karma and jasmine
JavaScript
71
star
7

ngx-typeahead

A simple but yet powerful typeahead component for Angular (css framework agnostic)
TypeScript
60
star
8

sublime-angular2-snippets

John Papa's snippets for sublime text
51
star
9

Backbone.Safe

a plugin for backbone models to save & load data using localstorage as well
JavaScript
32
star
10

affinity

affinity is a case study: an attempt to create a simple component based library
TypeScript
18
star
11

angular2-infinite-scroll

Infinite Scroll Directive For Angular (version 2 up to 2.3.1)
TypeScript
15
star
12

gulp-dogen

a fast & easy scaffold cli for any development (based on gulp)
JavaScript
14
star
13

ngrx-action-creator-factory

An Action Creator Factory for ngrx
JavaScript
13
star
14

Backbone.Beamer

An Extension Manager & Loader for Backbone.js
JavaScript
10
star
15

html-extended

HTML syntax Sublime Text definition which supports Web Components with "-" in tag name
8
star
16

backbone-switcher

A view manager that switch views. built on top of backbone.beamer extension manager
JavaScript
8
star
17

angular2to1

An angular 2 to angular 1 Shim - Write angular 2 components with angular 1
JavaScript
7
star
18

Timber

An application framework for starting projects with Backbone.js & Requirejs DEPRECATED
JavaScript
5
star
19

echoes-comp

A Component Base version for Echoes Player
JavaScript
3
star
20

use-recorder

A Mic recorder hook for React
TypeScript
3
star
21

backbone-transition

A view manager that inserts transition to before and after render method. built on top of backbone.beamer extension manager
JavaScript
2
star
22

echoes-mobile

echoes player wrapped in ionic for Android
JavaScript
1
star
23

backbone.collection-view

A collection view support on top of backbone.beamer extension manager
JavaScript
1
star