JavaScript

JavaScript

The JavaScript frontend logic has been developed using CoffeeScript. Coffeescript gets compiled to JavaScript by using gulp tasks. The code is located in the ./src/coffeescript folder.

A component architecture has been used, where each functionality is split into a separate class following a specific structure. Components get initialized in the main.coffee file by using a factory approach only when their associated element exists on the current page.

Browserify is used to manage module definition and importing, which follows the CommonJS convention used by node.js using the require and module.exports commands.

Managing Javascript dependencies

To manage Javascript dependencies npm (node package manager) is used. For a speedier development it is recommended to use yarn, an alternative package manager that uses the npm package repository. This guide assumes you are familiar with package managers. In case you are not, it is recommended that you learn more about them at their respective websites.

Code structure

The code is divided into two categories:

  • modules These are reusable components, defined as classes
  • utilities These are utility functions, each performs one thing only

The main.coffee file is the entry point of the application, where all modules are instantiated using the factory utility defined in utilities/factory.coffee.

The whole code gets minified into a main.js file, which is good for caching purposes. The factory utility ensures that only components needed for the page get instantiated.

# the BannerSearchForm component will be instantiated only
# if the page contains an element with the class '.js-bannerSearchForm'
factory(BannerSearchForm, '.js-bannerSearchForm')

We use js- classes to signify which classes are used as JavaScript hooks.

Another folder called templates contains hogan.js templates which can be then used to render Ajax dependant or other JavaScript views. These files have mustache extension, because hogan is compatible with mustache.js, but it is more lightweight.

template = require '../templates/bookListItem.mustache'
html = template({ data: 'some data' })

Creating components

In order to work with the factory utility, components need to be structured in the following way:

# file themes/source_unibz/src/coffeescript/modules/Accordion.coffee
$ = require 'jquery'

class Accordion
  # a contructor with an @$el parameter is required
  # the corresponding to the CSS selector given as second
  # argument to the factory utility will assigned to this
  # parameter for usage in the code of the module
  constructor: (@$el) ->
    @$toggler = @$el.find('.js-accordion-toggle')
    @$region = @$el.find('.js-accordion-region')
    @openClass = 'is-open'
    # we initialize event handlers in the last part
    # of the constructor
    @$toggler.on 'click', $.proxy(@toggleState, this)

  # other code and functions
  toggleState: (e) ->
    if @$el.hasClass @openClass
      @$el.removeClass @openClass
      @$region.attr('aria-expanded', false)
    else
      @$el.addClass @openClass
      @$region.attr('aria-expanded', true)

module.exports = Accordion

This module can then be imported and initialized in the *main.coffee file as follows:

Accordion = require './modules/Accordion'

$ ->
  factory(Accordion, '.js-accordion')

Creating utilities

An utility is simply a reusabel function which performs one single functionality and can be defined as follows:

# file themes/source_unibz/src/coffeescript/utilities/readConfigFromHTMLAttrs.coffee
_ = require 'lodash'
$ = require 'jquery'

module.exports = ($el, propertyName) ->
  propertyName = 'data-' + propertyName + '-'
  _.reduce $el.get(0).attributes, (memo, attr) ->
    if attr and attr.name and attr.name.indexOf(propertyName) is 0
      memo[attr.name.replace(propertyName, '')] = attr.value
    memo
  , {}

It is then possible to import and use an utility as a normal function:

readConfigFromHTMLAttrs = require '../utilities/readConfigFromHTMLAttrs'
readConfigFromHTMLAttrs($el, 'filterable-property-label')