Author: jomendez

Senior Software Engineer with extensive experience in JavaScript. Feel passion for the UX developments using MV* JavaScript frameworks like AngularJS, etc. Intensive experience working with AngularJS, JQuery, UnderscoreJS, HTML, CSS, .Net, C# Stack. Love to share content and learn while teach.

Udacity Machine Learning Nano Degree Starbucks Capstone Challenge

Introduction

This blog post is part of the final project for the Udacity Machine Learning Engineer Nanodegree Program using AWS SageMaker

The project contains simulated data that mimics customer behavior on the Starbucks rewards mobile app. The goal of the project is to analyze this "historical" data in order to implement an algorithm that finds the most suiting offer type for each customer.

The data is contained in three files:

  • portfolio.json - containing offer ids and meta data about each offer (duration, type, etc.)
  • profile.json - demographic data for each customer
  • transcript.json - records for transactions, offers received, offers viewed, and offers completed

Here is the schema and explanation of each variable in the files:

portfolio.json

  • id (string) - offer id
  • offer_type (string) - type of offer ie BOGO, discount, informational
  • difficulty (int) - minimum required spend to complete an offer
  • reward (int) - reward given for completing an offer
  • duration (int) - time for offer to be open, in days
  • channels (list of strings)

profile.json

  • age (int) - age of the customer
  • became_member_on (int) - date when customer created an app account
  • gender (str) - gender of the customer (note some entries contain 'O' for other rather than M or F)
  • id (str) - customer id
  • income (float) - customer's income

transcript.json

  • event (str) - record description (ie transaction, offer received, offer viewed, etc.)
  • person (str) - customer id
  • time (int) - time in hours since start of test. The data begins at time t=0
  • value - (dict of strings) - either an offer id or transaction amount depending on the record

Solution Statement

This is a classification problem, my approach is going to be: create a Machine learning model to
predict the best offer for the user, for BOGO or discount (we are going to leave out the
informational offers, which have no real "conversion")
There are the steps to follow:

  • Fetching data
  • Data Cleaning
  • Data Preparation
  • Data visualization and analysis
  • Train Model
  • Evaluate the model

Evaluation Metrics

Since this is a classification problem we can use the following metrics to evaluate the model:

  • Precision The proportion of positive cases that were correctly identified.
  • Recall The proportion of actual positive cases which are correctly identified.
  • F1-score, that combines the two previous measures.
  • Roc_auc_score Area Under the ROC Curve (AUC), a measure that calculates the area
    under the Receiving Operating Characteristic Curve. This particular curve accounts that
    higher probabilities are associated with true positives and vice-versa.

Algorithms evaluated

We explored two algorithms Linear Learner and XGBoost to find the best models for for each offer, a Discount, a Buy One Get One (BOGO)
or Informational.

Project design

During the fetching data process, we have to join all the different pieces of information coming
from the 3 data sources, we need to fetch the data.

Data preparation. After analyzing the data, we transform the dataset through different stages
missing imputation, categories encoding, data standardization, etc.

Clean and Visualize the data to understand it's content and analyze it, to find possibles outliers
and remove them (if possible).

Data distribution visualization



Feature Engineering

Preprocessing with scikit-learn

  • Encode categorical variable gender (One Hot encoder)
  • Impute missing values
  • Normalize feature distributions

Things to notice:

  • There are 3 genre recorded in the dataset (F, M, O) which is going to be encoded during the feature engineering phase.
  • The scatterplot shows some odd structure but it should be because the data was simulated, also it doesn't look to be outliers.

Train the model

We used different ML models, one to predict the BOGO, the other to
predict the Discount and another to predict the informational offer. For each model, we try two algorithms, Linear Learner and Amazon Sage maker XG-Boost built in Algorithm.

For each algorithm, we tune the hyper-parameters to find the one with the best performance.
With the best two models, we can combine the results in order to obtain a single type of offer for
the user.

Evaluate the model we measure and compare the performances of the models with the
current benchmark, to learn if the proposed solution is viable for the offer attribution process.

Notebooks

The 3 notebooks and the data used for this project can be found in the following github repository:
https://github.com/jomendez/starbucks-captone-machine-learning.git

  • data-exploration.ipynb
  • feature_engineering.ipynb
  • training_the_model.ipynb

Results

CSS Box Model broken down

What is the CSS Box Model.

CSS Box Model

When a web page is rendered, the browser's rendering engine will display each HTML elements as a rectangular box.

Every rectangle is composed of a margin, border, padding and the element. If you open google chrome browser's console and inspect an element, go to the right column and scroll down until you see a box.

css box model

How to calculate the size of the box?

width = content width + padding-left + padding-right + border-left + border-right 
height = content height + padding-top + padding-bottom + border-top + border-button

note: The margin doesn't affect the size of the box, but it affects other content interacting around the box.

You can control how the size of the box it calculated with box-sizing.
The box-sizing CSS property sets how the total width and height of an element is calculated.

syntax:

box-sizing: content-box | border-box

See full example below for a better understanding.

See the Pen
Example css model box
by Jose Mendez (@jomendez)
on CodePen.

From the MDN:

Box model content area.

The content area, bounded by the content edge, contains the "real" content of the element, such as text, an image, or a video player. Its dimensions are the content width (or content-box width) and the content height (or content-box height). It often has a background color or background image.

If the box-sizing property is set to content-box (default) and if the element is a block element, the content area's size can be explicitly defined with the width, min-width, max-width, height, min-height, and max-height properties.

Box model padding area.

The padding area, bounded by the padding edge, extends the content area to include the element's padding. Its dimensions are the padding-box width and the padding-box height.

The thickness of the padding is determined by the padding-top, padding-right, padding-bottom, padding-left, and shorthand padding properties.

Box model border area.

The border area, bounded by the border edge, extends the padding area to include the element's borders. Its dimensions are the border-box width and the border-box height.

The thickness of the borders are determined by the border-width and shorthand border properties. If the box-sizing property is set to border-box, the border area's size can be explicitly defined with the width, min-width, max-width, height, min-height, and max-height properties. When there is a background (background-color or background-image) set on a box, it extends to the outer edge of the border (i.e. extends underneath the border in z-ordering). This default behavior can be altered with the background-clip css property.

Box model margin area.

The margin area, bounded by the margin edge, extends the border area to include an empty area used to separate the element from its neighbors. Its dimensions are the margin-box width and the margin-box height.
he size of the margin area is determined by the margin-top, margin-right, margin-bottom, margin-left, and shorthand margin properties. When margin collapsing occurs, the margin area is not clearly defined since margins are shared between boxes.

What if these values are not declared?

If margin, padding or borders are undeclared, they are either zero or the browser's default value.

See the box model with your own eyes.

You can visualize the box model in your page by opening the chrome developer tool and inspect the body of your page, add temporarely the following code:

* {
border: 1px solid red !important;
}

visualizing the css box model

Simplistic CRUD ionic 5 firestore TODO app example

Simplistic CRUD ionic 5 firestore TODO app example

This is a very simplistic CRUD (Create, Read, Update, Delete) Created with ionic 5 and firestore (database).

You can download the source code here.

Note:
I used this design as inspiration: https://dribbble.com/shots/4782162-To-Do-App-Concept-Minimal.

The idea here is to illustrate how to create a simple CRUD with firestore.

 Simplistic CRUD ionic 5 firestore TODO app example

Installation.

My advice is that you install nvm (to manage your node versions), here is tutorial step by step.

After you install nvm run the following commands:

nvm install 12

If everything went well you'll have installed node 12.0.0 .

Now you'll need to install ionic cli:

npm install -g @ionic/cli

Now is time to download the project and install it's dependencies.

git clone https://github.com/jomendez/ionic-5-crud-todo-with-firestore.git
cd ionic-5-crud-todo-with-firestore
npm install

Next step it to create a config file that will contain your firebase config keys here:

src/app/config/firebase.ts

If you are on bash you can use the following command:

touch src/app/config/firebase.ts

Assuming that you are familiar with firebase and already have an account, paste your configuration info into the firebase.ts file, it should looks something like this (This is just and example), If you are not familiar with firebase or doesn't have an account, I'll provide more information on how to do it bellow.

Example of configuration:

export const firebaseConfig = {
  apiKey: "AIzaSwERfdilPYtcNDfvFR6x944EowerdfghfSDFsfg",
  authDomain: "crud-456657.firebaseapp.com",
  databaseURL: "https://crud-456657.firebaseio.com",
  projectId: "crud-456657",
  storageBucket: "crud-456657.appspot.com",
  messagingSenderId: "10130122343423",
  appId: "1:1013012342277:web:sd9f8sd8fs8dfs89df89s",
  measurementId: "G-3FGJ34GFGJK"
};

How to get your firebase configuration.

Go to https://firebase.google.com/ and login with your account or create a new one.

Click on create a web app (This is the screen that you'll see the first time you create a projects):

image

Or add a new project (If you have already other projects created):

image

Enter the name of your project, accept the terms, and finish the other two steps on the wizard:

image

Next you'll see something like this, from here you can grab the configuration key.

image

If you missed the previous screen you can always go to the cog wheel on the left side panel, and click on project settings.

image

Now scroll down to the Your App section and click on config radio button, and copy the code highlighted on the image bellow, and paste it on the firebase.ts file.

image

Create web apps with Webpack

webpack banner

A simple but powerful project to rapid prototyping and development of front-end apps, with scss (optional), pug (optional), ES7, webpack (bundlefy, uglyfy, etc). Very useful to create static websites or apps, without having to worry about setting up the environment.

You can get the source code free on Github.

Features.

  • Modern Technologies: Full support for HTML5, PUG, JavaScript (Vanilla and ES7) and CSS (Sass, scss and PostCSS).
  • Built-in Server: Local development server with live reloading.
  • Performance Tuning: CSS and JavaScript transpilation, bundling, autoprefixing, and minification.
  • Image Optimization: Optimizes images for loading speed.
  • Favicon Generation: Automatically generates all favicons for Web, Apple and Android devices from one image file.
  • Code Linting: Full support for JavaScript (ESLint) and CSS (StyleLint) linting.
  • Setup Wizard: Optionally install helpful libraries and snippets .including:
    • jQuery.
    • Google Analytics.
    • FTP deployment process.
  • Cutting Edge: Uses Webpack for processing and bundling your code.
  • Deployment: Built-in support for deployment via FTP.

Requirements.

  • Node.js (developed with node 12).

Installation Steps.

Clone repo:

 git clone https://github.com/jomendez/simple-webpack-boilerplate &&
 cd simple-webpack-boilerplate &&
 rm -rf .git && git init
  1. Run npm install.
  2. Then run npm run setup to configure the app (include JQuery, etc).

In this step you'll be able to setup your google analytics, enable jquery, setup (optional) an FTP for deployments.

image

  1. Run npm run dev to open a browser and lunch a local web server with live-reload.
  2. Run npm run build build for production (./dist folder).
  3. Run npm run deploy to deploy your code to your FTP.

If everything went well (npm run dev)...

You browser should open a new tab and you should see something like this:

image

Quick example.

It is recommended to create a new page within the src folder or use the one that comes with the example page.pug to start adding your content.

image

Then you can go to src/js/app.js file and add the following lines:

const page = require("../page.pug");
document.querySelector('container').innerHTML = page();

The code above is going to transpile the pug file this particular case (but it can be an html file).
Then we will insert the content of the page into the container tag on the index.html.

My interactive portfolio

jose mendez portfolio

About the author

I describe myself as a passionate Front-end developer, focusing on create, test, and optimize full web apps with compelling user interfaces that runs in web browsers on any device, from desktop computer to tablets and cellphones, therefore, my principal allies are javascript, html, css, and the browser's event loop. That's why you will always find me learning and improving my skills to master those pillars and it's surrounding frameworks and technologies, such as angular, nodejs, ionic, react, etc.

Goals

Some times I find it a little bit hard to interpolate/transmit the description above to recruiters and hiring managers, just with my online presence (before any interview or conversation). That's why my intention is that they just get it by seeing and interacting with this portfolio.

Thus I decided to create a portfolio/profile/interactive resume as a complement for my actual resume and Linkedin profile, in addition to that, also for this two reasons:

  1. To challenge my skills with css animations and interaction.

  2. To create a showcase and challenge myself in a cool framework that I was recently immersing: Threejs which is used to create 3d environments, interactions, etc. on the web.

I wanted to do something different for this project, so I thought... why don't I create a 3d interactive version of myself. I did a research on the internet and I found this article, about how to create an interactive 3D character with Threejs, and I used it as a guide to create my own 3d interactive character:

https://tympanus.net/codrops/2019/10/14/how-to-create-an-interactive-3d-character-with-three-js/

About the (portfolio/profile/interactive resume)

Now, the portfolio/profile/interactive resume can be broken down into 2 main parts:

  1. The get to know the non-technical side of the person, the activities that he likes more (the 4 inner buttons) click on the buttons to know more about the author and see the interactions and animations of the character...

jose mendez portfolio inner menus

  1. The more technical part, more like a resume, showing the technical skills, places he worked, some of his recent work (games, Ionic starters, etc.), featured blog posts, certifications etc.That you can access by clicking on the outer buttons, (placed in the corners)

Portfolio

You can navigate to the portfolio/profile/interactive resume here:

http://www.jomendez.com/portfolio/

Javascript is weird

javascript is weird

I mean, javascript could be weird for developers that are just starting with the language and mostly for developers that comes from other languages like C++ or C# for example.

There are certain areas of the language that cam blow your mind 🤯🤯
In this article we are going to see 5 examples, and keep it simple withing the ~2 min reading time.

Numeric separators

Lets take for example the following code:

const number1 = 2_0_0_0;
const number2 = 6_0_0_0;

console.log(number1 + number2);

What you think the result is going to be?

You can copy and paste the code on a browser developer console or you can play with it yourself on codepen.

The result is 8000, but why?

The thing is that the underscore (_) is a numeric separator for numeric literal. It is used to group digits to make long numbers more readable, for example:

1234567

1_234_567 <-- Therefore, it is more readable with the separator.

More info in this article.

Some weird types.

Another example is when you run the code typeof null

typeof null;
// result object

which incorrectly suggests that null is an object (it isn't, it's a primitive value).

well in this case it is not just a weird thing, it is a bug in the language that can't be corrected because it will brake other parts of the code.

There is a more deep explanation in this article.

Meanwhile...

 typeof NaN;
 // result number.

Is it weird that NaN — “not a number” is actually a number or is just me? On the other hand, NaN is not equal to itself.

console.log(NaN === NaN);
//evaluates false.

Functions.

In javascript a function can self-invoke it self IIFE (Immediately Invoked Function Expression) , so you can do declaration and execution on the same statement.

(function() { console.log('hello'); })();
//display 'hello'

As far as I know this only exist in javascript. It is quite useful, can be used to avoid variable hoisting from within blocks, protect against polluting the global environment and simultaneously allow public access to methods while retaining privacy for variables defined within the function.

You can learn more on this article

Reset an array

And last but not least, the most weird way of resetting an array, you can set the length property to cero: arr.length = 0;.

const arr = [1,2,3,4,5,6];
console.log(arr);
// displays [1, 2, 3, 4, 5, 6]

arr.length = 0;
console.log(arr);
// displays []

I hope you like it, and if you know any other weird thing specific of the javascript language please feel free to share it with us. 😉

javascript proxy – Querying arrays with more readable methods

javascript

javascript Proxy.

This a new feature introduced in the ES6 standard. The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

The basics.

There are 3 key terms we need to define before we proceed:

  • handler — the placeholder object which contains the trap(s).
  • traps — the method(s) that provide property access.
  • target — object which the proxy virtualizes.

Here is a list of available Traps.

  • apply
  • construct
  • defineProperty
  • deleteProperty
  • get
  • getOwnPropertyDescriptor
  • getPrototypeOf
  • has
  • isExtensible
  • ownKeys
  • preventExtensions
  • set
  • setPrototypeOf

The list going on and on...

The syntax

let proxy = new Proxy( target, trapObject );

Quick example.

Let's see a quick example before we dive in into create our array query library.


class Student {
    constructor(first, last, scores) {
        this.firstName = first;
        this.lastName = last;
        this.testScores = scores;
    }
    get average() {
        let average = this.testScores.reduce( 
            (a,b) => a + b, 
            0 
        ) / this.testScores.length;
        return average;
    }
}

//instantiate the student class  
let john = new Student( 'John', 'Doe', [70, 80, 90] );

let johnProxy = new Proxy( john, {
   //we trap the get and print the property accessed 
    get: function( target, key, context ) {
        console.log( `john[${key}] was accessed.` );
        return john[key];
    } 
});

johnProxy.firstName;
//output.
//> john[firstName] was accessed.
//> <property content>

In this example we are just intercepting the properties called, for example johnProxy.firstName and printed it to the console, this could be used for debugging purpose in dev environments. We can do the same and intersect the 'set' and print a message to the console when we set the value on a property.

Let's create our query library.

It would be similar to write an assertion library, where you can use the name of the field you want to query withing the method's call instead of passing it as a parameter.

First let's create the assertion object.

Here we are going to define the assertion operations, our proxy object will return the objects based on this conditions:

let assertions = {
  Equals: (object, value) => object === value,
  IsNull: (object, value) => object === null,
  IsUndefined: (object, value) => object === undefined,
  IsEmpty: (object, value) => object.length === 0,
  Includes: (object, value) => object.includes(value),
  IsLowerThan: (object, value) => object < value,
  IsGreaterThan: (object, value) => object > value,
  EndsWith: (object, value) => object.endsWith(value),
  StartsWith: (object, value) => object.startsWith(value),
}

The proxy object.

Here we going to trap the get, of the target object to read the name of the property and make sure it starts with findWhere then we use the object field name we want to query, then the assertion (from the assertions above), So the syntax will looks something like this arr.findWhereNameStartsWith('Jo')

let wrap = arr => {
  const prefix = 'findWhere';
  let assertionNames = Object.keys(assertions);
  return new Proxy(arr, {
    get(target, propKey) {
      if (propKey in target) return target[propKey];
      var assertionName = assertionNames.find(assertion =>
        propKey.endsWith(assertion));
      if (propKey.startsWith(prefix)) {
        var field = propKey.substring(prefix.length,
          propKey.length - assertionName.length).toLowerCase();

        var assertion = assertions[assertionName];
        return value => {
          return target.filter(item => assertion(item[field], value));
        }
      }
    }
  })
}

Declare the object.

Next step is to declare the object and wrap it up with the proxy.

let arr = wrap([
  { name: 'John', age: 23, skills: ['mongodb', 'javascript'] },
  { name: 'Lily', age: 20, skills: ['redis'] },
  { name: 'Iris', age: 43, skills: ['python', 'javascript'] }
]);

Calling the methods

console.log(arr.findWhereNameEquals('Lily')) // finds Lily.
console.log(arr.findWhereSkillsIncludes('javascript')) // finds Iris.

The code above the first line will return the object where Property Name is equal to Lily and print it to the console, and the second line will return the object(s) where skills property includes javascript within the array.

Full source code.

Use the following code to play more with javascript proxy.

Things to notice.

Notice how we call the a method in the array that we didn't declared before arr.findWhereAgeIsLowerThan(30) we use the proxy to read this method, the expected syntax is findWhere + <array_field_name> + <assertion> this allow us to create custom queries using the array field name we want to query within the method name:

  • arr.findWhereAgeIsLowerThan(27);
  • arr.findWhereNameEquals('Lily');
  • arr.findWhereSkillsIncludes('javascript');

Wrapping up

With JavaScript proxy, you can wrap up an existing object and intercept any access to its attributes or methods. Even if they don't exist!

There are many real-world applications for Proxies:

  • Create SDK for an API.
  • validation.
  • value correction.
  • Revoke access to an object property/method.
  • Querying arrays with more readable methods (the main topic of this post).
  • property lookup extensions.
  • tracing property accesses.
  • Monitoring async functions.

Conclusions.

Javascript proxy enables a lot of possibilities to create more readable APIs where you can combine field names, assertions and create dynamics methods on the fly, however as you can see in the link below IE doesn't support it and this can be a downside to create code that needs to be supported by all major browsers. Unfortunately, it’s not possible to polyfill or transpile ES6 proxy code using tools such as Babel, because proxies have no ES5 equivalent.

For more info about javascript proxy and browser compatibility you can Learn more here

Improve performance of Hybrid apps to achieve almost native performance

native performance

Native performance

In this post we are going to cover how to achieve almost native performance with APIs that the web provides, to make fast hybrid apps. Every day the browsers evolves and improve at a very accelerated speed, so in my personal opinion we are going to get to a point were the performance of a web page running on a browser and the performance of a native applications is going to be seamless, if we are not there already.

What is a hybrid app

Hybrid applications are, websites packaged into a native wrapper (WebView). Basically, a hybrid app is a web app built using HTML5 and JavaScript, wrapped in a native container

Improving for the web

The goal of this article is to expose how to improve the HTML, CSS and javascript performance for mobile hybrid applications, is in deed to show some of the techniques used by the hybrid frameworks to boost the performance on mobile applications

Rendering Performance

To write performant sites and apps you need to understand how HTML, JavaScript and CSS is handled by the browser, and ensure that the code you write (and the other 3rd party code you include) runs as efficiently as possible. Read more here

requestAnimationFrame()

How it works:

  • When you call requestAnimationFrame, it request that your function be called before next paint
  • The function is called ~60 times/sec or throttled in the background
  • Animations optimized into single reflow/repaint
  • Smooth animations without jank

Example of requestAnimationFrame()

function animate() { 
  requestAnimationFrame(animate) 
  myElement.style.transform = `translateX(${x}px)`; 
  X++; 
} 
requestAnimationFrame(animate) 

requestAnimationFrame() Browser support

requestAnimationFrame availability

Reference: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame

Avoiding Layout Thrashing with DOM batching

What is layout thrashing:

Layout Thrashing is where a web browser has to reflow or repaint a web page many times before the page is ‘loaded’. In the days before JavaScript’s prevalence, websites were typically reflowed and painted just once, but these days it is increasingly common for JavaScript to run on page load which can cause modifications to the DOM and therefore extra reflows or repaints. Depending on the number of reflows and the complexity of the web page, there is potential to cause significant delay when loading the page, especially on lower powered devices such as mobile phones or tablets.

Reference: https://blog.idrsolutions.com/2014/08/beware-javascript-layout-thrashing/

This can be avoided by batching the writes and reads with fastdom (it relies on requestAnimationFrame())

How it works?

FastDom works as a regulatory layer between your app/library and the DOM. By batching DOM access we avoid unnecessary document reflows and dramatically speed up layout performance.

More info here

fastdom example for native performance

Reference: https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing

ionappfullplus - promo-banner

Efficient style modifications

Skip layout and paint by only modifying composite-only
properties.

Modern browsers can animate four things really cheaply: position, scale, rotation and opacity. If you animate anything else, it’s at your own risk, and the chances are you’re not going to hit a silky smooth 60fps.

html cheap operations

Reference: https://www.html5rocks.com/en/tutorials/speed/high-performance-animations/

Passive Event Listeners

Indicate touch events won’t block scrolling, Run event listener without holding up scrolling, provides smooth touch and scroll animations and gestures

Example:

passive event listener

Passive Event Listeners browser availability

passive event listeners availability

Reference: https://developers.google.com/web/updates/2016/06/passive-event-listeners

will-change

Indicates to the browser certain properties will change
frequently (ex: scrolling, animations, gestures, etc.)

Browser promotes element to it's own layer, smoother animations with less CPU usage (though
possibly higher RAM usage).

Note: Use with caution: If everything is optimized, nothing is

will-change example:

will-change: auto;
will-change: scroll-possition;
will-change: contents;
will-change: transform;
will-change: opacity;
will-change: left, top;

Fallback:

transform: translateX(0)

will-change - availability

will-change availability

Reference: https://developer.mozilla.org/en/docs/Web/CSS/will-change

CSS containment

The new CSS Containment property lets developers limit the scope of the browser's styles, layout and paint work. learn more here

In other words, it indicate isolated elements, browser optimizes, limiting recalculation paint/layout/size/style to sub-tree, deriving in fast component updates

Example of CSS containment

containment example

css containment example speed

This layout is 1425x faster!

CSS containment browser availability

css containment availability

Reference: https://developers.google.com/web/updates/2016/06/css-containment

Conclusions

We highly recommend you to use a framework, frameworks do this stuff for you, for example in ionic all the components are optimized with these performance improvements.
In other words if you want to create a hybrid a mobile application try to avoid direct DOM manipulation, always use a framework, with that you'll be more close to achieve native performance .

14 ionic Hacks to improve hybrid app development

ionic hacks

I've been working with ionic for a while now, and I decided to make a compilation of useful ionic hacks, tips, tricks or simply useful information from my journey, so I can have a centralized place where I can find easy and fast this useful compilation, and also share it with all the ionic developers, I hope this might be of some help for others.

I divided it into 3 categories (for now), I think this is going to be an on-going process of keeping adding ionic hacks, Every time I find something interesting I'll update this post.

Ionic hacks index

- UI/UX

  • Hide Scrollbar when using Chrome
  • Configure your Ionic App Config
  • Making Text Selectable

- Debugging

  • Remote Debug on Android and iOS
  • Debugging faster on iOS & Android with -livereload
  • Targeting Platforms and Operating Systems
  • Target platform from url

- Practical solutions

  • ngZone, include into angular cycle digest
  • Declare globally window's var for plugins with no wrappers
  • @ViewChild only works when the view is loaded
  • Use Crosswalk for Android
  • Improving Loading Times and performance of your Ionic App
  • Using Custom Scripts
  • Add cordova-android-support-gradle-release to resolve gradle version conflict issues (Android)

Lets get started

Hide Scrollbar when using Chrome

When debugging your ionic app, using for example ionic serve lab on Chrome, One thing that is really annoying is the visible scrollbar:
chrome hide scroll bar

Go to app/apps.css to add the following css globally (this will hide all the scrollbars inside your application):

* ::-webkit-scrollbar {
  display: none;
}

Configure your ionic app config

We can modify the general behavior of components through the root configuration of our app. The Config lets you configure your entire app or specific platforms. You can set the tab placement, icon mode, animations, define back button icon, and more. This is one of the most interesting ionic hacks.

@NgModule({
  declarations: [ MyApp ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp, {
      backButtonText: 'Go Back',
      iconMode: 'ios',
      modalEnter: 'modal-slide-in',
      modalLeave: 'modal-slide-out',
      tabsPlacement: 'bottom',
      pageTransition: 'ios-transition'
    }, {}
  )],
  bootstrap: [IonicApp],
  entryComponents: [ MyApp ],
  providers: []
})

You could also configure these values at a component level

<ion-tabs tabsPlacement="top">
  <ion-tab tabTitle="Dash" tabIcon="pulse" [root]="tabRoot"></ion-tab>
</ion-tabs>

Any value can be added to config, and looked up at a later in any component.

config.set('ios', 'favoriteColor', 'green');

// from any page in your app:
config.get('favoriteColor'); // 'green' when iOS

Making Text Selectable

You might have noticed that you can’t select the text inside your ionic app, which is especially challenging if you want to enable the user to be able to select text (to be able to copy/paste for example) inside of your application.

You can apply this css rule to all the application, or just to a specific element container:

body {
    -webkit-user-select: text;
    user-select: text;
}

Remote Debug on Android and iOS

Android

For this you'll need to have Android Studio installed on your computer

Connect your device to the computer and make sure it is correctly detected, by running adb devices (Android SDK is required).

Build your app and install it on your device by running ionic cordova run android --device command. Once your app is launched on the device, open the Chrome browser on your computer, in a new tab's url bar type: chrome://inspect/#devices, and inspect your device.

ionic device remote debug

iOS

Plug in your device and make sure it is correctly detected by your computer. Build your app and install it on your device by running ionic run ios --device.

Once your app is launched on device, open Safari dev tools (on your computer) by clicking on Develop > Your iPhone > Your app:

ios safari remote debugging

Debugging faster on iOS & Android with -livereload

This is very simple, when you run the command to deploy to your device, add parameter -l ionic cordova run android --device -l you can also add parameter -c to display console logs in your command prompt:

--livereload, -l Spin up server to live-reload www files
--consolelogs, -c Print out console logs to terminal

Targeting Platforms and Operating Systems

Some times we need to detect in which platform the code is running, some scenarios are when you are using cordova plugins that are only supported by for example Android and iOS, and you are planning to deploy your app also to Windows app store, and as a PWA. A specific plugin with this characteristics is the admob free plugin.

ionic-platform

Here is a list of the platforms supported:

ionic platforms

Target platform from url

This is a useful functionality, when you run your application in the browser ionic serve you can use the --lab parameter to open your app into an iframe wrapper that call for specific platform, in that way is easy for you to visually select what platform target you want the ionic application to run.

But what happen when you deploy your application to firebase hosting for example, maybe to demo a prototype for your clients, or in our case to demo our latest starter on the ionic market

In our case wecalled the application with the following parameters: http://< path to your app >?production=true&ionicplatform=android

For Android:
https://www.jomendez.com/ionappfullplus/?production=true&ionicplatform=android

For iOS:
https://www.jomendez.com/ionappfullplus/?production=true&ionicplatform=ios

ngZone, include into angular cycle digest

As the official angular documentation states:

The most common use of this service is to optimize performance when starting a work consisting of one or more asynchronous tasks that don't require UI updates or error handling to be handled by Angular. Such tasks can be kicked off via runOutsideAngular and if needed, these tasks can reenter the Angular zone via run.

In other words, it allows you to either execute code outside angular cycle digest (to improve performance) or, to include code executed asynchronous (outside angular) inside the angular cycle digest.

A practical example is when we created the dialogFlow (chatbot) showcase, since the cordova plugin didn't have the ionic wrapper, it executes the code outside of angular:

      this.ngZone.run(() => {
        console.log(response);
        let text = this.processCommand(response.result.fulfillment.speech);
        this.messages.push({
          text: text,
          sender: "api"
        });
        this.content.scrollToBottom(400);
      });

ionappfullplus - promo-banner

Declare globally window's var for plugins with no wrappers

This is the same scenario as before, we needed to call the dialogflow plugin's api via window variable window.ApiAIPlugin if try to call ApiAIPlugin from the window variable, Typescript will complaint about ApiAIPlugin not been a property of window

We have 3 solution for this, one is what the title of this category suggest, declare the window variable globally:

import { DatabaseProvider } from "../../../providers/database/database";

declare var window;

@IonicPage()
@Component({
  selector: 'page-dialogflow-chat',
  templateUrl: 'dialogflow-chat.html',
})
export class DialogflowChatPage {
//...
}

Second is to cast the window variable to "any" type

(<any>window).ApiAIPlugin

And the third one is to call the property using the arry call format:

window["ApiAIPlugin"].requestText

@ViewChild only works when the view is loaded

I remember faced this issue before, but the last time I did was crating the signature-pad showcase where I needed to reference a canvas within the HTML, and call the method clear() as soon as the page is loaded.

Once you declare the @ViewChild() if you try to use the reference on the constructor method you'll get an error:

Uncaught TypeError: Cannot read property 'clear' of null

One potencial solution is to call it when ionViewDidEnter lifecycle event is called:

 ionViewDidEnter() {
    this.signaturePad.clear();
 }

Use Crosswalk for Android

Every Android version runs a different WebView (a browser that runs your ionic application). The performance is different across devices, and it can be really bad on the old Android devices. To get a consistent performance and experience with fluidity and responsiveness on every Android device, you can install Crosswalk. It basically embeds the latest Chromium browser into your application, it will add around 20Mb per APK, both ARM and X86.

To install the plugin you can run the following command:


ionic plugin add cordova-plugin-crosswalk-webview

Note:

Knowing this was very useful, when created our multi-platform Gamefy (which is a bundle of 5 games in 1), because the performance of the games was poor on android low-end devices, and crosswalk fixed the issue.

Improving Loading Times and performance of your Ionic App

As every developer should know you are first of all responsible for writing good code that performs. It’s not the fault of Ionic (most of the time) if the app loads slowly, but there are few thinks you can do to improve your performance.

Make sure to use lazy load for your page, although when you generate a page from the ionic CLI it generate the page ready for lazy loading, the starters templates doesn't use lazy load.

By doing this, your app won’t load all the pages on startup immediately but rather prolong the loading a bit so the first page will appear a lot faster. This is very important if you are creating PWA, this is one of the key points for PWA.

To learn more about how to use lazy load on an Ionic app, visit here

As mentioned before, another improve is to use ngZone to execute tasks that don't require UI updates or error handling to be handled by Angular to improve performance.

As mentioned in the previous point, install crosswall can be a boost in your performance specially for low-end android devices.

Finally, what can make a huge change for all Ionic apps is building for production. This will remove any debugging plugin, minify, uglify the code, etc. Simply run:


ionic cordova build ios --prod --release

That command will activate Angular’s production mode, and run a full production build so you can get the smallest bundle possible.
It will take a while, especially the first time you run it, but it is worth it.

Using Custom Scripts

You'll notice that in the package.json file of your project there are a property called scripts

"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build",
"lint": "ionic-app-scripts lint",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve",
}

Here you can define your own tasks, for deployment, android app signing, zipalign, etc. So every time you need to sign your apk to deploy to the app store, you can define a task to do that

"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build",
"lint": "ionic-app-scripts lint",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve",
"jarsign": "jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore release-key.jks path\\platforms\\android\\app\\build\\outputs\\apk\\release\\app-release-unsigned.apk jomendez",
"zipalign": "\"C:\\Users\\user\\AppData\\Local\\Android\\sdk\\build-tools\\27.0.3\\zipalign\" -f -v 4 path\\platforms\\android\\app\\build\\outputs\\apk\\release\\app-release-unsigned.apk path\\platforms\\android\\app\\build\\outputs\\apk\\release\\name.apk",

}

Then you just have to run:


npm run jarsign
or
npm run zipalign

Add cordova-android-support-gradle-release to resolve gradle version conflict issues (Android)

In my experience sometimes cordova plugins throw errors regarding packages versions vs environment versions, or the most common for me has been conflict between two or more cordova plugin, the most recent was when I was trying to integrate the barcodescanner plugin on ionAppFullPlus together with the facebook plugin.

I received the following error:

Build fails with error
> java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

The problem was that the two plugin uses different versions for the supporting libraries

After few hours trying everything I found this plugin for Android (cordova-android-support-gradle-release) that aligns various versions of the Android Support libraries specified by other plugins to a specific version, which fixed the issue.

Installation:

cordova plugin add cordova-android-support-gradle-release

Conclutions

I will try to update this post often with new ionic hacks, I think there is a lot of things that can be done to to improve the performance of ionic applications, or the developer's productivity, etc.

How to combine tab navigation with side menu in Ionic

menu banner

With ionic CLI you can quickly and easy create a Side Menu app just by running the command

ionic start sidemenu

or create a tab application by running:

ionic start tab

Those are common navigation patterns used by a lot of mobile applications today:

In this short tutorial we are going to try to combine those different navigation patterns into one application.

We will keep things simple, we are going to make the transition from a login page (Taken from ionappFull4Pro starter) to our inside navigation, which combines side menu and tabs.

Login workflow

This is a module that is part of the ionappFull4Pro starter, and with it you'll be able to:
- Login with Firebase username and password
- Login with Google (native) //for this the app must run on a device or an emulator
- Login with Facebook (native) //for this the app must run on a device or an emulator
- Login with Google (PWA mode) //this mean that you can login with google running the app on the browser
- Recover your password
- Register new user
- User editable profile

Prerequisites

We assume you are familiar with ionic, and you have set up your ionic environment, if not you can follow this tutorial: https://ionicframework.com/getting-started#cli

We are going to start creating a blank ionic template using the CLI commandand then we are going to create all the needed pages:

ionic start convinedNavigation blank
ionic g page menu
ionic g page tabs
ionic g page tab1
ionic g page tab2

Spoiler alert

We are going to use lazy loading, for load our pages, so after create the new project feel free to remove the home page generated by default.

Now use your favorite IDE to open the project (mine is Microsoft Code) and change the rootPage variable to a string "LoginPwaPage", this will tells ionic LoginPwaPage is the entry point page.

ionic app component

Lest import the login component from ionappFull4Pro into our project. First, copy the login-workflow folder (which contains login, profile,recover and register pages) into our project (also you can follow the instructions on the instructions.txt file included in the folder).

import login from ionappfull4pro

This is how it should looks like:

login workflow in our local project

Follow the instructions on the instructions.txt file to complete the import of this component:

Instructions.txt

Instructions to import this component in your existing ionic app.

If you don't have an ionic app created, create one, by running the following command on the command console, on the folder you want your app to be created, 
once your app is created locate the pages folder and copy this component (folder) in the page folder:

$ ionic start MyAppName blank

for more info go to the ionic official documentation.


Once you have your app created Install the components dependencies (see what is imported on the ts files on this component):

On your firebase dashboard enable the email/password methods, see documentation: http://jomendez.com/ionAppFull4Pro-documentation/#!/firebase_configuration
	
Notes:
For this component to work you need to setup firebase and also import the DatabaseProvider from "/providers/database/database" included on the ionAppFull4Pro project.
Also import /providers/auth-data provided from ionAppFull4Pro to your app
Also copy src/config/config.ts from this project (ionAppFull4Pro) to your application, and enter your firebase information.

Remember to install the firebase dependencies and add the providers to the app.module.ts file:

npm i --save angularfire2 firebase

For this tutorial we are going to use the login-pwa component to keep it simple and be able to test the app in the browser without having to run it on a device or emulator. Login pwa component uses some "fancy" animation library called VegasJs (this is not required for the login component to work, you can easily remove it). In order to include it and make it work you'll need to copy few thinks from ionappFull4Pro starter, first include the jquery library and vegas dependencies on index.thml and then copy the files into assets folder:

add jquery and vega index
images on the assets folder

This is how the app.modules should looks like after all the imports and declaration:

app modules dependencies

One last thing it we have to redirect to menu page after login, so we will have to modify the login() method, lets comment the code that redirects to the profile page and add the code that set the MenuPage as the root:

redirect to menupage

Build the menu navigation:

Add this menu code to your src/pages/menu/menu.html


<ion-menu [content]="content">
  <ion-header>
    <ion-toolbar>
      <ion-title>Menu</ion-title>
    </ion-toolbar>
  </ion-header>
 
  <ion-content>
    <ion-list>
      <button ion-item menuClose *ngFor="let p of pages" (click)="openPage(p)">
          <ion-icon item-start [name]="p.icon" [color]="isActive(p)"></ion-icon>
          {{ p.title }}
        </button>
    </ion-list>
  </ion-content>
</ion-menu>
 
<!-- main navigation -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false"></ion-nav>

Add this code to src/pages/menu/menu.ts
Notice that in page one of the pages that can be called is the Profile page that comes with the login component from ionappFull4Pro starter

 pages = [
    { title: 'Tab 1', pageName: 'TabsPage', tabComponent: 'Tab1Page', index: 0, icon: 'home' },
    { title: 'Tab 2', pageName: 'TabsPage', tabComponent: 'Tab2Page', index: 1, icon: 'shuffle' },
    { title: 'Profile', pageName: 'AfterLoginPage', icon: 'contacts' },,//notice we cal here the Profile that comes with the login workflow component on ionAppFull4Pro starter
  ];
import { Tab2Page } from './../tab2/tab2';
import { Tab1Page } from './../tab1/tab1';
import { TabsPage } from './../tabs/tabs';
import { Component, ViewChild } from '@angular/core';
import { IonicPage, NavController, Nav } from 'ionic-angular';
 

@IonicPage()
@Component({
  selector: 'page-menu',
  templateUrl: 'menu.html',
})
export class MenuPage {
  // Basic root for our content view
  rootPage = 'TabsPage';
 
  // Reference to the app's root nav
  @ViewChild(Nav) nav: Nav;
 
  pages = [
    { title: 'Tab 1', pageName: 'TabsPage', tabComponent: 'Tab1Page', index: 0, icon: 'home' },
    { title: 'Tab 2', pageName: 'TabsPage', tabComponent: 'Tab2Page', index: 1, icon: 'shuffle' },
    { title: 'Profile', pageName: 'AfterLoginPage', icon: 'contacts' },
  ];

  constructor(public navCtrl: NavController) { }
 
  openPage(page) {
    let params = {};
 
    // The index is equal to the order of our tabs inside tabs.ts
    if (page.index) {
      params = { tabIndex: page.index };
    }
 
    // The active child nav is our Tabs Navigation
    if (this.nav.getActiveChildNav() && page.index != undefined) {
      this.nav.getActiveChildNav().select(page.index);
    } else {
      // Tabs are not active, so reset the root page 
      // In this case: moving to or from SpecialPage
      this.nav.setRoot(page.pageName, params);
    }
  }
 
  isActive(page) {
    
    let childNav = this.nav.getActiveChildNav();
 
    if (childNav) {
      if (childNav.getSelected() && childNav.getSelected().root === page.tabComponent) {
        return 'primary';
      }
      return;
    }
 
    // Fallback needed when there is no active childnav (tabs not active)
    if (this.nav.getActive() && this.nav.getActive().name === page.pageName) {
      return 'primary';
    }
    return;
  }
 
}


Adding the Tab

Open your src/pages/tabs/tabs.html and change it to:

<ion-tabs [selectedIndex]="myIndex">
  <ion-tab [root]="tab1Root" tabTitle="Tab 1" tabIcon="home"></ion-tab>
  <ion-tab [root]="tab2Root" tabTitle="Tab 2" tabIcon="contacts"></ion-tab>
</ion-tabs>

This tab bar will be pointing to the 2 tabs pages we created at the beginning.
Now change your src/pages/tabs/tabs.ts

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
 
@IonicPage()
@Component({
  selector: 'page-tabs',
  templateUrl: 'tabs.html',
})
export class TabsPage {
 
  tab1Root: any = 'Tab1Page';
  tab2Root: any = 'Tab2Page';
  myIndex: number;
 
  constructor(navParams: NavParams) {
    // Set the active tab based on the passed index from menu.ts
    this.myIndex = navParams.data.tabIndex || 0;
  }
}

Now we only need to add a menu button to all of our pages so we can toggle the menu from everywhere. Go through these 2 files (tab1, tab2) and change it, add the following html code:

Tab1

<ion-header>
  <ion-navbar>
      <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Tab1</ion-title>
  </ion-navbar>
</ion-header>
 
<ion-content padding>
  This is tab 1 content
</ion-content>

Tab2

<ion-header>
  <ion-navbar>
      <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Tab2</ion-title>
  </ion-navbar>
</ion-header>
 
<ion-content padding>
  This is tab 2 content
</ion-content>

This is the result:

Try it yourself:

Conclusions

With this article we got two navigation pattern working together, it could be a little bit tricky, but it is possible, also we were able to integrate/import the login module very easy, from the ionAppFull4Pro ionic starter into our applications, saving us a lot of time and development effort, and just with that we were able to implement an app with firebase login, google login, recover password, register user, and profile page.

Cheers,
Jose