Organization

A single entry point dispatcher, that defines how we initialize the client
side code for the current page.

Components slightly detached from the DOM tree, in most cases implemented through
prototypes.

Single entry point

The Single entry point
handles how the JavaScript bootstraps the client side code based on the current
page in the browser, which helps to organize application in a way we can relate
pieces of the client side code with the HTML code.

Page handlers draw a parallel to Rails controllers and actions, and the Single
entry point implementation mimics the Front Controller
pattern, implemented in most web frameworks.

Using page.js, for instance, we can
have page specific handlers that are responsible for instantiating any component
required by the interface or firing any necessary setup code.

// app/assets/javascripts/pages/orders/index.js// The following function executes when we render the `orders/index.html.erb`// view, which adds a `data-page="orders#index"` attribute to the `body`// element.page.at('orders#index',function(){$('[data-chosen]').chosen();});

With this approach we can isolate the code execution and event binding from the
definition of the components or plugins used in the project, which
helps developers to navigate through the code base and understand the execution
flow and how the rest of the JavaScript code relates to the pages and sections
of the application.

Page handlers, as server side controllers, shouldn’t contain application logic
and be responsible for orchestrating the execution of other components,
and injecting any dependencies (like specific DOM elements)
or binding elements to help one component communicate with another.

// app/assets/javascripts/pages/projects/index.js// The handler injects the required DOM elements for each of the components// used in the interface, binds one with another through an event handler// and spins up the `projectSelector` events.page.at('projects#index',function(){varprojectSelector=newProjectSelector('[data-projects]'),chart=newActivityChart('[data-chart]');projectSelector.on('project:selected',function(project){chart.render(project);});projectSelector.bindEvents();});

Script Loaders

For delaying the JavaScript execution and state the dependencies between different
components and modules we recommend the usage of the [Asynchronous module definition]
(https://en.wikipedia.org/wiki/Asynchronous_module_definition) paired with [almond]
(https://github.com/jrburke/almond) for loading the modules after the Asset Pipeline
concats everything into a bundled file.

The AMD format wraps each entity into a define function call, with a module ID
identifier, an Array of dependencies and a factory function, and provides a
require function to require modules elsewhere in the app.

Third party dependencies

For any third party dependency that is commonly used as a global variable (like jQuery
and Turbolinks) or isn’t well suited for the loading pattern used by the application,
we should use that library as global dependency instead of shoehorn it into the
module format and risk introducing any API inconsistency or update issue into
the app.

// LoDash does not exports a named module and doesn't work out of the box// with almond, so we require it upfront and rely on the global `_` variable.//= require lodash//= require almond

Directory structure

We suggest the following directory for Rails and non Rails projects when we aren’t
using a client side framework like Ember that bring its
own patterns to the app.

Is not required to create all directories upfront, and the project can adopt
this structure as the codebase grows and requires more abstractions. You can start
with a simple application.js monolith and go from there as necessary.

ESLint

It is strongly advised to use ESLint to detect formatting
errors and potential problems with your JavaScript code. You can install it
with npm install -g eslint so the eslint command will be available through your
system. You can then setup your editor to analyze your code automatically in
development with any of the following tools:

The linter and [linter-eslint]
(https://atom.io/packages/linter-eslint) packages for the Atom editor.

You should add a .eslintrc file to the root of your project directory to
customize how ESlint will inspect your code and which type of warnings you want
to look for. You can start with something like the following settings: