Leverage
applications
with
6
Elior Boukhobza @mallowigi
Fullstack Developer at CodeOasis
About me
CodeOasis
Still not ready yet...
Source: http://kangax.github.io/compat-table/es6/
But we're getting there.
However...
We have transpilers!
Transpiler:
Transformer + Compiler
class Foo extends Bar {
constructor () {
super();
}
static method () {
}
}
function Foo() {
Bar.call(this);
}
Foo.prototype = new Bar();
Foo.prototype.method = function () {};
6
What's new in ES6 ?
Classes
Modules
Arrow
functions
Template Strings
Constants
Symbols
Shorthand
Object literals
Collections
Promises
Destructuring
Default/Rest/
Spread parameters
Iterators
Generators
Proxies
Reflect API
Looks cool. But can we use it now?
=
require('modules') in the browser
<!doctype html>
<html lang="en">
<head>
...
</head>
<body>
<!-- build:js scripts/app.min.js -->
<script src="scripts/config/config.js"></script>
<script src="scripts/entities/ticket.js"></script>
<script src="scripts/entities/ticketCollection.js"></script>
<script src="scripts/entities/entities.js"></script>
<script src="scripts/dashboard/ticketBuilder.js"></script>
<script src="scripts/dashboard/consolidater.js"></script>
<script src="scripts/dashboard/corsProxyService.js"></script>
<script src="scripts/dashboard/controllers/appController.js"></script>
<script src="scripts/dashboard/controllers/searchController.js"></script>
<script src="scripts/dashboard/controllers/resultsController.js"></script>
<script src="scripts/dashboard/controllers/resultController.js"></script>
<script src="scripts/dashboard/dashboard.js"></script>
<script src="scripts/app.js"></script>
<!-- endbuild -->
</body>
</html>
Before
After
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!-- vendors -->
<script src="lib/vendors.js"></script>
<!-- your app bundle's main -->
<script src="js/app.js"></script>
</body>
</html>
require('angular/angular');
require('angular-ui-router/release/angular-ui-router');
require('./app-controllers');
require('./templates');
var routes = require('./routes');
module.exports = angular.module('directory', [
'templates',
'ui.router',
'directory.controllers'
]).config(routes);
require browser libraries and modules
export stuff
import angular from 'angular/angular';
import from 'angular-ui-router/release/angular-ui-router';
import from './app-controllers';
import from './templates';
import routes from './routes';
export default angular.module('directory', [
'templates',
'ui.router',
'directory.controllers'
]).config(routes);
require browser libraries
export stuff
Tip: Put an "index.js" file in each of your modules directories and import them from your main app file.
Solution: napa
napa allows installation of non-npm modules without package.json"
{
"scripts": {
"install": "napa"
},
"napa": {
"angular": "angular/angular#v1.3.15",
"backbone": "jashkenas/backbone#master",
"jquery": "jquery/jquery"
}
}
package.json :
Main entry point:
var angular = require('angular'),
Backbone = require('backbone'),
$ = require('jquery/dist/jquery');
// packages that don't have a package.json need to be specified their "main file".
Note: Requiring 3rd-party libraries also mean that they will be included in your app bundle. Make sure to minify your sources!
import '../config';
import '../services';
import User from "./User.js";
import Recommendations from "./Recommendations.js";
export default angular.module('discover.models', [
'ionic',
'LocalStorageModule',
'discover.config',
'discover.services'
])
.service('User', User)
.service('Recommendations', Recommendations)
;
// Add modules
import './config';
import './models';
import './services';
import './views';
import run from './run';
// The app modules
var modules = [
// ng templates
, 'templates'
// app modules here
, 'discover.config'
, 'discover.models'
, 'discover.services'
, 'discover.views'
];
export default angular.module('discover', modules)
.run(run);
main.js
// Save deps as closures
var $http, SERVER, $localStorage;
// Every service is a class
class User {
// Inject deps into constructor (I use angular-mocks _ hack)
constructor (_$http_, _SERVER_, _localStorageService_) {
// Services and constants
$http = _$http_;
SERVER = _SERVER_;
$localStorage = _localStorageService_;
// ... properties
}
// methods...
}
// We could also write "export default User" and use ng-annotate...
export default ['$http', 'SERVER', 'localStorageService', User];
// Still need to import MainController to extend it... which we cannot do prior to ES6!
import {MainController} from '../../MainController';
// Private deps
var $timeout, AudioPlayer;
// Export the controller without its list of deps
export class DiscoverController extends MainController {
constructor (_$timeout_, _User_, _Recommendations_, _AudioPlayer_) {
// Call super controller
super();
// Private deps
$timeout = _$timeout_;
AudioPlayer = _AudioPlayer_;
// Public deps
this.User = _User_;
this.Recommendations = _Recommendations_;
// Public properties
this.currentSong = angular.copy(this.songs[0])
}
// Use ES5 getter to get a binding to "Recommendations.queue" and not a copy.
get songs () {
return this.Recommendations.queue;
}
// Shorthand syntax
nextSong() {
// $log is a property of MainController
this.$log('Playing next song');
this.isPlaying = false;
this.Recommendations.nextSong();
}
}
export default ['$timeout', 'User', 'Recommendations', 'AudioPlayer', DiscoverController];
Example Usage
import {MainController} from '../../MainController';
// Import single methods, not the whole service (bonus: alias)
import {auth as authenticate} from '../../models/User"
export class SplashCtrl extends MainController {
constructor () {
super();
this.username = '';
this.isSignUp = false;
}
submitForm () {
authenticate(this.username, this.isSignUp)
.then(() => {
// on success change state
super.$state.go('tab.discover');
})
.catch((error) => {
this.error = error;
})
}
}
export default SplashCtrl;
Exemple requiring single methods
But it's probably better to stick to DI principles and inject the whole service.
function minMax () {
return {
restrict: 'EA',
scope: {
min: '@',
max: '@'
},
replace: true,
template: `<p>min is ${min}, max is ${max}</p>`,
controller: ['$scope', function ($scope) {
// Default destructuring values
var {min = 0, max = 100} = $scope;
console.log('min is %d, max is %d', min, max);
}],
link: function(scope, elem, attrs) {
var {timeout, title, foo, bar} = attrs.options;
// Do something with that
}
}
};
export default minMax;
// Before: bind function
_.map(collection, function (item) {
return item.property;
}).bind(this);
// Context parameter
_.map(collection, function (item) {
return item.property;
}, this);
// Now:
_.map(collection, item => item.property);
// Before: arguments
function (arg1) {
var myArgs = Array.prototype.slice.call(arguments, 1);
console.log(arg1, myArgs);
}
// Now: rest params
function (arg1, args...) {
// PS: "Array.prototype.slice.call" is now "Array.from"
console.log(arg1, args);
}
new Promise(function (resolve, reject) { ... })
var obj = {
...
[Symbol('private')]: 'a private property'
}
{
let MyController = function ($scope) { ... }
}
THE
FUTURE
IS
NOW