In this tutorial we will write our app using ecmascript6
javascript, which is the latest version of javascript updated with the new ecmascript standards (From now on we will refer it as 'es6'). So before we dive into building our app, we need to make an initial setup inorder to achieve that.
Iorder to write some es6 code we will need a pre-processor. babel plays a perfect roll for that. But that's not all. One of the most powerful tools in es6 is the module system. It uses relative paths inorder to load different modules we implement. babel
can't do that alone because it can't load relative modules using sytax only. We will need some sort of a module bundler.
That's where Webpack kicks in. Webpack
is just a module bundler, but it can also use pre-processors on the way and it can be easily configured by whatever rules we specify, and is a very powerful tool and is being used very commonly.
Meteor
also uses the same techniques to implement es6, and load npm
modules into client side code, but since we're using Ionic
cli and not Meteor
, we will implement our own Webpack
configuration, using our own rules!
Great, now that we have the idea of what Webpack
is all about, let's setup our initial config:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
var camelCase = require('lodash.camelcase');
var upperFirst = require('lodash.upperfirst');
module.exports = {
entry: [
'./src/index.js'
],
output: {
path: __dirname + '/www/js',
filename: 'app.bundle.js'
},
externals: [
{
'angular': 'angular',
'cordova': 'cordova',
'ionic': 'ionic'
},
resolveExternals
],
target: 'web',
devtool: 'source-map',
babel: {
presets: ['es2015', 'stage-0'],
plugins: ['add-module-exports']
},
module: {
loaders: [{
test: /\.js$/,
exclude: /(node_modules|www)/,
loader: 'babel'
}]
},
resolve: {
extensions: ['', '.js'],
alias: {
lib: __dirname + '/www/lib'
}
}
};
function resolveExternals(context, request, callback) {
return cordovaPlugin(request, callback) ||
callback();
}
function cordovaPlugin(request, callback) {
var match = request.match(/^cordova\/(.+)$/);
var plugin = match && match[1];
if (plugin) {
plugin = camelCase(plugin);
plugin = upperFirst(plugin);
callback(null, 'this.cordova && cordova.plugins && cordova.plugins.' + plugin);
return true;
}
}
NOTE: Since we don't want to digress from this tutorial's subject, we won't go into details about
Webpack
's config. For more information, see reference.
We would also like to initiate Webpack
once we build our app. All our tasks are defined in one file called gulpfile.js
, which uses gulp's API to perform and chain them.
Let's edit our gulpfile.js
accordingly:
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
45
46
47
48
49
50
51
var minifyCss = require('gulp-minify-css');
var rename = require('gulp-rename');
var sh = require('shelljs');
var webpack = require('webpack');
var webpackConfig = require('./webpack.config');
var paths = {
webpack: ['./src/**/*.js', '!./www/lib/**/*'],
sass: ['./scss/**/*.scss']
};
gulp.task('default', ['webpack', 'sass']);
gulp.task('webpack', function(done) {
webpack(webpackConfig, function(err, stats) {
if (err) {
throw new gutil.PluginError('webpack', err);
}
gutil.log('[webpack]', stats.toString({
colors: true
}));
done();
});
});
gulp.task('sass', function(done) {
gulp.src('./scss/ionic.app.scss')
...some lines skipped...
});
gulp.task('watch', function() {
gulp.watch(paths.webpack, ['webpack']);
gulp.watch(paths.sass, ['sass']);
});
From now on all our client code will be written in the ./src
folder, and Gulp
should automatically detect changes in our files and re-build them once our app is running.
NOTE: Again, we would like to focus on building our app rather than expalining about 3rd party libraties. For more information about tasks in
Gulp
see reference.
And last but not least, let's install the necessary dependencies inorder to make our setup work. Run:
$ npm install babel --save
$ npm install babel-core --save
$ npm install babel-loader --save
$ npm install babel-plugin-add-module-exports --save
$ npm install babel-preset-es2015 --save
$ npm install babel-preset-stage-0 --save
$ npm install expose-loader --save
$ npm install lodash.camelcase --save
$ npm install lodash.upperfirst --save
$ npm install script-loader --save
$ npm install webpack --save
TIP: You can also write it as a single line using
npm i <package1> <package2> ... --save
.
Our package.json
should look like this:
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
34
35
36
37
"version": "1.1.1",
"description": "whatsapp: An Ionic project",
"dependencies": {
"babel": "^6.5.2",
"babel-core": "^6.7.6",
"babel-loader": "^6.2.4",
"babel-plugin-add-module-exports": "^0.1.2",
"babel-preset-es2015": "^6.6.0",
"babel-preset-stage-0": "^6.5.0",
"expose-loader": "^0.7.1",
"gulp": "^3.5.6",
"gulp-concat": "^2.2.0",
"gulp-minify-css": "^0.3.0",
"gulp-rename": "^1.2.0",
"gulp-sass": "^2.0.4",
"lodash.camelcase": "^4.1.1",
"lodash.upperfirst": "^4.2.0",
"script-loader": "^0.7.0",
"webpack": "^1.13.0"
},
"devDependencies": {
"bower": "^1.3.3",
...some lines skipped...
"ionic-plugin-keyboard"
],
"cordovaPlatforms": []
}
Ionic
provides us with a very nice skelton for building our app. But we would like to use a different method which is a little more advanced which will help us write some es6 code properly.
Thus, we shall clean up some files from our project, just run:
$ cd ./www
$ rm -rf ./css
$ rm -rf ./img
$ rm -rf ./js
$ rm -rf ./templates
Next, we will setup our index.html
:
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>whatapp</title>
<!-- compiled css output -->
<link href="css/ionic.app.css" rel="stylesheet">
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.bundle.js"></script>
</head>
<body>
<!--
The nav bar that will be updated as we navigate between views.
-->
Whatsapp
, since that's what it represents.ionic.app.css
. All our scss files should be defined in scss
folder.app.bundle.js
using our Webpack
config we've just defined.ng-app
attribute which will then take place in our javascript code.Now that we have an initial setup, let's define our entry point for our code. Create a file called index.js
in our src
folder with the following contents:
1
2
3
4
5
6
7
8
9
// libs
import 'script!lib/angular/angular';
import 'script!lib/angular-animate/angular-animate';
import 'script!lib/angular-sanitize/angular-sanitize';
import 'script!lib/angular-ui-router/release/angular-ui-router';
import 'script!lib/ionic/js/ionic';
import 'script!lib/ionic/js/ionic-angular';
// app
import './app';
This is simply a file where all our desired scripts are loaded. Note that libraries are being loaded with the script!
pre-fix, which is braught to us by the script-loader
npm package. This pre-fix is called a loader, and we actually have many types of it, but in this case it tells Webpack
that the files specified afterwards should be loaded as-is, without handling any module requirements or any pre-processors.
NOTE: We can also specify the script loader as a general rule for all our libraries, but this way it won't be clear that the files we just imported are being imported as scripts. Both approaches are good, but we will stick with the direct and simple approach of specifying the script loader for every library module imported, because it's more declerative.
As you can see there is also an app.js
file being imported at the bottom. This file should be our main app file. Let's write it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Angular from 'angular';
import Ionic from 'ionic';
import Keyboard from 'cordova/keyboard';
import StatusBar from 'cordova/status-bar';
const App = 'whatsapp';
Angular.module(App, [
'ionic'
]);
Ionic.Platform.ready(() => {
if (Keyboard) {
Keyboard.hideKeyboardAccessoryBar(true);
Keyboard.disableScroll(true);
}
if (StatusBar) {
StatusBar.styleLightContent();
}
Angular.bootstrap(document, [App]);
});
As you can see, we define our app's module and we bootstrap it. Bootstraping is when we initialize primary logic in our application, and is done automatically by Angular
. Ofcourse, there is some additional code related to cordova
enviroment, like hiding the keyboard on startup.
We'de now like to build our app and watch for file changes as we run our app. To do so, just edit the ionic.project
file and add Gulp
files to run on startup.
1
2
3
4
5
6
7
8
{
"name": "whatsapp",
"gulpStartupTasks": [
"webpack",
"sass",
"watch"
]
}
Also, since we use pre-processors for both our .js
and .css
files, they are not relevant anymore. Let's make sure they won't be included in our next commits by adding them to the .gitignore
file:
6
7
8
9
10
plugins/
.idea
www/lib/
www/css/
www/js/