Building an Ionic hybrid mobile app with TypeScript
1. Building a cross-platform Hybrid
Ionic App with TypeScript
Lessons learned in building a real-world hybrid application using the
Ionic framework and TypeScript
Oleksandr Zinchenko [Macaw], Serge van den Oever [Macaw]
2. Versions used
This document is produced when using the following versions:
• Cordova version 4.1.2 (cordova --version)
• Ionic version 1.2.8 (ionic --version)
• Ionic framework version 1.0.0-beta.11 (ionic lib)
3. Contents
1. Introduction
2. Minimal Ionic project structure
3. Create a project
4. Project modifications: initial
structure
5. Project modifications: npm
packages
6. Project modifications: shellinit
7. Project modifications: hooks
8. Project modifications: TypeScript
interfaces
9. Project modifications: TypeScript
compiler
10.Working with Typescript
11.app.ts template
12.Controller template
13.Service template
14.Directive template
15.Restore existing project from
source control
16.Running app
17.Debugging
18.Development tools
19.Accessing the file system
20.Development strategy
21.Best coding practices
22.Debug/release code compilation
23.Preparing for the store: Android
24.Preparing for the store: iOS
25.Update the app without store
approval
26.Optimization tips
27.Ionic/Angular issues (1)
28.Ionic/Angular issues (2)
29.References
4. Introduction
TypeScript.
TypeScript is a strict superset of JavaScript, and adds optional static
typing and class-based object-oriented programming to the language.
It is designed for development of large applications and transcompiles to
JavaScript.
Additional features include:
1) Type annotations and compile-time type checking
2) Interfaces
3) Classes
4) Modules
5) Abbreviated "arrow" syntax for anonymous functions
Cordova.
Apache Cordova is a set of device APIs that allow to access native device
function from JavaScript. Combined with a UI framework this allows to
create cross-platform app using just HTML, CSS and JavaScript.
Cordova command-line interface (cli) includes:
1) create template Cordova project
2) build app for target platform using the same source code
3) run app in an emulator or device
Angular.
AngularJS lets you write client-side single-page web applications using
JavaScript. Its goal is to simplify both development and testing of an app
by providing a framework for client-side model-view-controller (MVC)
architecture.
Most important concepts in AngularJS:
1) Services
2) Controllers
3) Directives
Ionic.
Ionic is an open source front-end SDK (CSS framework and a Javascript
UI library) for developing hybrid mobile apps with HTML5. Ionic Javascript
UI library is built on top of angular and cordova. The Ionic framework
introduces its own command-line interface (cli) on top of the Cordova cli.
Ionic cli extends Cordova cli with:
1) livereload - reload app immediately after code changes
2) packaging for store deployment – certificates
3) build in the cloud - build for iOS on Windows
4) Gulp build system and the Bower package manager
The purpose of the current presentation is to show the complete developing
cycle of a cross-platform hybrid app using Cordova, Angular, Ionic
and TypeScript.
5. Minimal Ionic project structure
The following folders and files would be created in new project:
hooks - used to “hook in” actions during the different stages of the Ionic (or actually Cordova) build process
node_modules - project specific node.js modules
plugins - Cordova plugins are located here
scss - the sass files are managed here, builds to css (we move this folder into www to enable css debugging based
on generated map files in the sass compilation)
www - the actual folder where the source code for our app lives. The www folder contains all files that are packaged
into the native host application. All other folders and files are there to support the build process of the native
application, or contains sources compiled into the www folder (like the scss folder, which we move into www).
Ionic uses two package management systems:
• npm - for node packages, useful for installing packages to extends the build process
• packages required are managed through package.json
• packages are installed in the folder node_modules
• Bower - managing client-side libraries
• .bowerrc contains the Bower configuration: { "directory": “www/lib" }
all client-side library packages are installed in www/lib
• packages required are managed through bower.json
gulpfile.js is the “makefile” for the Gulp build system
config.xml is the Cordova configuration file that is used in building the hybrid app
6. Create a project
1. Use either Windows, OSX or Linux (We only tested on Windows and OSX)
2. Make sure that node.js is installed (see http://nodejs.org/)
3. install the node package cordova and ionic globally using the node.js package manager:
>npm install cordova -g (or update if already installed)
>npm install ionic -g (or update if already installed)
4. Create ionic project:
>ionic start myapp blank --appname "My App" –id com.mycompany.myapp --sass
5. where:
myapp is the folder created in the current folder for your project
blank is the template used for the project
--appname specifies the name of the app
--id specifies the package name (internal name)
--sass specifies to use SASS for CSS precompiling
6. Add target platform (do: > ionic platform list for available platforms on your OS):
>ionic platform add <PLATFORM>
7. Project modifications: initial structure
1. Move the folder scss into the folder www. All sources that are compiled should we in the www
folder, so source maps can point to the original source files while debugging.
2. Change gulpfile.js so the sass folder points to the www/scss folder:
var paths = {sass: ['./www/scss/**/*.scss']};
3. Change gulpfile.js so the 'sass' task also points to the www/scss folder:
gulp.src('./www/scss/ionic.app.scss')
4. Add the folders used by the package managers www/lib, node_modules, plugins, platforms
to the .gitignore file (Gist).
All external packages, plugins and platforms should not be managed in source control.
5. When working on Windows and using Visual Studio, it is important to mark the node_modules
folder as hidden to avoid issues in Visual Studio which can’t handle deep folder structures
correctly.
6. When working in Visual Studio it is recommended that each project member create his own
solution file to be able to use different path to the project. *.suo files should be excluded from
source control.
8. Project modifications: npm packages
We also install the Cordova and Ionic packages locally inside the project folder so we can
ensure specific versions of Ionic and Cordova for the project.
> npm install cordova --save-dev
> npm install ionic --save-dev
Use the --save-dev option to register the packages as dependency, so they can be
pulled in later using npm install. The information about packages would be written into
package.json file (initially this file created by ionic start command).
Initially the above commands install the latest versions of Cordova and Ionic. When done
with development lock-down the used versions using the command:
> npm shrinkwrap
9. Project modifications: shellinit
1) The project specific node packages and their commands are installed in the folder
node_modules/.bin. Add this folder to the PATH (at the beginning) to ensure the usage of the
project specific versions of the packages.
On OSX/Linux add in the file ~/.bash-profile: export PATH=./node_modules/.bin:$PATH
On Windows add file shellinit.bat (Gist) with the following lines:
@rem Add the node_modules.bin folder (found with 'npm bin') to the
path
@cd %~dp0
@for /f %%i in ('npm bin') do set PATH=%%i;%PATH%
On Windows execute the script shellinit.bat before executing command-line commands.
2) In addition you can add the following lines to be sure that all dependent packages will be always installed
by their respective package manager and are up to date:
@rem make sure everything is installed
call npm install
call bower install
call tsd update
call tsd rebundle
call cordova restore plugins --experimental
10. Project modifications: hooks
When working with a Cordova hybrid app, you might need a way to extend your build process. Cordova Hooks
serves that purpose and can execute scripts before or after certain points of the build process. The obvious
example of hook usage is copying app icons and splash screens to the appropriate platform folder during build
(see more info here).
An example of hook script for copying resources that executes after Cordova’s ‘prepare’ command is
hooksafter_prepare030_resource_files.js (Gist):
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var rootdir = process.argv[2];
filestocopy.forEach(function(obj) {
Object.keys(obj).forEach(function(key) {
var val = obj[key];
var srcfile = path.join(rootdir, key);
var destfile = path.join(rootdir, val);
console.log("copying "+srcfile+" to "+destfile);
var destdir = path.dirname(destfile);
if (fs.existsSync(srcfile) && fs.existsSync(destdir)) {
fs.createReadStream(srcfile)
.pipe(fs.createWriteStream(destfile));
}
});
});
var filestocopy = [{
"config/ios/splashscreens/Default-568h@2x~iphone.png":
"platforms/ios/MyApp/Resources/splash/Default-568h@2x~iphone.png"
}, {
"config/ios/splashscreens/Default-Landscape@2x~ipad.png":
"platforms/ios/MyApp/Resources/splash/Default-Landscape@2x~ipad.png"
}, {
"config/ios/splashscreens/Default-Landscape~ipad.png":
"platforms/ios/MyApp/Resources/splash/Default-Landscape~ipad.png"
}, {
"config/ios/splashscreens/Default-Portrait@2x~ipad.png":
"platforms/ios/MyApp/Resources/splash/Default-Portrait@2x~ipad.png"
}, {
"config/ios/splashscreens/Default-Portrait~ipad.png":
"platforms/ios/MyApp/Resources/splash/Default-Portrait~ipad.png"
}, {
"config/ios/splashscreens/Default@2x~iphone.png":
"platforms/ios/MyApp/Resources/splash/Default@2x~iphone.png"
}, {
"config/ios/splashscreens/Default~iphone.png":
"platforms/ios/MyApp/Resources/splash/Default~iphone.png"
}];
11. Project modifications: TypeScript interfaces
A large set of type definitions for common JavaScript libraries is managed by the DefinitlyTyped interfaces
library. In order to be able to install definitely typed interfaces (definitions) you can use the TypeScript
Definition Manager (tsd):
1. Install tsd locally (0.6.0-beta.4 or greater):
>npm install tsd@next --save-dev
At the time of writing tsd@next was a beta version. Later you should be able to install it using:
>npm install tsd -–save-dev
2. Install all needed type definitions. For example all cordova plugins definitions:
>cd www
>tsd install cordova/* --save –overwrite
--save –overwrite flags will save a list of installed definitions in tsd.json.
3. Add the tsd.json file to source control. Exclude the typings folder (wwwtypings) from source control.
It is possible to update or restore all definitions files configured in tsd.json by:
> tsd reinstall
> tsd rebundle - create the tsd.d.ts file included by _reference.ts
12. Project modifications: TypeScript compiler
1. In order to be able to compile TypeScript into JavaScript we need to install the gulp-tsc npm module:
> npm install gulp-tsc —save-dev
2. In the default Ionic gulpfile.js add the following bold lines to support TypeScript compilation (Gist):
var typescript = require('gulp-tsc');
var paths = {
sass: ['./www/scss/**/*.scss'],
typescript: ['./www/scripts/**/*.ts']
};
gulp.task('default', ['sass', 'compile']);
function compileTypeScript(done) {
gulp.src(paths.typescript)
.pipe(typescript({ sourcemap: true, out: 'tslib.js', sourceRoot: '../scripts' }))
.pipe(gulp.dest('./www/js/'))
.on('end', done);
}
gulp.task('compile', compileTypeScript);
gulp.task('watch', function() {
compileTypeScript();
gulp.watch(paths.sass, ['sass']);
gulp.watch(paths.typescript, ['compile']);
});
3. The above additions compile all TypeScript files in the www/scripts folder to a single file www/js/tslib.js. Include this resulting JavaScript file in
index.html:
<script src="js/tslib.js"></script>
13. Working with Typescript
1. Each TypeScript file should begin with the line:
/// <reference path='../_reference.ts'/>
Using this reference all TypeScript files ‘know’ about each other, and editors like Visual Studio
can provide intellisense.
2. Create a _reference.ts file in the www/scripts. This file should contain references to all
theTypeScript files in the project: typings, services, controllers, directives, interfaces and of
course the main entrypoint of the app, app.ts:
/// <reference path='../typings/tsd.d.ts' />
/// <reference path="controllers/MyController.ts" />
/// <reference path="services/MyService.ts" />
/// <reference path="directives/MyDirective.ts" />
/// <reference path="interfaces/MyInterface.ts" />
/// <reference path="app.ts" />
Note that the file ../typings/tsd.d.ts is generated and maintained by the tsd tool.
14. app.ts template
app.ts is the entry point of the app (Gist).
1. Add reference to _reference.ts:
/// <reference path="_reference.ts"/>
2. We declare an interface for the global rootScope object
$rootScope:
interface IAppRootScopeService extends
ng.IRootScopeService {
online: boolean;
lastScrollTimestamp: number;
isScrolling: () => boolean;
onScroll: () => void;
}
3. Directives are registered using angular.module() calls:
var myApp: ng.IModule = angular.module("angularApp", [
"ngSanitize",
"ionic",
"directive.bindonce",
]);
4. Services should be registered using myApp.service() calls:
myApp.service("serviceName",
myNameSpace.MyServiceClassName);
5. Controllers should be registered in the UI-routing structure (Learn
more here: https://github.com/angular-ui/ui-router).
myApp.config(($stateProvider, $urlRouterProvider) => {
$stateProvider.state("view", {
url: "/view",
templateUrl: "views/view.html",
controller: "ViewController"
});
});
6. Actual code entry point of the app is myApp.run():
myApp.run(
function (
$rootScope: ft.IAppRootScopeService
) {
// myApp entry point
}
);
15. Controller template
1) Add reference in _reference.ts
2) Bind in app.ts to angular UI-router state:
.state(‘home', {
templateUrl: "views/home.html",
controller: “MyController”
});
/// <reference path='../_reference.ts'/>
interface IMyControllerScope extends ng.IScope {
vm: IMyController; // now our view model (vm) in the scope is typed
}
interface IMyController {
myString: string;
myFunction: (arg) => boolean;
}
class MyController implements IMyController {
myString: string = ‘initial value’;
// $inject annotation. It provides $injector with information about dependencies to be injected into constructor
// it is better to have it close to the constructor, because the parameters must match in count and type.
// See http://docs.angularjs.org/guide/di
public static $inject = [
"$scope",
"$rootScope“
];
constructor(
private $scope: IMyControllerScope,
private $rootScope: IAppRootScopeService
) {
var currentClass: MyController = this;
$scope.vm = this;
$scope.$on("$destroy", () => {
// Clean up detached Dom elements
// Clean up attached listeners
});
currentClass.myString = 'assigning variables here';
}
myFunction(arg): boolean {
// arg processing here
return true;
}
}
Gist of the template
16. Service template
/// <reference path='../_reference.ts'/>
module myNameSpace {
"use strict";
export class MyServiceClassName {
public static $inject = [
"$log"
];
public pubVar: string;
private privVar: string;
constructor(
private $log: ng.ILogService
) {
privVar = ‘5’;
}
someFunction(element): number {
return parseInt(privVar, 10);
}
}
}
Register a service:
1) Add reference in _reference.ts
2) Register in app.ts:
var myApp: ng.IModule = angular.module("angularApp",
[
"ngSanitize",
"ionic",
"directive.bindonce",
]);
myApp.service(“serviceName", myNameSpace.MyServiceClassName);
Gist of the template
Note that we’re creating a service inside myNameSpace name space. This kind of structure is useful for big proje
cts. As an example you can redefine some interface inside a certain namespace and it will not affect the code out
side this name space.
17. Directive template
/// <reference path='../../typings/angularjs/angular.d.ts' />
interface IMyDirectiveScope extends ng.IScope {
bookmarker: string;
}
angular.module('directive.bindonce', [])
.directive('bindOnce', function () {
return {
restrict: 'A',
scope: true,
link: ($scope: IMyDirectiveScope): void => {
setTimeout(() => {
$scope.$destroy();
}, 0);
}
}
});
Register a directive:
1) Add reference in _reference.ts
2) Register in app.ts file:
var myApp: ng.IModule = angular.module("angularApp",
[
"ngSanitize",
"ionic",
"directive.bindonce",
]);
Nota bene: Always use functions for your directive definitions. See: http://discventionstech.wordpress.com/2014/01/19/wri
te-angularjs-code-using-typescript/
Gist of the bind-once directive
18. Restore existing project from source control
1. Update/install node.js (http://nodejs.org/)
2. Pull existing project
3. >cd root_directory_of_the_project
4. >shellinit.bat - This script configures settings for command-line development with local node_modules and
automatically runs:
npm install – reads package.json file and installs listed packages and dependencies
bower install – reads bower.json file and installs listed JavaScript libraries
tsd update – reads tsd.json and installs TypeScript interfaces
tsd rebundle – create tsd.d.ts file which is included to the project for references to TypeScript interfaces
cordova restore plugins --experimental – reads the Cordova config.xml and installs listed plugins
5. When working on Windows AND using Visual Studio, it is important to mark the node_modules folder as hidden to
avoid issues in Visual Studio which can’t handle deep folder structures correctly.
6. >ionic platform add <PLATFORM> - Adds target platform and copies installed plugins.
19. Running app
To run the app on Android device:
1) enable usb debugging on your device (see tutorial)
2) install OEM USB Drivers (see docs)
3) >gulp – to build the project
4) >ionic run android – to run app from device’s file system
>ionic run android ––livereload – to run app in remote server and access it from your device.
Changing any of the source files will result in an immediate compilation and reload of the app. This option is
extremely useful during development and debugging.
To run the app on Genymotion Android emulator:
1) disable hyper-v (see tutorial)
2) install genymotion Android emulator (http://www.genymotion.com)
3) >ionic run android or >ionic run android –livereload (note that for Genymotion you
should use >ionic run instead of ionic emulate)
To run the app on IOS device
//to do
To run the app on IOS emulator
//to do
To run the app in PC browser (without Cordova support):
>ionic serve – same as run --livereload but this time without Cordova and in the desktop browser.
20. Debugging
1) Android chrome debugging (Android version 4.4+)
The app uses a web browser component running the Cordova api to
communicate with native features of the device or emulator. The app can be
debugged using the Chrome browser. The Chrome browser allows to connect
remotely (url: chrome://inspect/#devices) to the app running on an
android device or emulator using usb debugging. Note that this approach is
not possible if you have an Android version less than 4.4.
2) IOS safari debugging
//to do
21. Development tools
Windows:
1) Visual Studio as an editor
2) Genymotion android emulator
3) ConEmu - Windows terminal supporting multiple tabs
4) TotalCommander with ADB plugin to get access to the file system of
the device or emulator
OSX:
WebStorm
iFunBox
22. Accessing the file system
Files on a device could be accessed for read and write using org.apache.cordova.file plugin. As of
v1.2.0, URLs to important file-system directories are provided as constants which make it extremely
convenient, because file system on different platforms vary significantly. As an example the path to
the application directory in the code looks like
var path = cordova.file.applicationDirectory for both Android and iOS, but the actual
value of path variable would be different.
It is important to know that:
1) >ionic serve – doesn’t use Cordova at all and runs in desktop browser only. This command
creates a small webserver which runs the app. To read files you can use $http requests.
2) >ionic run <PLATFORM>
>ionic emulate <PLATFORM> - these runs will actually install the app on the device (or
emulator) and you will have full access to the file system and app files via the cordova.file
plugin.
3) >ionic run <PLATFORM> --livereload
>ionic emulate <PLATFORM> --livereload
these commands create a remote livereload webserver and access it from the browser component in
the app. You do have access to device’s file system with the cordova.file plugin here, but you
can not access the HTML, JavaScript and CSS files embedded in the app.
23. Development strategy
1) Start with browser based (w/o Cordova) development using Chrome, simulate device
functionality where possible
• Fast development cycle (>ionic serve)
• Full Chrome debugging power
• Possible to release app preview as an url accessible on dropbox or other location
2) To develop interactions with a device we should use livereload approach using a real device or
an emulator (>ionic run <PLATFORM> --livereload)
3) Develop for Android, test on iOS
• From Android 4.4 full remote debugging and profiling through Chrome inspect
• iOS debugging is “suboptimal”
4) Test for the correct css layout on different devices for every platform.
5) Test JavaScript functionallity of the app in different platforms. The behaviour might be different
(As an example: not all Cordova plugins are cross-platform).
24. Best coding practices
1. Don’t use ‘this’ pointer directly in a code as it is misleading and most likely will produce
scope-related errors(in case of nested functions). Instead you should assign value of ‘this’
pointer to a definitly typed variable:
var currentClass: ClassName = this;
2. Use types for all variables and functions as it helps to find type missmach.
3. Read best coding practices for TypeScript.
4. Read angularJS ‘style guide’ which contain the best coding practices for angular.
25. Debug/release code compilation
In order to place the app into the web store it is recommended to:
1.minify HTML, CSS and JavaScript code
2.uglify JavaScript code
3.remove all comments from HTML, CSS and JavaScript
Install ready to use gulp modules that could perform these tasks:
1.>npm install gulp-ng-annotate -–save-dev
used to remove angularJS injections and comments and minifies
JavaScript
2.>npm install gulp-uglify –save-dev
3.>npm install gulp-sync –save-dev
helper module that is used to make sync calls of the tasks in Gulp
4.>npm install gulp-minify-html –save-dev
5.>npm install gulp-rename –save-dev
used to change file name
It is convinient to add these tasks to project gulpfile.js (see full gulpfile.js
at Gist):
var ngAnnotate = require('gulp-ng-annotate');
var uglify = require("gulp-uglify");
var gulpsync = require('gulp-sync')(gulp);
var minifyHTML = require('gulp-minify-html');
var rename = require("gulp-rename");
gulp.task('compile', compileTypeScript);
gulp.task('default', ['debug']);
gulp.task('release', gulpsync.sync(['minifyHtml', 'sass',
'compile', 'minifyJs']));
gulp.task('debug', ['sass', 'compile']);
gulp.task('minifyJs', function (done) {
gulp.src('./www/js/tslib.js')
.pipe(ngAnnotate({remove: true, add: true, single_quotes:
true}))
.pipe(uglify())
.pipe(gulp.dest('./www/js'))
.on('end', done);
});
gulp.task('minifyHtml', function (done) {
gulp.src('./www/index.html')
.pipe(minifyHTML({ empty: true }))
.pipe(rename(function (path) { path.basename += "-min"; }))
.pipe(gulp.dest('./www'));
gulp.src('./www/views/*')
.pipe(minifyHTML({ empty: true }))
.pipe(gulp.dest('./www/views/min'))
.on('end', done);
});
Now it’s much easier to compile debug/release versions of your code:
>gulp debug
>gulp release
26. Preparing for the store: Android
Before publishing it is necessary to:
1. increase android-versionCode in project/config.xml
file
2. Build:
>cordova build –release
3. sign the App with jarsigner (a part of Java SE
Development Kit)
4. allign the App with zipalign (a part of Android SDK)
It is convinient to write nodejs script ‘build.js’ that will
handle all mentioned tasks:
function shellExec(cmd, callback){
console.log(cmd + ' executing...');
exec(cmd, function(err, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (err !== null) {
console.error('fatal Error: ' + err);
process.exit(1);
} else {
typeof callback === 'function' && callback();
}
});
}
shellExec('cordova build --release android', function ()
{
shellExec('jarsigner -verbose -sigalg SHA1withRSA -
digestalg SHA1 –keystore <PATH_TO_KEYSTORE>
<PATH_TO_release-unsigned.apk> -storepass <PASSWORD>
elsevierMyApp', function() {
shellExec('zipalign -v 4 <PATH_TO_release-
unsigned.apk> <OUTPUT_APK_PATH>');
});
});
usage: >gulp build
28. Update the app without store approval
Why it’s important to avoid web store approval?
1) First of all it’s important because people which were not directly involved with developing could update a
content.
2) Second reason is that verifying a new release could take enormous amount of time for some platforms (up
to 10 days for iOS).
The idea is to build a hybrid app that will be a wrapper (bootstrapper app) for an actual App (embedded app).
Bootstrapper app should be configured (name, icons, splash) as it is embedded app and during the run it
should redirect to the actual embedded app. Luckily it is easy to do using javaScript window.location
command.
Bootstrapper app contains:
1) bootstrapper files
2) platform specific cordova.zip file with archived Cordova and plugins
3) embedded app archived into www.zip file
4) content.zip archive containing additional app content
During each start bootstrapper compares version file version.json in the app folder with version file on a
remote server. If updates found bootstrapper detects it and installs new version of the embedded app or
installs new content.
Source code for the bootstrapper you could find at Git, flow diagram you can fins in the blogpost . (Note that
this version still uses old cordova.file plugin conventions for device file system)
29. Optimization tips
1. Only release versions of the app should be published
2. Exclude console output in release version of the app
3. AngularJS ng-repeat might significantly slow down a hybrid app. Critical case is when each ng-
repeat item contain other angular bindin(s)
4. Minimize amount of angular bindings in HTML code
5. Allways manually unregister Jquery listeners at onDestroy event as angular don’t do it
automatically
6. Allways manually clean all detached DOM elements at onDestroy event as angular don’t do it
automatically
7. Use bind-once bindings if you don’t expect binded value to be changed (see directive template)
8. Starting from angularJS v1.3.5 It is possible to turn off angular debug data (see docs)
30. Ionic/Angular issues (1)
• Ionic scroll (overflow-scroll=“false”) gives problem with
$ionicScrollDelegate.scrollTo() function. We solve it by using native scrolling
(overflow-scroll=“true”)
• Tap during scroll issue. Touching of a list item during scroll process results in firing touch event of
another item. This bug could be solved by using scroll detection $ionicScrollDelegate.on-
scroll or by creating a directive in case of overflow-scroll=“false”:
angular.module('directive.scrolldetector', [])
.directive('scrollDetector', function ($window) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
function handler(e) {
scope.$evalAsync(function () {
scope.$eval((<any>attrs).scrollDetector);
});
}
scope.$on('$destroy', () => {
element[0].removeEventListener("scroll", handler);
});
element[0].addEventListener("scroll", handler);
}
}
});
Next create function that we can call to check if scrolling is active:
$rootScope.onScroll = () => {
$rootScope.lastScrollTimestamp = (new Date()).getTime();
}
$rootScope.isScrolling = () => {
//to be sure that at least 300 ms we have no scrolling
if ($rootScope.lastScrollTimestamp + 300 > (new Date()).getTime()) {
return true;
} else {
return false;
}}
usage:
in html: <ion-content overflow-scroll="true" scroll-detector="onScroll()">
in script: onTap = (e) => {
if (typeof $rootScope.isScrolling == 'function' && !$rootScope.isScrolling()) {
//tap event here
}}
31. Ionic/Angular issues (2)
• Angular digest loop and $ionicLoading sync issue. Let’s imagine that you want to use $ionicLoading mesage
right before script start heavy calculation. You will probably write the following:
$ionicLoading.show({ template: ‘wait…’, noBackdrop: true });
//script for heavy calc
which will result in heavy calc first and only then you will see your message. Next you will probably add
timeout wrapper to push your heave calc script call to digest loop right after $ionicLoading.show call:
$ionicLoading.show({ template: ‘wait…’, noBackdrop: true });
$timeout(()={
//script for heavy calc
});
which results with the same problem. We don’t know the clean way to solve the issue and as a workaround
we use this:
$ionicLoading.show({ template: ‘wait…’, noBackdrop: true });
$timeout(()={
//script for heavy calc
}, 300);