Docs

Everything You Need to Know

Tutorials

Organizing Your Code

ES6 (ECMAScript 2015)

If you use the ChocolateChip-UI command line tool to create a start project, you can write your app using ES6. JSPM will transpile the code into ES5. You can use many of the features of ES6 now while still supporting browsers that don't support it. You can use classes, arrow functions, object comprehensions, rest parameters, let and const, promises, template literals and most importantly, modules.

We have four sample apples written with ES6 modules. You can learn more about them here: Demos.

ES6 Modules

Before you get started using ES6 modules, make sure you have JSPM installed globally:

npm i -g jspm

This is a package manager designed to let you use ES6 modules now for any browser. It takes you modules, figures out the dependencies and creates a single bunduled file of all your JavaScript that passed just the parts needed. No global memory polution.

When you use the ChocolateChip-UI commandline tool to create a project, if you use the --jspm or -j flag, your project will be created with JSPM support included. Please read the section on JSPM in the installation documentation.

Modules are a great way to add structure to your app and make it easy for yourself and others to extend the app over time. However, you need to understand how modules work and how to structure you app's processes so that they can work with the module format. As we already mentioned, modules are imported before the DOM is loaded. This means any code that is looking for elements will not work when separated out as a module. You can get around this by wrapping your DOM code and then mounting it after importing. In fact, we made it so that views and routes have a special method - .mount() - so you can mount them after importing. This tells the view or route to re-scan the DOM to find elements or attach events.

Export

Before you can import something, it needs to be importable. You do this using the export keyword. There are a number of types of code patterns you can export from a module:

  1. functions
  2. object or arrays of data
  3. classes
  4. views
  5. routes

Export Functions

These are the easiest. Just place export before the function defintion:

export function whatever() {
  // Do stuff:
  alert('I was imported :-)')
}

For code that deal with DOM nodes, such as get elements or binding events, you can wrap it in a function to mount after importing:

export function getAnElement() {
  // Get an element:
  $('#itemList').addClass('we-are-ready');
}
export function bindEvent() {
  // Bind an event:
  $('#someButton').on('tap', function() {
    launchSomeProcess();
  });
}

You can export objects and arrays of data as well. Here is an object:

export const person = {
  name: {
    first: 'John',
    last: 'Doe'
  },
  age: 32,
  job: 'developer'
}

And here is an array:

export const todosData = [
  'Take a nap.',
  'Eat a snack.',
  'Play a game.'
];

You can also put your app's views in files that you export:

export const totalPurchasedItemsViews = $.View({
  element: '#purchaseDetails',
  variable: 'item',
  template: `<li>
    <h3><span>{= item.genre }</span>: {= item.product_title }</h3>
  </li>`
});

And routers:

// Set up router:
export const selectedCoffeeRoute = $.Router();
selectedCoffeeRoute.addRoute([
  {
    route: 'coffeeShopDetail',
    callback(id) {
      const selectedShop = coffeeshops.filter(shop => {
        return shop.id === id;
      })[0];
      shopDetailView.render(selectedShop);
    }
  }
]);

Import

Because ChocolateChip-UI is for building apps that run in the browser, many of its features depend on the DOM being loaded for them to work. This becomes a problem when using ES6 modules, as imports and exports occur before the DOM is loaded. This means that views and widgets will not be able to find their elements to initialize themselves.

Importing a module is very straightforward - you use the import keyword and the path to the module you want to use. You imports must be at the very top of the document, before any other code or they will not be imported:

import {bindEvent} from './controllers/bindEvent';
import {todosData} from './data/todosData';
import {totalPurchasedItemsViews} from './views/totalPurchasedItemsViews';
import {selectedCoffeeRoute} from './routes/selectedCoffeeRoute';

In the above imports, notice the paths to the modules. This is very important. You will learn to hate ENOENT errors when you try to build your app. These are "file not found" errors because you have a path wrong. This can be because you copy and pasted from one file to another, and the paths are different. Think about where the file that is importing the module is located. If it is in a folder called "views" and it is trying to access a folder outside it called "controllers", you will need to use a path like this:

import {bindEvent} from '../controllers/bindEvent

In contrast, from you app.js file, which should be at the same level as all of your module folders, you should be able to access modules like this:

import {bindEvent} from './controllers/bindEvent

or:

import {bindEvent} from 'controllers/bindEvent

Mounting your Imports

After importing your modules, depending on the type, you may need to mount them. If the module is a view or a route, you'll definitely need to mount it. If the module has to bind events or somehow access the DOM, you'll also need to mount it. Views and routes have a special method to mount them:

import {shopsView} from './views/shopsView';
import {shopDetailView} from './views/shopDetailView';

// Mount the imported views:
shopsView.mount();
shopDetailView.mount();

After mounting a view, you might want to bind it to a model or render it like so:

import {shopsView} from './views/shopsView';
import {shopDetailView} from './views/shopDetailView';
import {coffeshopsArray} from './data/coffeshopsArray';

// Make a model with imported data:
const shopsModel = $.Model(coffeshopsArray);

// Mount the imported views:
shopsView.mount();
shopDetailView.mount();

// Render coffeeshops list:
//=========================
shopsView.setModel(shopsModel);
shopsView.render();

// Or render view with raw data:
shopsView.render(coffeshopsArray);

After importing a route, you mount it like this:

import {selectedCoffeeRoute} from './route/router';

// Mount imported route:
//======================
selectedCoffeeRoute.mount();

For functions that have DOM related code — accessing elements, binding events or setting up a widget's functionality — you import and mount them by invoking them immediately:

import {addToCart} from './controllers/addToCart';
import {goToCart} from './controllers/goToCart';
import {cancelOrder} from './controllers/cancelOrder';
import {placeOrder} from './controllers/placeOrder';

// Mount imported controllers:
//=================================
addToCart();
goToCart();
cancelOrder();
placeOrder();

Functions that do not access the DOM can be important and then invoked whenever you need to, as usual with function calls.

Project Setup for Modules

Below is the file structure for a project:

+data
  - bestWines.js
  - californiaWines.js
+ dev
  - app.js
  + controllers
    + about
      - aboutSheet.js
    + heroImage
      - outputHeroImg.js
    + purchase
      - handlePurchase.js
      - handlePurchaseProgressBar.js
    + search
      - handleWineSearch.js
      - searchPanelControlsSetup.js
      - searchPanelInit.js
      - searchParameters.js
      - showChosenSearchParameters.js
  + routes
    - router.js
  + views
    - views.js

And here's the app.js file for that:

import * as app from './views/views';
import {aboutSheet} from './controllers/about/aboutSheet';
import {outputHeroImg} from './controllers/heroImage/outputHeroImg';
import {bestWines} from '../data/bestWines';
import {wines} from '../data/californiaWines';
import {wineRoute} from './routes/router';
import {searchPanelInit} from './controllers/search/searchPanelInit';
import {searchPanelControlsSetup} from './controllers/search/searchPanelControlsSetup';
import {showChosenSearchParameters} from './controllers/search/showChosenSearchParameters';
import {searchParameters} from './controllers/search/searchParameters';
import {handleWineSearch} from './controllers/search/handleWineSearch';
import {handlePurchaseProgressBar} from './controllers/purchase/handlePurchaseProgressBar';
import {handlePurchase} from './controllers/purchase/handlePurchase';

$(function() {

  // Mount imported views:
  //======================
  app.specialRedsView.mount();
  app.specialWhitesView.mount();
  app.selectedWineView.mount();
  app.filteredWinesView.mount();
  app.wineryView.mount();

  // Mount imported router:
  //=======================
  wineRoute.mount();

  app.specialRedsView.render(bestWines[0].data);
  app.specialWhitesView.render(bestWines[1].data);

  // Mount imported controllers:
  //============================
  aboutSheet();
  outputHeroImg();
  searchPanelInit();
  searchPanelControlsSetup();
  handleWineSearch();
  handlePurchaseProgressBar();
  handlePurchase();
  
});

View the ES6 Examples

We have provided examples of four simple apps made with ChocolateChip-UI: Fragranž, SFCoffee, TodoMVX and Vino. The come in two versions: double-clickable launch from the desktop and run from with terminal with Gulp, Babel, JSPM and Browsersync. You want to take a could look at the later. When you down the source code from Github, you'll find two folder: simple and jspm. The simple folder holds the double-clickable versions. These are great if you're new to ChocolateChip-UI and you just want to see how to put things together to make something work. However, once you want to get serious about build a real world app, you'll want to look closely at the versions of these apps in the jspm folder.

The best way to get the lay of an app is to open the dev folder inside the jspm folder. This is where you will actually work. When you use the --jspm or -j flag to start your app using the ChocolateChip-UI command line tool, you'll also find you have a dev folder with an app.js file inside it. The build script takes this, checks for imports, runs everything through Babel and then through JSPM to bundle it all into a single file. This means that anything you import into your app.js file will get bundled into a single app.js file after building. Look at how the example apps code in the dev folder is structured. You can build and run them like this:

npm install
# when done, run:
jspm install

# when done run:

gulp

To learn more about these demos, visit Demos