Nikola Brežnjak blog - Tackling software development with a dose of humor
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Home
Daily Thoughts
Ionic
Stack Overflow
Books
About me
  • Home
  • Daily Thoughts
  • Ionic
  • Stack Overflow
  • Books
  • About me
Nikola Brežnjak blog - Tackling software development with a dose of humor
JavaScript

Making AJAX calls in pure JavaScript

Making AJAX calls in pure JavaScript

TL;DR

In this, beginner oriented, post I’ll show you how to make AJAX calls in pure JavaScript, step by step with few examples.

So, what is AJAX?

AJAX stands for:

  • Asynchronous – means that if you start some request (call some API), you can move on to another task before that request is finished. This is the direct opposite of when you execute something synchronously – in that case, you have to wait for it to finish before moving on to another task.
  • JavaScript – the best language ever ?
  • And – added ‘And’ as three letter acronyms just don’t cut it anymore
  • XML – Extensible Markup Language that no one on the web uses anymore :), JSON FTW 🙂

OK, but what does it do?

AJAX lets you load some new data to your web page, without the need to reload the whole webpage. This behavior makes your site feel faster and more responsive. Not to mention that nowadays this is the defacto standard. Namely, if you come across a site on which you fill out a form and submit it, and then it has to reload, the site basically screams at you: “OOOOOLD!”.

Where can I see it in action?

I’d argue that you can see it in action on any decent webpage nowadays. For example, load the Google website in your browser, and open the dev tools. On Chrome, you can do that with right-clicking a mouse and selecting Inspect, and then clicking in the Network tab.

If you also select the XHR filter and start writing something in the search bar, you’ll start seeing the AJAX requests. Very important to note here is that the site didn’t reload.

If you click on one of these items in the Name column and then click the Response tab, you’ll see the actual response that the server sent back.

⚠️ This is an important concept to remember: web works in a Request/Response kind of a way. A client (your browser) sends Request to some server, and the server returns a Response, which then client processes.

Another example of AJAX in action is when you are presented with a newsletter signup form on some site. You fill out the name and an email address, click send, and the site doesn’t refresh. Instead, you get the message right there like “you’ve been subscribed, check your email”.

This is great, but how do I use it?

No one likes the theory, but it actually may help here. To use AJAX in JavaScript, you need to do four things:

  • create a XMLHttpRequest object
  • write the callback function
  • open the request
  • send the request

I know I know, you must be like:

OK, OK, so let’s take those steps from above and turn them into code:

  • create a XMLHttpRequest object
    • var xhr = new XMLHttpRequest();
    • of course, you can name the variable differently as well, but I hope you know this much about JS, or programming in general, so won’t go into that here ?)
  • write the callback function
    • xhr.onreadystatechange = function() {};
  • open the request
    • xhr.open('GET', 'http://www.google.com');
    • the first parameter is the type of the request. Another common one is ‘POST’, and we’ll talk about it more in the next post.
    • the second parameter is the URL (a link) to which you want to send the request. In our case, that’s Google’s website.
  • send the request
    • xhr.send()
    • finally, send the request

If we put it all in one place, we get this:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {};
xhr.open('GET', 'http://www.google.com');
xhr.send()

Fine, but, erm, where do I test this?!

You’ll be a bit disappointed to learn that the code above doesn’t do much.

Besides, where could you test this? Well, for one thing, you can’t test it on your local machine in a way of creating an index.html and opening it in your browser.

You have to test this on some website that’s online. Examples speak more than words, so let’s go and open up http://www.google.com in your browser.

Now let’s do a few things:

  • open the dev tools
  • select the Elements tab
  • right click the html element and select Edit as HTML
  • delete everything and click outside of the box that appears and you’ll end up with a blank page
  • add a div to the body tag like this: <div id="result">Testing</div>

Next, in the Console tab of the dev tools, write this:

var clearResult = function() {
    document.getElementById('result').innerHTML = '';
}

and then call it like this: clearResult().

The purists will kill me for using var instead of let ?

To save you a lot of trouble by figuring out why using clear as the function name won’t work, check out this post.

Now, copy paste the following code to the Console tab and press Enter to execute it:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4){
        document.getElementById('result').innerHTML = xhr.responseText;
    }
};
xhr.open('GET', 'https://www.google.com');
xhr.send();

Woilà, what do you get? You just loaded Google’s main page back in ?

AJAX can only go so far, most of the time

If you try playing out with the URL in the open command, you’ll soon stumble upon CORS. It basically means that, if your domain is google.com, then you can’t load the data from:

  • some other domain like example.com
  • a subdomain like abc.google.com
  • a different port on the same domain like google.com:8080
  • a different protocol like http

There are ways to get around this (server proxy on the same domain, JSONP, CORS setting on the domain server, using browser plugins), and I encourage you to dig deeper and learn more about it on your own (or wait until I write about it in some other post).

I want more examples

Great, happy to provide you with them.

Load up my test site. Copy the AJAX function from above and replace https://www.google.com with http://nikola-breznjak.com/_testings/ajax/test1.php and observe what happens.

Try changing the link to http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola and see what happens then. This is called sending the parameter (ime) in the URL. Which brings me to the following difference…

GET vs. POST

There are two common methods for sending HTTP requests:

  • GET – used for most requests. The browser uses a GET method whenever it requests a new web page, CSS file, image, and so on. Use GET when you want to “get” something from the server.
  • POST – frequently used with web forms to send data to the server. Use POST when sending data that will be stored, deleted or updated on the server.

You can send parameters with GET in the URL, and that’s a benefit as well as the downside, as there’s a limit to the length of the parameters in a GET request (2048 chars), as well as there’s a security issue. With POST you can send way more data, and in a secure way.

XML vs. JSON

XML is short for eXtensible Markup Language, and you can learn more about it here. It used to be for transmitting structured data that’s easily parsable by computers. You’ll notice it’s similar to HTML; for example:

<phones>
    <phone>+38598123456</phone>
    <phone>+38598654321</phone>
</phones>

Though, TBH, it’s not used on the web, so I won’t bore you with it. What is used, extensively, on the web these days is JSON. It looks very much like JavaScript object literal, with one addition – the key also needs to be enclosed with double quotes. For example:

[
    {"phone":"+38598123456"},
    {"phone":"+38598654321"}
]

This is an array of objects which consist (currently) of one property called phone.

Mini Project

So, mini project time now. Let’s suppose that you work at a job where your boss says that you need to update this page to have the sidebar load the advertisement from the API that can be found here.

He also adds that you need to make it consistent to the current design (Bulma rocks btw!) and make the price be of different colors based on the following rules:

If the price (cijena in the API response) is below 100 make it some greenish color. If it’s between 100 and 300, make it some blueish color. If it’s more than 300, make it red.

How are you going to do it?

I encourage you to try it out for yourself and only then come back and see my approach below.

You should have this in the end:

You can totally test this now by pasting the code below in the Console on this page.

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
    if (xhr.readyState === 4){
        var oglasi = JSON.parse(xhr.responseText);
        var oglasiHTML = '';
        for (var i=0; i<oglasi.length; i++){
        var klasaCijene = '';
        if (oglasi[i].cijena <100){
            klasaCijene = 'is-success';
        }
        else if (oglasi[i].cijena > 100 && oglasi[i].cijena < 300){
            klasaCijene = 'is-info';
        }
        else if (oglasi[i].cijena > 300){
            klasaCijene = 'is-danger';
        }

            oglasiHTML += '<p><span class="tag ' + klasaCijene + '">' + oglasi[i].cijena + '</span>' + ' ' + oglasi[i].tekst + '</p>';
        }

        document.getElementById('oglasi').innerHTML = oglasiHTML;
    }
};

xhr.open('GET', 'http://nikola-breznjak.com/_testings/ajax/test2.php');
xhr.send();

Conclusion

That’s all folks, hope it was useful and see you next time when I’ll show you how much easier it is to do all of this with a jQuery. Sure sure, in some later posts we’ll come to the fetch API as well. But first, baby steps ?

JavaScript

Don’t name your global JavaScript function ‘clear’

TL;DR

Same as the title, don’t name your global JavaScript function clear.

TL;DR

I learned the hard way not to use clear as the name for my global JavaScript function.

Given the example below:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Test 1</title>

    <script>
        function clear() {
            document.getElementById('result').innerHTML = '';
        }
    </script>
</head>

<body>
    <button id="clear" onclick="clear()">Clear</button>
    <div id="result">Testing</div>
</body>

</html>

and clicking the Clear button, the Testing text will just not be removed.

I found this very cool StackOverflow explanation of why is that. The gist of it is that this way the document.clear function is being called, and simply renaming it to something like clearResult works fine.

Angular 2, Go

JWT authentication in an Angular application with a Go backend

TL;DR

In this tutorial, I’m going to show you how to build a simple web app that handles authentication using JWT. The frontend will be written in Angular 5, and the backend will be in Go. I’ll cover some theory concepts along the way as well.

You can check out the final source code on Github.

JWT

JWT stands for JSON Web Token, and it is an encoded string that, for example, looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==.mKIuU0V0Bo99JU5XbeMe6g-Hrd3ZxJRlmdHFrEkz0Wk

If you split this string by ., you’ll get three separate strings:

  • header – contains encoded information about the token
  • payload – contains encoded data that is being transmitted between two parties
  • verification signature – used to verify that the data has not been changed

The official website says this about JWTs:

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

If you’re like

then I don’t blame you. So let’s define this in a bit more detail.

JSON Web Tokens are a way to communicate information between two parties securely. A claim is some data that is sent along with the token, like user_id.

Secure communication in this context refers to the fact that we can be certain that the information has not been tampered with, but it does not mean that it is hidden from a potential attacker. Actually, a potential attacker could read what is in JWT (so please don’t send any passwords as claims), but he wouldn’t be able to modify it and send it back in that form.

Based on the premise that a JWT can’t be tampered with, it is very useful for authentication. We can give a user a JWT that contains their userid, which can be stored locally and used to verify that requests are coming from an authenticated user.

JWTs are short, so you can easily send them as a POST parameter, HTTP header, or add it as a query string to a URL. You can store them in local storage and then send them with every request to the server, making sure the user is authorized.

It seems that a lot of people like and use them. However, I must note that a lot of security researchers frown upon this practice.

Learn by doing

It’s not necessary to know the intricate details of how JWTs work, to be able to use them, but it can sometimes give you this great feeling of awesomeness when you go an extra mile.

So, with that spirit in mind, we’re going to create our own JSON Web Token now ?

Payload

For example, say that I want to send the following data securely to someone:

{
    "user_id": "a1b2c3",
    "username": "nikola"
}

This is my payload. To add it to our JWT, we first need to base64 encode it. You can do this easily with JavaScript inside your browser’s developer tools (Console window) by using the btoa function:

btoa(JSON.stringify({
    "user_id": "a1b2c3",
    "username": "nikola"
}));

Or, with the ever so slightly popular Go, you would do it like this:

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    data := `{"user_id":"a1b2c3","username":"nikola"}`
    uEnc := base64.URLEncoding.EncodeToString([]byte(data))
    fmt.Println(uEnc)
}

which then gives you:
eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==

⚠️ In the JavaScript example we had to use the JSON.stringify function first, as otherwise the resulting decoded string would just be [object Object].

We can decode the base64 encoded string by using the atob function in JavaScript:

atob('eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==')

or in Go:

uDec, _ := base64.URLEncoding.DecodeString("eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==")

The result, in both cases, is {"user_id":"a1b2c3","username":"nikola"}

Header

Next, we need to encode the header:

{
    "alg": "HS256",
    "typ": "JWT"
}

In JWT, the header actually comes before the payload. In the header, we are specifying that we created a JWT and that we used a certain hashing algorithm HS256. This particular algorithm will allow us to use a secret password. You could use the RSA SHA256 algorithm to use a private & public key pair instead. Here’s a good tutorial on the different hashing algorithms used in JWTs.

The header, base64 encoded, looks like this: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

Verification signature

As the last step, we need to create the verification signature. We do this by joining the encoded header and payload string, separating them with .. After that, we need to apply the HS256 algorithm along with the secret password that is only known to the sender.

Our encoded header and payload look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==

We’re going to use 42isTheAnswer as our password.

We don’t have this hashing function available in the browser, so we need to use Node to do it. First, install base64url by running: npm install base64url. If you’re new to Node, I recommend this tutorial.

Create a new JavaScript file with the following content:

var base64url = require('base64url');

var crypto    = require('crypto');
var message     = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==';
var key       = '42isTheAnswer';
var algorithm = 'sha256';
var hash, hmac;

hmac = crypto.createHmac(algorithm, key);
hmac.setEncoding('base64');
hmac.write(message);
hmac.end();
hash = hmac.read();

var final = base64url.fromBase64(hash);
console.log(final);

Or, in Go, you would use:

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
)

func main() {
    message := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ=="
    sKey := "42isTheAnswer"

    key := []byte(sKey)
    h := hmac.New(sha256.New, key)
    h.Write([]byte(message))
    b := base64.URLEncoding.EncodeToString(h.Sum(nil))
    fmt.Println(string(a))
}

After you execute any of the scripts above, you should get this string:

mKIuU0V0Bo99JU5XbeMe6g-Hrd3ZxJRlmdHFrEkz0Wk

Now we add this string to the token from before (also separated by .) and we get:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYTFiMmMzIiwidXNlcm5hbWUiOiJuaWtvbGEifQ==.mKIuU0V0Bo99JU5XbeMe6g-Hrd3ZxJRlmdHFrEkz0Wk

We can test this JWT on jwt.io:

Security

Our payload and header were just base64 encoded, which can just as easily be base64 decoded. So, how exactly is this secure then?

The important point is that it can’t be changed, since the verification signature is built using the header and the payload data, if either of those change, we won’t be able to verify the signature – so if somebody tampers with the JWT, we will know.

Since the payload data has been changed, the verification signature will no longer match, and there’s no way to forge the signature unless you know the secret that was used to hash it. When this JWT hits the server, it will know that it has been tampered with.

General remarks

If JWTs are used for Authentication, they will contain at least a user ID and an expiration timestamp.
This type of token is known as a Bearer Token. It identifies the user that owns it and defines a user session.

A Bearer Token is a signed temporary replacement for the username/password combination. The very first step for implementing JWT-based authentication is to issue a Bearer Token and give it to the user through the process of logging in.

The key property of JWTs is that to confirm if they are valid we only need to look at the token itself.

Demo apps

We’re going to build a simple full-stack app that will have a:

  • landing page
  • login page
  • members page
  • backend for authentication

Here’s how the authentication with JWTs works:

  • user submits the username and password to the server via the login page
  • server validates the sent data and creates a JWT token with a payload containing the user’s id and an expiration timestamp
  • server signs the Header and Payload with a secret password and sends it back to the user’s browser
  • browser takes the signed JWT and starts sending it with each HTTP request back to the server
  • signed JWT acts as a temporary user credential, that replaces the permanent credential (username and password)

Here’s what the server does upon receiving the JWT token:

  • the server checks the JWT signature and confirms that it’s valid
  • the Payload identifies a particular user via a user id
  • only the server has the secret password, and the server only gives out tokens to users that submit the correct password. Therefore, the server can be certain that this token was indeed given to this particular user by the server
  • the server proceeds with processing the HTTP request with this user’s credentials

Angular CLI with Bulma

Angular CLI is an awesome tool for Angular, and Bulma is a simple CSS framework that’s just a pure joy to work with.

Let’s start by generating a new project with Angular CLI (install it, in case you don’t have it already):

ng new jwt-auth

After this process is finished, run ng serve inside the jwt-auth folder, and you’ll have an app running at http://localhost:4200/:

Adding Bulma

Add this in the index.html file:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.0.0/js/all.js"></script>

Update app.component.html to:

<nav class="navbar">
  <div class="container">
    <div class="navbar-brand">
      <a class="navbar-item">
        JWT Angular Login
      </a>
    </div>

    <div id="navbarMenuHeroA" class="navbar-menu">
      <div class="navbar-end">
        <a class="navbar-item">
          Home
        </a>
        <a class="navbar-item">
          Login
        </a>
        <a class="navbar-item">
          Members
        </a>
        <a class="navbar-item">
          Logout
        </a>
      </div>
    </div>
  </div>
</nav>

<router-outlet></router-outlet>

<footer class="footer">
  <div class="container has-text-centered">
    <div class="content">
      From Croatia with ❤️
    </div>
  </div>
</footer>

If you take a look at the page now, you’ll see:

So, we have a header with links and a footer with simple text.

The <router-outlet></router-outlet> element will be used to serve other pages.

Now, let’s create three new components using Angular CLI:

ng g component home
ng g component login
ng g component members

One reason why Angular CLI is useful is that by generating the component, it creates 3 files for us and imports the component in the app.module.ts file:

create src/app/members/members.component.css (0 bytes)
create src/app/members/members.component.html (26 bytes)
create src/app/members/members.component.spec.ts (635 bytes)
create src/app/members/members.component.ts (273 bytes)
update src/app/app.module.ts (1124 bytes)

Now, let’s wire up the routes in app.module.ts:

const routes = [
    { path: 'login', component: LoginComponent },
    { path: 'members', component: MembersComponent },
    { path: '', component: HomeComponent },
    { path: '**', redirectTo: '' }
];

...
imports: [
    BrowserModule,
    RouterModule.forRoot(routes)
],

Set the links in the app.component.html using routerLink like this:

<a class="navbar-item" [routerLink]="['']">Home</a>
<a class="navbar-item" [routerLink]="['/login']">Login</a>
<a class="navbar-item" [routerLink]="['/members']">Members</a>

If all is fine, you should see this in your browser:

Login

Replace the contents of the login.component.html with:

<section class="hero">
  <div class="hero-body has-text-centered">
    <form [formGroup]="form">
      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Email</label>
            <div class="control">
              <input class="input" type="text" placeholder="[email protected]" formControlName="email" name="email">
            </div>
          </div>
        </div>

        <div class="column"></div>
      </div>

      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Password:</label>
            <div class="control">
              <input class="input" type="password" formControlName="password" name="password">
            </div>

            <br>
            <br>
            <a class="button is-primary is-medium is-fullwidth" (click)='login()'>Login</a>
          </div>
        </div>

        <div class="column"></div>
      </div>
    </form>
  </div>
</section>

and add login.component.ts with:

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
    form: FormGroup;

    constructor(private fb: FormBuilder) {
        this.form = this.fb.group({
            email: ['', Validators.required],
            password: ['', Validators.required]
        });
    }

    ngOnInit() {
    }

    login() {
        console.log('Clicked the Login button');
    }
}

You may notice that we used a bunch of imports from @angular/forms, so we also need to add it in app.module.ts in the imports array:

...
imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    RouterModule.forRoot(routes)
],
...

Before we go to the actual authentication section, let’s just fix the Home and Members area slightly.

Home and Members

Update the HTML files to the following content:

home.component.html:

<section class="hero" id="hero">
  <div class="hero-head"></div>
  <div class="hero-body">
    <div class="container has-text-centered">
      <h1 class="is-1 title">
        Welcome to JWT Angular Auth!
      </h1>
    </div>
  </div>
</section>

members.component.html:

<section class="hero" id="hero">
  <div class="hero-head"></div>
  <div class="hero-body">
    <div class="container has-text-centered">
      <h1 class="is-1 title">
        Members area
      </h1>
    </div>
  </div>
</section>

Go

Our backend is written in Golang, and it looks like this:

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
    "time"

    jwt "github.com/dgrijalva/jwt-go"
    "github.com/rs/cors"
)

const (
    PORT   = "1337"
    SECRET = "42isTheAnswer"
)

type JWTData struct {
    // Standard claims are the standard jwt claims from the IETF standard
    // https://tools.ietf.org/html/rfc7519
    jwt.StandardClaims
    CustomClaims map[string]string `json:"custom,omitempty"`
}

type Account struct {
    Email    string  `json:"email"`
    Balance  float64 `json:"balance"`
    Currency string  `json:"currency"`
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", hello)
    mux.HandleFunc("/login", login)
    mux.HandleFunc("/account", account)

    handler := cors.Default().Handler(mux)

    log.Println("Listening for connections on port: ", PORT)
    log.Fatal(http.ListenAndServe(":"+PORT, handler))
}

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello from Go!")
}

func login(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        log.Println(err)
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }

    var userData map[string]string
    json.Unmarshal(body, &userData)

    // Demo - in real case scenario you'd check this against your database
    if userData["email"] == "[email protected]" && userData["password"] == "admin123" {
        claims := JWTData{
            StandardClaims: jwt.StandardClaims{
                ExpiresAt: time.Now().Add(time.Hour).Unix(),
            },

            CustomClaims: map[string]string{
                "userid": "u1",
            },
        }

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        tokenString, err := token.SignedString([]byte(SECRET))
        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        json, err := json.Marshal(struct {
            Token string `json:"token"`
        }{
            tokenString,
        })

        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        w.Write(json)
    } else {
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }
}

func account(w http.ResponseWriter, r *http.Request) {
    authToken := r.Header.Get("Authorization")
    authArr := strings.Split(authToken, " ")

    if len(authArr) != 2 {
        log.Println("Authentication header is invalid: " + authToken)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    jwtToken := authArr[1]

    claims, err := jwt.ParseWithClaims(jwtToken, &JWTData{}, func(token *jwt.Token) (interface{}, error) {
        if jwt.SigningMethodHS256 != token.Method {
            return nil, errors.New("Invalid signing algorithm")
        }
        return []byte(SECRET), nil
    })

    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    data := claims.Claims.(*JWTData)

    userID := data.CustomClaims["userid"]

    // fetch some data based on the userID and then send that data back to the user in JSON format
    jsonData, err := getAccountData(userID)
    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    w.Write(jsonData)
}

func getAccountData(userID string) ([]byte, error) {
    output := Account{"[email protected]", 3.14, "BTC"}
    json, err := json.Marshal(output)
    if err != nil {
        return nil, err
    }

    return json, nil
}

⚠️ I’m not a Go expert (yet), so this code would be written way more idiomatic by someone who’s using the language longer. But, when confronted with such thoughts yourself, remember this: “Perfect is the enemy of good”, and it’s way better to learn by doing and getting stuff ‘out there’ and getting feedback, than to ‘wait x months until you master a language’.

So, here goes my best attempt at explaining what the code does, from top to bottom:

package

package main

First, we have the package statement. Every Go program must have a main package.

imports

import (
    "encoding/json"
    "errors"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
    "time"

    jwt "github.com/dgrijalva/jwt-go"
    "github.com/rs/cors"
)

Then we have the imports. All of the imports, except for jwt and cors are from the standard Go library. If you’re using an editor like VS Code, or an IDE like GoLand, then these imports are added automatically as you save your code.

One thing I love about Go is the auto code format, so finally, some language where there will be no debate about whether the brackets in ifs go on the same line or in the next. Consistency FTW!

constants

const (
    PORT   = "1337"
    SECRET = "42isTheAnswer"
)

Then we have two constants: PORT and SECRET. It is not a practice in Go to have all uppercase letters for constants, but I’m blindly sticking to that habit it seems.

structs

type JWTData struct {
    // Standard claims are the standard jwt claims from the IETF standard
    // https://tools.ietf.org/html/rfc7519
    jwt.StandardClaims
    CustomClaims map[string]string `json:"custom,omitempty"`
}

type Account struct {
    Email    string  `json:"email"`
    Balance  float64 `json:"balance"`
    Currency string  `json:"currency"`
}

Next, we have two structs: JWTData and Account. The JWTData struct, along with some standard fields (claims) has an additional CustomClaims map, that can hold key-value pairs of type string. We will use this data type to add our own custom claims (userid).

The Account struct is used as an example structure for responding to the logged in user once he’s logged in and comes to the Members page. It contains the Email, Balance and Currency fields.

main

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", hello)
    mux.HandleFunc("/login", login)
    mux.HandleFunc("/account", account)

    handler := cors.Default().Handler(mux)

    log.Println("Listening for connections on port: ", PORT)
    log.Fatal(http.ListenAndServe(":"+PORT, handler))
}

In the main function we ‘register’ the handlers for our API. If we presume that this Go program would be running on a domain http://api.boringcompany.com, then the request to that URL would be handled by the hello function that we’ll show below. If the request is sent to the http://api.boringcompany.com/login URL, it would be handled by the login function that we’ll show below, etc. Finally, we print the message via the log and start the server with the http.ListenAndServe function.

The CORS handler is necessary only when developing locally. If you’ll do that, then I also recommend the CORS plugin for the browser you’re using (here is the one that I use for Chrome).

hello handler

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello from Go!")
}

This is a simple function that outputs Hello from Go! back to the user when he hits our main API URL.

login handler

func login(w http.ResponseWriter, r *http.Request) {
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        log.Println(err)
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }

    var userData map[string]string
    json.Unmarshal(body, &userData)

    // Demo - in real case scenario you'd check this against your database
    if userData["email"] == "[email protected]" && userData["password"] == "admin123" {
        claims := JWTData{
            StandardClaims: jwt.StandardClaims{
                ExpiresAt: time.Now().Add(time.Hour).Unix(),
            },

            CustomClaims: map[string]string{
                "userid": "u1",
            },
        }

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        tokenString, err := token.SignedString([]byte(SECRET))
        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        json, err := json.Marshal(struct {
            Token string `json:"token"`
        }{
            tokenString,
        })

        if err != nil {
            log.Println(err)
            http.Error(w, "Login failed!", http.StatusUnauthorized)
        }

        w.Write(json)
    } else {
        http.Error(w, "Login failed!", http.StatusUnauthorized)
    }
}

In the login function we first read the body of the request and parse out the email and password parameters. We then check this email/password combination and make sure it’s correct. Of course, for demo purposes it was done like that in the code; in real case scenario, you’d check this against your database most probably.

If the user credentials are correct, we prepare the claims where we use the standard ExpiresAt claim, and also we add our own custom claim of userid with the value u1.

Next, we use the jwt.NewWithClaims function to sign the header and the payload with the HS256 hashing algorithm and we use the SECRET as a key for that. Finally, we then return this token to the user in JSON format.

Otherwise, if any errors happen, we send back the unauthorized status with a failure message.

account handler

func account(w http.ResponseWriter, r *http.Request) {
    authToken := r.Header.Get("Authorization")
    authArr := strings.Split(authToken, " ")

    if len(authArr) != 2 {
        log.Println("Authentication header is invalid: " + authToken)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    jwtToken := authArr[1]

    claims, err := jwt.ParseWithClaims(jwtToken, &JWTData{}, func(token *jwt.Token) (interface{}, error) {
        if jwt.SigningMethodHS256 != token.Method {
            return nil, errors.New("Invalid signing algorithm")
        }
        return []byte(SECRET), nil
    })

    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    data := claims.Claims.(*JWTData)

    userID := data.CustomClaims["userid"]

    // fetch some data based on the userID and then send that data back to the user in JSON format
    jsonData, err := getAccountData(userID)
    if err != nil {
        log.Println(err)
        http.Error(w, "Request failed!", http.StatusUnauthorized)
    }

    w.Write(jsonData)
}

func getAccountData(userID string) ([]byte, error) {
    output := Account{"[email protected]", 3.14, "BTC"}
    json, err := json.Marshal(output)
    if err != nil {
        return nil, err
    }

    return json, nil
}

In the account function we first read the Authorization header and take out the token. Then we make sure the token is valid and has not been tampered with, and we parse out the claims by using the jwt.ParseWithClaims function.

With the userID claim we fetch some data (using the getAccountData function) and then send that data back to the user in the JSON format.

Running the Go app

You can run this app locally on your computer with go run main.go. Of course, you need to have Go installed. You can check how to do that in this tutorial.

Finishing up the Angular frontend

Now let’s switch back to our Angular project and make actual requests to our API.

Auth service

Using Angular CLI, execute the following command in your terminal:

ng g service auth 

This now created two files for us:

create src/app/auth.service.spec.ts (362 bytes)
create src/app/auth.service.ts (110 bytes)

Copy the following code to the auth.service.ts file:

import { Injectable } from '@angular/core';
import { RequestOptions, Response } from '@angular/http';

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Router } from '@angular/router';

@Injectable()
export class AuthService {

    API_URL = 'http://localhost:1337';
    TOKEN_KEY = 'token';

    constructor(private http: HttpClient, private router: Router) { }

    get token() {
        return localStorage.getItem(this.TOKEN_KEY);
    }

    get isAuthenticated() {
        return !!localStorage.getItem(this.TOKEN_KEY);
    }

    logout() {
        localStorage.removeItem(this.TOKEN_KEY);
        this.router.navigateByUrl('/');
    }

    login(email: string, pass: string) {
        const headers = {
            headers: new HttpHeaders({ 'Content-Type': 'application/json', 'Cache-Control': 'no-cache' })
        };

        const data = {
            email: email,
            password: pass
        };

        this.http.post(this.API_URL + '/login', data, headers).subscribe(
            (res: any) => {
                localStorage.setItem(this.TOKEN_KEY, res.token);

                this.router.navigateByUrl('/members');
            }
        );
    }

    getAccount() {
        return this.http.get(this.API_URL + '/account');
    }
}

AuthService consists of these functions:

  • login – we send the email/password that the user enters to the server and upon success, we store the token in local storage. Please note the warning that I gave in the theory part of this tutorial.
  • logout – we delete the token from local storage and redirect the user to the landing page
  • token – returns the token from local storage
  • isAuthenticated – returns true/false if the token exists in the local storage
  • getAccount – requests the user data and returns a promise

login component

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from '../auth.service';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
    form: FormGroup;

    constructor(private fb: FormBuilder, private authService: AuthService) {
        this.form = this.fb.group({
            email: ['', Validators.required],
            password: ['', Validators.required]
        });
    }

    ngOnInit() {
    }

    login() {
        const val = this.form.value;

        if (val.email && val.password) {
            this.authService.login(val.email, val.password);
        }
    }
}

The most important part is the login function that calls the AuthService login function passing it email and password. We use the FormBuilder in Angular to access form fields that in the HTML code look like this:

<section class="hero">
  <div class="hero-body has-text-centered">
    <form [formGroup]="form">
      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Email</label>
            <div class="control">
              <input class="input" type="text" placeholder="[email protected]" formControlName="email" name="email">
            </div>
          </div>
        </div>

        <div class="column"></div>
      </div>

      <div class="columns">
        <div class="column"></div>

        <div class="column is-3">
          <div class="field">
            <label class="label is-pulled-left">Password:</label>
            <div class="control">
              <input class="input" type="password" formControlName="password" name="password">
            </div>

            <br>
            <br>
            <a class="button is-primary is-medium is-fullwidth" (click)='login()'>Login</a>
          </div>
        </div>

        <div class="column"></div>
      </div>
    </form>
  </div>
</section>

Notice <form [formGroup]="form"> and formControlName="email". In Angular we register the click handler like this: (click)='login()'.

members component

Members component is pretty simple:

<section class="hero" id="hero">
  <div class="hero-head"></div>
  <div class="hero-body">
    <div class="container has-text-centered">
      <h1 class="is-1 title">
        Members area
      </h1>

      <p>Email:
        <b>{{accountData?.email}}</b>
      </p>
      <p>Balance:
        <b>{{accountData?.balance}} {{accountData?.currency}}</b>
      </p>
    </div>
  </div>
</section>

We use it to show some data that we’ll get from the API. Very important part is the use of ? – this instructs Angular to not throw an error while it’s rendering the template in case the data doesn’t yet exist (as it will be the case since we’re fetching this data from an API).

The controller code looks like this:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
    selector: 'app-members',
    templateUrl: './members.component.html',
    styleUrls: ['./members.component.css']
})
export class MembersComponent implements OnInit {
    accountData: any;
    constructor(private authService: AuthService, private router: Router) { }

    ngOnInit() {
        this.authService.getAccount().subscribe(
            (res: any) => {
                this.accountData = res;
            }, (err: any) => {
                this.router.navigateByUrl('/login');
            }
        );
    }

}

When the component loads, we send a request to the API, and upon the success, we save the data in the accountData variable that we then use in the template as we saw previously. If an error occurs, we forward the user to the landing page.

app.component.html

<nav class="navbar">
  <div class="container">
    <div class="navbar-brand">
      <a class="navbar-item">
        JWT Angular Login
      </a>
    </div>

    <div id="navbarMenuHeroA" class="navbar-menu">
      <div class="navbar-end">
        <a class="navbar-item" [routerLink]="['']">
          Home
        </a>
        <a class="navbar-item" [routerLink]="['/login']" *ngIf="!authService.isAuthenticated">
          Login
        </a>
        <a class="navbar-item" [routerLink]="['/members']" *ngIf="authService.isAuthenticated">
          Members
        </a>
        <a class="navbar-item" *ngIf="authService.isAuthenticated" (click)="authService.logout()">
          Logout
        </a>
      </div>
    </div>
  </div>
</nav>

<router-outlet></router-outlet>

<footer class="footer">
  <div class="container has-text-centered">
    <div class="content">
      From Croatia with ❤️
    </div>
  </div>
</footer>

The important part to note here is the use of *ngIf="!authService.isAuthenticated" to show/hide the navigation links based on the fact if the user is logged in or not.

The only thing we need to do in the ‘code’ file is to make sure we import the AuthService via the constructor: constructor(private authService: AuthService) { }.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { MembersComponent } from './members/members.component';

import { AuthService } from './auth.service';
import { AuthInterceptorService } from './auth-interceptor.service';
import { CanActivateViaAuthGuard } from './can-activate-via-auth.guard';

const routes = [
    { path: 'login', component: LoginComponent },
    {
        path: 'members',
        component: MembersComponent,
        canActivate: [
            CanActivateViaAuthGuard
        ]
    },
    { path: '', component: HomeComponent },
    { path: '**', redirectTo: '' }
];

@NgModule({
    declarations: [
        AppComponent,
        HomeComponent,
        LoginComponent,
        MembersComponent
    ],
    imports: [
        BrowserModule,
        FormsModule,
        ReactiveFormsModule,
        HttpClientModule,
        RouterModule.forRoot(routes)
    ],
    providers: [
        AuthService,
        {
            provide: HTTP_INTERCEPTORS,
            useClass: AuthInterceptorService,
            multi: true
        },
        CanActivateViaAuthGuard
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

This file imports all the components that we’re using. As you can see, in the ‘declarations’ we list the components that we’re using, the imports contain imported components for working with forms or sending HTTP requests.

guards and interceptors

Finally, you may notice something new in the providers array, where with the usual AuthService we have two additional things defined (an interceptor service and an auth guard):

{
    provide: HTTP_INTERCEPTORS,
    useClass: AuthInterceptorService,
    multi: true
},
CanActivateViaAuthGuard

The interceptor service has one task: to intercept every request that goes from the app and add the token to that request in its header:

import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor } from '@angular/common/http';
import { AuthService } from './auth.service';

@Injectable()
export class AuthInterceptorService implements HttpInterceptor {

    constructor(private injector: Injector) { }

    intercept(req, next) {
        const authService = this.injector.get(AuthService);
        const authRequest = req.clone({
            // tslint:disable-next-line:max-line-length
            headers: req.headers.set('Authorization', 'Bearer ' + authService.token)
        });

        return next.handle(authRequest);
    }
}

THe guard is also simple and it’s defined like this:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';

@Injectable()
export class CanActivateViaAuthGuard implements CanActivate {
    constructor(private authService: AuthService) {

    }

    canActivate() {
        return this.authService.isAuthenticated;
    }
}

So, we define the so-called ‘canActivate’ guard which we use when we want to prevent the user from going to some route in the app to which he doesn’t have the access. We use this guard in the route definition to prevent the user from going to the /members link if he’s not logged in:

const routes = [
    { path: 'login', component: LoginComponent },
    {
        path: 'members',
        component: MembersComponent,
        canActivate: [
            CanActivateViaAuthGuard
        ]
    },
    { path: '', component: HomeComponent },
    { path: '**', redirectTo: '' }
];

When you have all of this wired up (and a Go program running locally), you should see:

Conclusion

We’ve covered a lot of ground in this long post:

  • we learned a bit about how JWTs work
  • we created our own JWT with code examples in JavaScript and Go
  • we made a full-fledged app with Angular 5 on the frontend and Go on the backend that uses JWTs for authentication
  • we made use of Angulars Guards and Interceptors

I hope this was enough to get you started and build upon this example.

If you have any questions, feel free to reach out in the comments.

#JWT authentication in an #Angular application with a #Go backend https://t.co/UpZgaOcJUb

— Nikola Brežnjak (@HitmanHR) February 19, 2018

Ionic3, JavaScript

How to create an Android Cordova plugin for showing Toast popups

TL;DR

In this post, I’m going to show you step by step how to build a Cordova plugin for Android that shows a native Toast popup.

You can check out the source code of the plugin here and the source of the demo Ionic app that’s using this plugin here.

In case you’re looking for the guide on how to write the Cordova plugin for the iOS platform, I wrote about it here.

plugin.xml

We start the process of plugin building by creating the plugin.xml file:

<?xml version='1.0' encoding='utf-8'?>
<plugin id="cordova-android-toast" version="1.0.0" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
    <name>AndroidToast</name>

    <description>Android Toast Plugin</description>
    <license>Apache 2.0</license>
    <keywords>android, toast</keywords>

    <engines>
      <engine name="cordova" version=">=3.0.0" />
    </engines>

    <js-module name="AndroidToast" src="www/AndroidToast.js">
        <clobbers target="AndroidToast" />
    </js-module>

    <platform name="android">
        <config-file target="config.xml" parent="/*">
            <feature name="AndroidToast">
                <param name="android-package" value="com.nikolabreznjak.AndroidToast" />
            </feature>
        </config-file>

        <source-file src="src/android/AndroidToast.java" target-dir="src/com/nikola-breznjak/android-toast" />
    </platform>
</plugin>

In this file you basically define:

  • the platform this plugin supports (<platform name="android">)
  • where the source files of your plugin will be (source-file elements)
  • where is the JavaScript file that will be the bridge from Cordova to native code (js-module tag src property)
  • what will be the plugin’s name by which you’ll reference it in the Cordova/Ionic code (<clobbers target="AndroidToast" />)

www/AndroidToast.js

Next comes the so-called ‘bridge’ file that connects the native and JavaScript side. It is common to put this file in the www folder. The contents of this file is as follows:

var exec = cordova.require('cordova/exec');

var AndroidToast = function() {
    console.log('AndroidToast instanced');
};

AndroidToast.prototype.show = function(msg, onSuccess, onError) {
    var errorCallback = function(obj) {
        onError(obj);
    };

    var successCallback = function(obj) {
        onSuccess(obj);
    };

    exec(successCallback, errorCallback, 'AndroidToast', 'show', [msg]);
};

if (typeof module != 'undefined' && module.exports) {
    module.exports = AndroidToast;
}

We created the AndroidToast function, which in other programming languages would basically be a class, because we added the show function on its prototype. The show function, via Cordova’s exec function, registers the success and error callbacks for the AndroidToast class and the show method on the native side that we’ll show now shortly. Also, we pass in the msg variable as an array to the native show function.

src/android/AndroidToast.java

The ‘native’ code is written in Java:

package com.nikolabreznjak;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
import android.content.Context;
import android.widget.Toast;

public class AndroidToast extends CordovaPlugin {
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if ("show".equals(action)) {
            show(args.getString(0), callbackContext);
            return true;
        }

        return false;
    }

    private void show(String msg, CallbackContext callbackContext) {
        if (msg == null || msg.length() == 0) {
            callbackContext.error("Empty message!");
        } else {
            Toast.makeText(webView.getContext(), msg, Toast.LENGTH_LONG).show();
            callbackContext.success(msg);
        }
    }
}

In the implementation file, we define our functions. We only have the show and execute functions in our example. When writing Cordova plugins for Android, every function from the bridge file has to call the exec function, which then calls the execute function on the native side. Then, based on the action parameter, we decide which function needs to be called. In our case, if we determine that the show action was called, we pass through the arguments to the private show function, which then uses the native Toast.makeText function to display the Toast message.

When displaying the Toast, our show method needs access to the app’s global Context, which can be obtained using the webView object that we get from extending CordovaPlugin. This represents the running Cordova app, and we can get the global Context from there using: webView.getContext(). Other parameters to the makeText function define the text that we want to show and the duration of how long we want to show it.

At this point you could customize the toast as much as the native Toast component allows, there are no restrictions even though this is used via Cordova as a kind of a ‘wrapper’. Some additional stuff that you could do is listed in the official documentation.

package.json

In earlier versions of Cordova, this file wasn’t required. You can generate it automatically with the help of the plugman package (install it with npm install plugman -g in case you don’t have it):

plugman createpackagejson /path/to/your/plugin.

If you’re in the plugin folder, then the command is: plugman createpackagejson .. The package.json file in my case now looks like this:

{
    "name": "cordova-android-toast",
    "version": "1.0.0",
    "description": "Android Toast Plugin",
    "cordova": {
        "id": "cordova-android-toast",
        "platforms": [
            "android"
        ]
    },
    "keywords": [
        "android",
        "toast",
        "ecosystem:cordova",
        "cordova-android"
    ],
    "engines": [{
        "name": "cordova",
        "version": ">=3.0.0"
    }],
    "author": "Nikola Brežnjak<[email protected]> (http://www.nikola-breznjak.com/blog)",
    "license": "Apache 2.0"
}

Using the plugin

First, you need to install it. If you’re using Ionic:

ionic cordova plugin add cordova-android-toast

If you’re using Cordova:

cordova plugin add cordova-android-toast

In the code, you would show the toast message like this:

constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
        platform.ready().then(() => {
            var androidToast = new AndroidToast();
            androidToast.show(
                'This is some nice toast popup!',
                function(msg) {
                    console.log(msg);
                },
                function(err) {
                    console.log(err);
                }
            );

        });
    }

If you’re using the newest (currently 3) version of Ionic, then you would have to add this line: declare var AndroidToast: any; after the imports in app.component.ts (or any other file where you’ll use the plugin) before actually using it. If you’re using Ionic 1, you don’t need to do this.

As a final note, make sure you’re accessing the plugin after the platform.ready() fires, just so you make sure that the plugin is ready for use.

Running the demo locally

Clone this repo:

git clone https://github.com/Hitman666/cordova-android-toast.git

CD into the cloned project

cd cordova-android-toast

Install the dependencies:

npm install && bower install

Add the Android platform (please note that this process may take a while to complete):

ionic cordova platform add android

Add the plugin:

ionic cordova plugin add cordova-android-toast

Run the project on the device (if you have it connected):

ionic cordova run android

Run the project on the emulator:

ionic cordova emulate android

You should see something like this once the app runs on your device:

Conclusion

I hope this post gave you enough information so that you’ll be dangerous enough to go and fiddle with the Android Cordova plugin building yourself ?

If you have any questions, feel free to reach out.

Ionic3

How to polish our existing Ionic3 calculator application

This is the third post in a series of posts which will teach you how to take advantage of your web development knowledge in building hybrid applications for iOS and Android. The first post in this series was all about How to get started with Ionic framework 3 on Windows and Mac, the second one was about How to create a calculator application with Ionic framework 3 by using Ionic Creator for UI.

In this post you’ll learn:

  • How to polish your existing calculator application
  • How to create icons and splash screen images automatically
  • How to implement Google AdMob ads
  • How to share your application with other users without going through the app stores
  • How to test your application on the real physical devices and emulators

So, if you’ve been following these series of posts then you probably already have the Calculator application ready and running on your machine in the web browser by using the ionic lab command.

If you just dropped by, or you would like to start from scratch, then you can clone the finished version of the 2nd tutorial from Github. After cloning, you have to run npm install to install all the dependencies. After this, the command ionic lab should run the application locally on your computer, and you should see it open up in your default browser. Of course, all this should work if you have Ionic properly installed. If not, please check out the instructions in the first tutorial.

At this point, we have a working simple calculator application that lacks some sanitization checks and design improvements. We’ll fix that in this section.

Sanitization check

In our current application, we don’t have a security measure against the possible malformed input. For example, one could enter two plus (+) signs one after another, which would consequently produce an error.

So, to handle this, we’re going to wrap our evaluation line of code in the try/catch block, and we’re going to show an alert to the user using the AlertController dialog (injected as a dependency in the CalculatorPage class constructor). The whole contents of the calculator.ts file should look like this now:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { AlertController } from 'ionic-angular';

@Component({
    selector: 'page-calculator',
    templateUrl: 'calculator.html'
})
export class CalculatorPage {
    constructor(public navCtrl: NavController, private alertCtrl: AlertController) { }

    result = '';

    btnClicked(btn) {
        if (btn == 'C') {
            this.result = '';
        }
        else if (btn == '=') {
            if (this.result == '') {
                return;
            }

            try {
                this.result = eval(this.result).toFixed(2);
            } catch (error) {
                this.showAlert();
                this.result = '';
            }
        }
        else {
            this.result += btn;
        }
    }

    showAlert() {
        this.alertCtrl.create({
            title: 'Malformed input',
            subTitle: 'Ooops, please try again...',
            buttons: ['Dismiss']
        }).present();
    }
}

Additionally, we’re checking to see if the result variable contains anything at the time the equals button is pressed, to avoid the undefined error that would otherwise happen. You can test this yourself by running the application clicking the equals button = and then on, for example, button 5, and you will see the text undefined5 appear in the Result area.

Also, you may have noticed that we added the toFixed(2) after our eval function call, to show the result formatted to two decimal places.

If you intentionally make an error while inputting the formula you would get this message:

Clearly, there are multiple ways you could approach this problem further to create a better user experience. One idea is to check on every button click if the current formula would execute correctly with eval and if not then immediately inform the user, or completely ignore the last clicked button. I encourage you to create your own way of the “improved UX” and share it in the comments.

Design changes

To be honest, our app currently doesn’t look quite representative. We’re going to change that to make it a bit nicer. If you ask any web designer these days, they will tell you that using pure CSS is, well, outdated. Ionic, as awesome as it is, has support for SASS out of the box so you can take advantage of the variables, nesting, mixins and all other great stuff that SASS provides.

For example, if you want to change the color you only need to change one variable, without having to trace it through all the files and change the particular color in all the places that it is used.

In the previous tutorial we added the ‘energized’ color to the colors array in the src/theme/variables.scss file. Now we’re going to do all of our changes in the pages/calculator/calculator.scss file, which, for now, looks like this:

page-calculator {

}

These changes will only apply to this page. If we’d like to add some styles that would apply to our whole app, we can do so in the src/app/app.scss file.

On the image below you can see what I came up with after changing few of the CSS rules, (and a slight addition to our HTML template that I’ll address additionally):

I’m sure that more design-inclined readers will come up with way more slick design than this and I encourage you to share your images/scss changes with the rest of us.

Now we’re going to go through the parts that were changed so that you can follow along in your example and see for yourself.

As for the changes, here is the final content of the src/pages/calculator/calculator.html file:

<ion-header>
    <ion-navbar>
        <ion-title>
            SuperSimple Calculator
        </ion-title>
    </ion-navbar>
</ion-header>

<ion-content>
    <div class="container">
        <form class="myInputRow">
            <ion-item>
                <ion-input type="text" placeholder="0" name="display" [(ngModel)]="result"></ion-input>
            </ion-item>
        </form>

        <div class="row">
            <button class="col2" ion-button color="danger" (click)="btnClicked('C')"> C </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('%')"> % </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('/')"> / </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('7')"> 7 </button>
            <button class="col" ion-button (click)="btnClicked('8')"> 8 </button>
            <button class="col" ion-button (click)="btnClicked('9')"> 9 </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('*')"> * </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('4')"> 4 </button>
            <button class="col" ion-button (click)="btnClicked('5')"> 5 </button>
            <button class="col" ion-button (click)="btnClicked('6')"> 6 </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('-')"> - </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('1')"> 1 </button>
            <button class="col" ion-button (click)="btnClicked('2')"> 2 </button>
            <button class="col" ion-button (click)="btnClicked('3')"> 3 </button>
            <button class="col" ion-button color="energized" (click)="btnClicked('+')"> + </button>
        </div>

        <div class="row">
            <button class="col" ion-button (click)="btnClicked('0')"> 0 </button>
            <button class="col" ion-button (click)="btnClicked('.')"> . </button>
            <button class="col2" ion-button color="danger" (click)="btnClicked('=')"> = </button>
        </div>
    </div>
</ion-content>

Here is the breakdown of the changes that I did:

  • changed the title to SuperSimple Calculator from just Calculator on the ion-title tag
  • removed all the id attributes
  • removed the ion-label in the form element
  • added the myInputRow class to the form element
  • added the row class to the div elements
  • added the col class to the button elements, except + and = buttons to which I’ve added the col2 class
  • wrapped everything in a div with the container class

In the scss/ionic.app.scss file I added the definitions for these new classes:

page-calculator {
    .container {
        display: flex;
        flex-direction: column;
        height: 100%;

        .row {
            flex: 2;
            flex-direction: row;
            padding: 0px !important;
            margin: 0px !important;

            .col {
                flex: 1;
                padding: 0px !important;
                margin: 0px !important;
            }

            .col2 {
                flex: 2;
                padding: 0px !important;
                margin: 0px !important;
            }
        }

        .myInputRow {
            flex: 1 !important;

            input {
                font-size: 40px;
                text-align: right;
            }
        }

        button {
            height: auto !important;
            border-radius: 0px !important;
            font-size: 32px;
            font-weight: bold;
            border-left: 1px solid #fff;
            border-bottom: 1px solid #fff;
        }
    }
}

Flexbox is used here as a basis for making the layout which fills the whole content horizontally. You can learn more about it from this Ionic specific tutorial, or you can learn more about Flexbox in general from this tutorial.

This way we now have an interface which fills the whole available content.

How to create icons and splash screen images automatically in Ionic framework 3

The icon is an important part of your application because it represents your application’s brand, and it helps to identify quickly where the app is on your phone. In case you’re familiar with creating apps then you will remember that it is a tedious process to create a lot of different size images both for iOS and Android platforms.

Also, the same goes for the so-called splash screen that shows up every time the application starts. Although having a splash screen is not mandatory, it certainly adds up to the feeling of a complete and professional application, which one would certainly want to convey with his application.

Ionic helps tremendously with this by providing a single Ionic CLI command to generate all the needed icon and splash screen sizes for us automatically. Also, Ionic created Photoshop Splash Screen Template, which you can download for free and use as a guideline for creating an icon. However, if you create you project by using ionic start command, as we have, then you’ll already have both the icon.png and splash.png files in the resources folder, and you can just edit them.

For when you’re creating a branded product having a custom made icon is definitely a must. However, in this case, I’ll show you how to use one of the free services to search for a free icon which you’ll then use in your application (even if your application is a commercial application).

I tend to use IconFinder a lot, and here are the settings which you have to use to filter out the calculator images that are Free (PRICE) and can be used in commercial applications and that don’t even require a link back (LICENSE TYPE).

Of course, you can also choose to buy an image if you happen to find one that you like. You can additionally search by format, size, and background. The filters should look like this (use this prepared link which sets them automatically):

I’m going to use the last one in the first row. Simply click on it, and you should get to the download page that looks like this:

To download it just click on the green ‘Download PNG’ button.

Now, in the resources folder find and open the icon.png file with an image editor of your choice. I’m going to use Gimp in this example as it’s free cross-platform image editor available for all the major operating systems (Linux, OS X, Windows). You should see something like this:

Now you need to do a few steps:

  • select the whole area (Ctrl + a or Command + a) and press the DEL button on your keyboard. This should leave you with a blank white canvas.
  • drag the calculator icon on this white canvas and you should see something like this:
  • click on the scale tool
  • then click on the calculator image. You should get this popup:
  • change the values in the popup to 1024px for both Width and Height and click the Scale button.
  • click on the Alignment Tool, then click on the calculator image and then on the circled two buttons on the image below. This will align the calculator image horizontally and vertically

Now save the file by going to File->Overwrite icon.png:

Repeat the process to create the splash screen image and name it (overwrite it as) splash.png. The image I came up with looks like this:

Now that you have both icon.png and splash.png images ready, navigate with your Terminal/Command prompt to the root folder of the application and run the following command:

ionic cordova resources

You should get the following output:

✔ Collecting resource configuration and source images - done!
✔ Filtering out image resources that do not need regeneration - done!
✔ Uploading source images to prepare for transformations - done!
✔ Generating platform resources: 48 / 48 complete - done!
✔ Modifying config.xml to add new image resources - done!

If, instead, at this point, you get an error like this:

[ERROR] No platforms have been added. Please run: ionic cordova platform add

That means that you haven’t added any platforms yet to which Ionic should build. Since we’re going to build the app for both Apple Store and Android Play Store we’re going to use the following two commands:

ionic cordova platform add android
ionic cordova platform add ios

You should see an output similar to this:

ionic cordova platform add ios
> cordova platform add ios --save
✔ Running command - done!
Using cordova-fetch for cordova-ios@~4.4.0
Adding ios project...
Creating Cordova project for the iOS platform:
    Path: platforms/ios
    Package: io.ionic.starter
    Name: MyApp
iOS project created with [email protected]
Discovered plugin "cordova-plugin-console" in config.xml. Adding it to the project
Installing "cordova-plugin-console" for ios
Adding cordova-plugin-console to package.json
Saved plugin info for "cordova-plugin-console" to config.xml
Discovered plugin "cordova-plugin-device" in config.xml. Adding it to the project
Installing "cordova-plugin-device" for ios
Adding cordova-plugin-device to package.json
Saved plugin info for "cordova-plugin-device" to config.xml
Discovered plugin "cordova-plugin-splashscreen" in config.xml. Adding it to the project
Installing "cordova-plugin-splashscreen" for ios
Adding cordova-plugin-splashscreen to package.json
Saved plugin info for "cordova-plugin-splashscreen" to config.xml
Discovered plugin "cordova-plugin-statusbar" in config.xml. Adding it to the project
Installing "cordova-plugin-statusbar" for ios
Adding cordova-plugin-statusbar to package.json
Saved plugin info for "cordova-plugin-statusbar" to config.xml
Discovered plugin "cordova-plugin-whitelist" in config.xml. Adding it to the project
Installing "cordova-plugin-whitelist" for ios
Adding cordova-plugin-whitelist to package.json
Saved plugin info for "cordova-plugin-whitelist" to config.xml
Discovered plugin "ionic-plugin-keyboard" in config.xml. Adding it to the project
Installing "ionic-plugin-keyboard" for ios
Adding ionic-plugin-keyboard to package.json
Saved plugin info for "ionic-plugin-keyboard" to config.xml
--save flag or autosave detected
Saving ios@~4.4.0 into config.xml file ...
✔ Copying default image resources into ./resources/ios - done!

> cordova platform add android --save
✔ Running command - done!
Using cordova-fetch for cordova-android@~6.2.2
Adding android project...
Creating Cordova project for the Android platform:
    Path: platforms/android
    Package: io.ionic.starter
    Name: MyApp
    Activity: MainActivity
    Android target: android-25
Subproject Path: CordovaLib
Android project created with [email protected]
Installing "cordova-plugin-console" for android
Installing "cordova-plugin-device" for android
Installing "cordova-plugin-splashscreen" for android
Installing "cordova-plugin-statusbar" for android
Installing "cordova-plugin-whitelist" for android

               This plugin is only applicable for versions of cordova-android greater than 4.0. If you have a previous platform version, you do *not* need this plugin since the whitelist will be built in.

Installing "ionic-plugin-keyboard" for android
--save flag or autosave detected
Saving android@~6.2.3 into config.xml file ...
✔ Copying default image resources into ./resources/android - done!

After this, the ionic cordova resources command should work.

From the output, you can see that 48 images were created and hopefully now you realize how much time this saved. All the needed configuration regarding the icons and splash screens was generated by Ionic and placed in the config.xml file.

It’s worth noting that you will not see the icon nor the splash screen when using the browser testing or Ionic View testing (discussed in more detail in the How to use Ionic.io cloud service to share our application with other users without going through the app store section below). Instead, you will only see these once you deploy them to the actual physical device or the emulator (which we’ll cover in the How to test our application on the real physical devices and emulators section below).

⚠️ You can add an iOS platform if you’re developing on a Windows machine, and ionic cordova resources command will generate icons and splash screens for it. However, keep in mind that you will not be able to build the project for iOS on your Windows machine. Instead, you’ll need a Mac computer to do so. We’ll cover building the app in more detail in the How to test our application on the real physical devices and emulators section below.

How to implement Google AdMob ads

There are multiple ways you can earn money with your app these days and here are just a few:

  • Paid app – set a price for your app directly on the App/Play Store that users need to pay before downloading your app
  • Freemium – give the app for free but charge for in-app purchases like adding some extra features (think more gold or faster production in game apps)
  • Ad-based – show ads inside your application. Potentially offer the in-app purchase to remove the ads

Here we’re going to cover the Ad-based monetization option where I’ll show you how to add Google AdMob ads to our calculator application. There are two parts to implementing Google AdMob ads to an Ionic project, and I broke them into AdMob settings and Ionic settings.

AdMob settings

Let’s start with AdMob settings:

  1. Sign in/Sign up for AdMob at https://www.google.com/admob/
  2. Click the Apps and then ADD APP button:

  1. Since our app is not published yet we will click the No button:

  1. Fill in the app name and platform and click the ADD button:

  1. Save the App ID somewhere and proceed to create the Ad unit

  1. Select Banner Ad format:

  1. Configure the adds type, size, placement, style, name:

  1. You can read additional info on how to implement GA and AdMob, but for now, let’s just click Done:

  1. You will now see the following similar screen:

The most important thing to note here is this Ad unit ID, which in my test case is ca-app-pub-7957971173858308/5068937357. Make a note of this string as it’s the most important part of this setting. You can click on the copy to clipboard button and paste it as a comment (for now) in your app.

  1. Create as much Ad units as you may need (for each platform[iOS, Android] and ad format [Banner, Interstitial, etc.]). In my case, I just created the additional Interstitial Ad and will use them on both iOS and Android devices for this demo.

Ionic settings

Those of you familiar with Ionic 1 know that you can add any plugin to your Ionic project thanks to the project called ngCordova. For Ionic 3, there’s the same thing called Ionic Native.

Ionic Native is a TypeScript wrapper for Cordova/PhoneGap plugins that makes it easy to add any native functionality that you may need into your Ionic app. Ionic Native wraps the plugin callbacks in a Promise or an Observable, providing a common interface for all plugins and ensuring that native events trigger change detection in Angular.

Navigate to the root of the application with your Terminal/Command prompt and execute the following command to add the cordova-plugin-admobpro plugin:

ionic cordova plugin add cordova-plugin-admobpro

You should see the following output after running the command:

> cordova plugin add cordova-plugin-admobpro --save
✔ Running command - done!
Installing "cordova-plugin-admobpro" for android
Installing "cordova-plugin-extension" for android
Subproject Path: CordovaLib
Installing "cordova-plugin-admobpro" for ios
Plugin dependency "[email protected]" already fetched, using that version.
Installing "cordova-plugin-extension" for ios
Adding cordova-plugin-admobpro to package.json
Saved plugin info for "cordova-plugin-admobpro" to config.xml

Additionally, you also need to run this command:

npm install --save @ionic-native/admob-pro

which, if completed successfully, will only output something like added 1 package in 4.443s to the console.

You need to add this second command as well because this installs some files needed by TypeScript.

⚠️ At this point, depending on the version of Ionic CLI that you have you may need to add a platform by executing: ionic cordova platform add ios or ionic cordova platform add android depending on the platform you’re trying to build for. You have to execute that if the command ionic cordova platform ls shows that you don’t have any installed platforms on the current project:

→ ionic cordova platform ls
✔ cordova platform ls - done!
Installed platforms:
Available platforms: 
 android ~6.2.2
 blackberry10 ~3.8.0 (deprecated)
 browser ~4.1.0
 ios 4.4.0
 osx ~4.0.1
 webos ~3.7.0

If everything is fine with running ionic cordova platform add ios you will see an output like this:

Using cordova-fetch for cordova-ios@~4.4.0
Adding ios project...
Creating Cordova project for the iOS platform:
    Path: platforms/ios
    Package: io.ionic.starter
    Name: MyApp
iOS project created with [email protected]
Installing "cordova-plugin-admobpro" for ios
Installing "cordova-plugin-extension" for ios
Discovered plugin "cordova-plugin-console" in config.xml. Adding it to > the project
Installing "cordova-plugin-console" for ios
Adding cordova-plugin-console to package.json
Saved plugin info for "cordova-plugin-console" to config.xml
Discovered plugin "cordova-plugin-device" in config.xml. Adding it to > the project
Installing "cordova-plugin-device" for ios
Adding cordova-plugin-device to package.json
Saved plugin info for "cordova-plugin-device" to config.xml
Discovered plugin "cordova-plugin-splashscreen" in config.xml. Adding > it to the project
Installing "cordova-plugin-splashscreen" for ios
Adding cordova-plugin-splashscreen to package.json
Saved plugin info for "cordova-plugin-splashscreen" to config.xml
Discovered plugin "cordova-plugin-statusbar" in config.xml. Adding it > to the project
Installing "cordova-plugin-statusbar" for ios
Adding cordova-plugin-statusbar to package.json
Saved plugin info for "cordova-plugin-statusbar" to config.xml
Discovered plugin "cordova-plugin-whitelist" in config.xml. Adding it > to the project
Installing "cordova-plugin-whitelist" for ios
Adding cordova-plugin-whitelist to package.json
Saved plugin info for "cordova-plugin-whitelist" to config.xml
Discovered plugin "ionic-plugin-keyboard" in config.xml. Adding it to > the project
Installing "ionic-plugin-keyboard" for ios
Adding ionic-plugin-keyboard to package.json
Saved plugin info for "ionic-plugin-keyboard" to config.xml
--save flag or autosave detected
Saving ios@~4.4.0 into config.xml file ...
✔ Copying default image resources into ./resources/ios - done!

Btw, there is also a free version of the AdMob Pro plugin. But, honestly, if you really start making money with your app, this will be a minor expense. Besides, if you’re having problems giving back to the actual plugin through which you’re making money then my dear padawan you have yet much to learn… ?

Now, let’s add this plugin to our app’s NgModule. In the src/app/app.module.ts file import AdmobPro:

import { AdMobPro } from '@ionic-native/admob-pro';

and then add it to the Providers array. The whole contents of the src/app/app.module.ts file should now looks like this:

import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { MyApp } from './app.component';
import { CalculatorPage } from '../pages/calculator/calculator';


import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { AdMobPro } from '@ionic-native/admob-pro';

@NgModule({
    declarations: [
        MyApp,
        CalculatorPage
    ],
    imports: [
        BrowserModule,
        IonicModule.forRoot(MyApp)
    ],
    bootstrap: [IonicApp],
    entryComponents: [
        MyApp,
        CalculatorPage
    ],
    providers: [
        StatusBar,
        SplashScreen,
        AdMobPro,
        { provide: ErrorHandler, useClass: IonicErrorHandler }
    ]
})
export class AppModule { }

Now I’m going to show you the final content of the src/pages/calculator/calculator.ts file and will explain what was changed step by step:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { AlertController } from 'ionic-angular';

import { AdMobPro } from '@ionic-native/admob-pro';
import { Platform } from 'ionic-angular';

@Component({
    selector: 'page-calculator',
    templateUrl: 'calculator.html'
})
export class CalculatorPage {
    constructor(public navCtrl: NavController, private alertCtrl: AlertController, private admob: AdMobPro, private platform: Platform) {
        this.platform.ready().then(() => {
            var admobid = {
                banner: 'ca-app-pub-7957971173858308/5068937357',
                interstitial: 'ca-app-pub-7957971173858308/5667703151'
            };

            this.admob.createBanner({
                adId: admobid.banner,
                isTesting: true,
                autoShow: true,
                position: this.admob.AD_POSITION.BOTTOM_CENTER
            })

            this.admob.prepareInterstitial({
                adId: admobid.interstitial,
                isTesting: true,
                autoShow: false
            })
        });
    }

    result = '';
    counter = 1;

    showInterstitialAd() {
        if (AdMobPro) {
            this.admob.showInterstitial();
        }
    }

    btnClicked(btn) {
        if (btn == 'C') {
            this.result = '';
        }
        else if (btn == '=') {
            if (this.result == '') {
                return;
            }

            try {
                this.result = eval(this.result).toFixed(2);

                if (this.counter++ == 5) {
                    this.showInterstitialAd();
                    this.counter = 0;
                }
            } catch (error) {
                this.showAlert();
                this.result = '';
            }
        }
        else {
            this.result += btn;
        }
    }

    showAlert() {
        this.alertCtrl.create({
            title: 'Malformed input',
            subTitle: 'Ooops, please try again...',
            buttons: ['Dismiss']
        }).present();
    }
}

First, we added the imports:

import { AdMobPro } from '@ionic-native/admob-pro';
import { Platform } from 'ionic-angular';

Then, via the constructor we pulled in the Platform and AdMobPro:

constructor(public navCtrl: NavController, private alertCtrl: AlertController, private admob: AdMobPro, private platform: Platform) { }

Then we wrapped everything in the platform.ready() promise. This is the most important part of the code! If you wouldn’t do that, it could happen that your app would start up and the plugins would still not be properly set up, and you wouldn’t see the ads displayed.

But then again, sometimes you would, and this is what it would make it a nightmare to debug. This is a very common issue that I’ve seen even back from Ionic 1 when answering the questions on StackOverflow. So, you may want to keep an ?️ on the fact that you need to wrap any plugin calls inside the platform.ready() promise, as that way you’ll be sure that all of the plugins have loaded before you’ll use them.

The code that’s executed after the promise resolves sets up our admobid object with banner and interstitial properties. Then we’re calling the createBanner and prepareInterstitial functions on the injected admob object. Note that the banner is set to show automatically when the app loads (autoShow: true) and the interstitial isn’t. Also, note that we’ve set the position of the banner ad to the bottom:

this.platform.ready().then(() => {
    var admobid = {
        banner: 'ca-app-pub-7957971173858308/5068937357',
        interstitial: 'ca-app-pub-7957971173858308/5667703151'
    };

    this.admob.createBanner({
        adId: admobid.banner,
        isTesting: true,
        autoShow: true,
        position: this.admob.AD_POSITION.BOTTOM_CENTER
    })

    this.admob.prepareInterstitial({
        adId: admobid.interstitial,
        isTesting: true,
        autoShow: false
    })
});

Then we added the showshowInterstitialAdAd function, which shows the Interstitial ad:

showInterstitialAd() {
    if (AdMobPro) {
        this.admob.showInterstitial();
    }
}

Of course, at this point, change the adId variable to your admob_key which you obtained in the first part (step 9). Also, when you’ll be publishing your app, don’t forget to change the isTesting variable to false. Don’t worry, I’ll remind you when we get to that part.

? If you run into any problems with this, just ping in the comments, and I’ll do my best to help you.

One common thing that you might have to do for Android is to install some extras via the Android SDK manager. To do so open Android Studio and select Configure->SDK Manager:

Make sure you have installed the packages marked as Installed on the image below (usually, those are Google Billing Library and Google Play services):

For more information about the Android SDK manager, take a look at the section about How to test our application on the real physical devices and emulators futher in the post.

What kind of an ad should you show?

This plugin’s documentation states an interesting fact that it’s strongly recommended to use the Interstitial ad type because it brings more than 10 times profit than the banner Ad. Here’s the table from the official documentation:

Ad Format Banner Interstitial
Click Rate < 1% 3-15%
RPM (1k impressions) 0.5$ – 4$ 10-50$

Banner ad is the small add that is usually placed in the bottom of the screen, whereas Interstitial ads are full-screen ads that cover the interface of their host app. Therefore, you may rather want to opt for this kind of an ad instead for the Banner one.

It’s important to note that there’s probably no exact formula here on when to show the Interstitial Ad, but there are some best practices, and this is what Google has to say about it:

Interstitial ads work best in apps with natural transition points. The conclusion of a task within an app, like sharing an image or completing a game level, creates such a point. Because the user is expecting a break in the action, it’s easy to present an interstitial without disrupting their experience. Make sure you consider at which points in your app’s workflow you’ll display interstitials, and how the user is likely to respond.

You can learn a bit more about it here.

Anyways, as you saw in my demo code above, I’ve opted for showing the banner ad all the time in the bottom of the screen, and I’m showing the interstitial ad every 5th time the user clicks the = button. You can tweak this any way you like in your app, but please remember the points mentioned above about best practices.

How to use Ionic.io cloud service to share our application with other users without going through the app store

Having the ability to immediately get feedback on something that you’re working on, without having to install the app manually on your client’s devices or waiting for the App Store/Play Store approval is indispensable for constant feedback loop which is crucial in rapid application development. This is something that Eric Ries stresses a lot in his book The Lean Startup.

In Ionic, this is easier than you may have thought. All you have to do is create the account on https://apps.ionic.io and after that in your Terminal/Command prompt in the root of the project just type the following command:

ionic upload

This will prompt you to login with your username and password combination which you created in the previous step. After successful login, the app will be uploaded to the Ionic.io cloud service and you will be able to view the app through an app called Ionic View, which you can download for free on your smartphone from the App Store/Play Store.

The output of the above command is short:

[INFO] Running app-scripts build: 

[09:55:39]  build dev started ... 
[09:55:39]  clean started ... 
[09:55:39]  clean finished in 2 ms 
[09:55:39]  copy started ... 
[09:55:39]  transpile started ... 
[09:55:42]  transpile finished in 2.30 s 
[09:55:42]  preprocess started ... 
[09:55:42]  deeplinks started ... 
[09:55:42]  deeplinks finished in 4 ms 
[09:55:42]  preprocess finished in 5 ms 
[09:55:42]  webpack started ... 
[09:55:42]  copy finished in 2.44 s 
[09:55:47]  webpack finished in 5.10 s 
[09:55:47]  sass started ... 
[09:55:48]  sass finished in 1.39 s 
[09:55:48]  postprocess started ... 
[09:55:48]  postprocess finished in 4 ms 
[09:55:48]  lint started ... 
[09:55:48]  build dev finished in 8.84 s 
> ionic cordova prepare
> cordova prepare
✔ Running command - done!

[09:55:50]  lint finished in 1.87 s 
✔ Requesting snapshot upload - done!
✔ Uploading snapshot - done!
[OK] Uploaded snapshot 19a5c87e-92aa-4757-b2c0-1c24e7ee9657!

And, as stated in the output, to share your app with a client or a friend, without first having to go through the App Store/Play Store, go to https://apps.ionic.io and add the testers email under the Settings->Collaborators section:

User will receive an email which will guide him through the setup:

⚠️ Those of you coming from Ionic 1 and being used to the ionic share EMAIL command, you’ll get the following error message if you run it:
ionic share [email protected]

[ERROR] ionic share has been removed as of CLI 3.0.
The functionality now exists in the Ionic Dashboard: https://apps.ionic.io

If you’ll get an error like this:

[ERROR] Your project file (./ionic.config.json) does not contain ‘app_id’. Run ionic link.

Then execute the command ionic link and choose the Create new app option in the terminal. That will bring you to the web interface where you have to enter the name of your app and click the Create App button:

Once the app is created you’ll see something like this:

Now go back to your Terminal and run ionic link again and choose the name your app. You should see an output similar to this:

✔ Looking up your apps - done!
? Which app would you like to link SuperSimpleCalculator (dec5230f)
> ionic config set app_id dec5230f
[OK] app_id set to dec5230f in ./ionic.config.json!
[OK] Project linked with app dec5230f!

Now you can rerun the ionic upload command and get an output that the snapshot of your app has been uploaded.

Once your client/tester/friend gets the email for being a Collaborator, he will be guided through setting up Ionic View and viewing your app.

However, there’s one more way for your client/friend to try out the app. First, tell them to download the Ionic View app from the App Store/Play Store:

They will have to sign up (it’s free):

And once they’re logged in, they have to click the PREVIEW A SHARED APP button:

At this point, they will have to enter your App Id which you’ll have to send them (Slack, Email, Messenger, choose your ?). You can find your id in the Ionic dashboard. In my case the id is dec523f:

They have to enter this id in the form and click the LOAD APP button:

They will get the following screen with instructions on how to use the app:

You will notice that we will not see ads in this version because Ionic View does not support the AdMob plugin:

Suffice to say; you dear reader can also try this out via your own Ionic View app.

How to test our application on the real physical devices and emulators

If you run the application (to which you added AdMob) in the browser, you will see few notices in your browser’s Console output that AdMob is not defined:

Also, if you view the app in Ionic View, you will notice that no ads will show up. This is because Cordova plugins don’t work in the browser, nor in the Ionic View. Instead, they have to be tested on the real device or in an emulator (in iOS terminology the word simulator is used). Just for reference, there are some plugins that work in the Ionic View.

In this section, we’re going to show how to install the needed prerequisites and how to run our application in an simulator/emulator and on the actual physical device for both iOS and Android.

iOS settings

The main tool for developing native iOS applications is Xcode. Xcode is praise-worthily free for download, and it comes with a lot of simulators in which you can see how your app would look like on a real device.

However, if you want to build and deploy iOS applications to the App Store, you need to have a Mac computer since Xcode only works on their operating system. Yes, even if you’re using Ionic, you need to use Xcode to build the application for iOS.

There are some ways around this, like for example with using a so-called Hackintosh computer, but honestly, from my experience, this is just not worth it.

If you’re really anti-Apple ?, then you’ll be happy to know that there are services which allow you to rent a Mac in the cloud, just for the time you need to build your application. Also, Ionic offers the so-called Package service that allows you to build mobile apps for any supported platform even if you don’t have a Mac. We won’t cover that here, but you can take a look at the official Package service documentation.

To publish an app in the App Store (or test it on your own iOS device) you’ll need to purchase the Apple Developer Program license which costs US $99 per year. You’ll need to sign up at http://developer.apple.com, but don’t worry about this for now; we’ll cover all the steps in the next tutorial where I’ll guide you through the process of deploying the application for both the Apple’s App Store and Google’s Play Store.

Installing the needed tools

As we’ve mentioned before, you need to install Xcode, and you can do that by opening up the App Store application on your Mac and search for Xcode. You install it like any other application by clicking on the download button and following the NextNextNext type of installation.

After the installation is finished, open up Xcode, and navigate to Xcode -> Preferences -> Downloads tab. Here you can download the latest version of the Simulator available. In my case, at the time of this writing, the latest version is iOS 11.0 Simulator, as you can see on the image below:

Also, we need to install the ios-sim package with npm:

npm install -g ios-sim

Running our application in a simulator

Now, to test our application in a simulator all we have to do is execute the following command:

ionic cordova emulate ios

After a bunch of cryptic messages you should see a Xcode simulator open up and show your awesome application along with a splash screen and ads:

⚠️ In case you get an error like this:

** BUILD SUCCEEDED **
Error: Cannot read property 'replace' of undefined
[ERROR] An error occurred while running cordova emulate ios (exit code 1).

Then execute the following command (don’t use sudo if on Windows):

cd platforms/ios/cordova/node_modules/ && sudo npm install ios-sim@latest

After this repeat the emulate command and all should be well.

Of course, run the ionic cordova emulate ios command in the root directory of the project, not in the /platforms/ios/cordova/node_modules folder!

If you test the app by doing five calculations, then after the fifth time you press the equals button = the Interstitial ad will appear, as shown in the image below:

If you exit the simulator back to the home screen (by pressing `Command (⌘) + Shift + H) you will see the application’s icon:

When you’re testing your application in a browser with ionic lab you have that great live reload feature. You can have that too in the simulator if you run the simulator with the extra -l flag. If you also want to see the console.log output, as you would see it in the browser, then you have to add the –c flag:

ionic cordova emulate ios -lc

Since we need to have an Apple Developer account to run the application on our physical phone device, we will leave this for our next tutorial where we’ll also show how to deploy the application to the actual App Store.

⚠️ At the time of writing this you can see in the updated screenshots of the iOS 11 simulator, the app isn’t iOS 11 ready. However, anyone that will be following this tutorial anew and will have ionic and cordova npm packages up to date, will not have this problem, as the Ionic team fixed everything in time for the iPhone X release. For more information, see the official blog post. For those of you who are maybe looking to update your Ionic 1 app to look nice on iPhone X, take a look at this tutorial.

Android settings

There are no prerequisites for Android development regarding the operating systems; you can develop for Android on Mac, Linux, and Windows computers.

Android provides some tools for developers that are available for free from their website.

? To publish an app in the Play Store you’ll need to purchase the Developer Program license which costs only US $25 one-time fee. You’ll need to sign up at https://play.google.com/apps/publish/signup/, but as said before – don’t worry; we’ll cover all the steps in the next tutorial.

Installing the needed tools

Download Android Studio from https://developer.android.com/studio/index.html

The installation is a simple NextNextNext type of installation, but if you get stuck, they have great official documentation.

Next, you need to set up the so-called PATH variable to point to the Android SDK tools. You can find the path by opening up the Android Studio, select the Configure->SDK Manager option and copy the Android SDK Location:

On Mac, you can set the PATH variable like this:

export PATH=$PATH:/Users/nikola/Library/Android/sdk/tools/.

You may want to put this line to your .bash_profile (or any related that you may have like .zshrc) file. For any further details about how to set the PATH variable, you can take a look at this tutorial.

As for Windows, the command would be:

set PATH=%PATH%;C:\Dev\android-sdk\tools

⚠️ To be honest, you may still encounter some errors while trying to get this to work and I’ve answered quite a few issues on StackOverflow. So, if you get stuck at this point, just drop me a line in the comments, and I’ll do my best to help.

Once you’ve set the PATH variable correctly for your operating system, you can check if it’s OK by opening up your Terminal/Command prompt and typing:

android

You should get an output similar to this one:

android
*************************************************************************
The "android" command is deprecated.
For manual SDK, AVD, and project management, please use Android Studio.
For command-line tools, use tools/bin/sdkmanager and tools/bin/avdmanager
*************************************************************************
Invalid or unsupported command ""

Supported commands are:
android list target
android list avd
android list device
android create avd
android move avd
android delete avd
android list sdk
android update sdk

You may get a message that you don’t have java tools installed:

In that case, click the More Info... button and you’ll be directed to their website where you have to click on the DOWNLOAD button (make sure you click the JDK one): Then, on the next screen, click the Accept License Agreement and select your operating system:

After the download run the .dmg file (on Mac OS) and finish the NextNextNext type of installation. If you’re on Windows, make appropriate choices for your OS.

Finally, you need to set up the SDK packages. For those of you who are in this field a bit longer, you’ll remember that you had to do everything via the Terminal/Command prompt. Specifically, for this you had to enter:

android sdk

But, with the new version of the Android Studio, as you saw in the previous Terminal output, you don’t have to do that. The way you should open up the SDK Manager is by opening up Android Studio and then click on Configure->SDK Manager:

I recommend that you download just the most recent packages as shown on the image below. Most probably you’ll have everything installed except Google Play services and Google Play Billing Library.

Make sure that you download only the platforms that you’ll need. On the image below, only the Android 7.1.1 (Nougat) version is installed:

Now run ionic cordova build android, and open the folder platforms/android/ with Android Studio (click the Open an existing Android Studio project link/button) and you should see something like this:

Create a new emulator

You need to have an emulator on which you’ll run your Android app. To begin the process of adding it, click on the Tools->Android->AVD Manager:

Click the Create Virtual Device button:

Choose a device:

Select the system image and click the Next button. If need be, first click on the Download link.

On the final screen just click the Finish button and you should see the following screen which shows the summary of your virtual devices:

Run the app on the new emulator

Run ionic cordova emulate android and you should see an Android emulator start up and show you your app:

Run the app on the physical device

If you would like to run the application on your own Android device then you first have to run the following command to build the project:

ionic cordova build android

The last few lines of the output will be something like:

BUILD SUCCESSFUL

Total time: 1.557 secs
Built the following apk(s): 
    /Users/nikola/DEV/Ionic3/Ionic3_3rdTutorial/platforms/android/build/outputs/apk/android-debug.apk

To be able to test your application on your Android device, you have to enable the developer settings, and you can do that in few steps:

  • Open the Settings view and scroll to About Phone
  • At the bottom of the About Phone view find a Build Number and tap on it seven times to enable the developer mode.
  • After this you can go back to the Settings view, and you’ll see a new option called Developer Options

Now you have to enable the USB debugging, and you can do that in the next few steps:

  • Select Developer Options item in the Settings view
  • Scroll down until you see the USB debugging option
  • Toggle it ON
  • Now your device is set up for debugging, and when it’s connected to your computer you’ll be able to deploy the application to the device

Now connect your device and execute the following command in your Termina/Command prompt:

ionic cordova run android

Find the application in your application list and open it. You should see the ads at the bottom, and after the fifth calculation, you should see an Interstitial ad appear.

⚠️ If for some reason the ionic cordova run android command wouldn’t work for you, you can always just copy the generated .apk file manually to your connected device (for example in the Downloads folder). After that open the Downloads folder on your phone with the file manager application that you use and open the .apk file that you pasted in. This way you’ll be prompted to install the application on your phone and then you’ll be able to find it in the programs list:

Conclusion

In this tutorial, you’ve learned quite a few things. You learned how to polish the existing calculator application by improving the design and user experience. Next, you learned how to create icons and splash screen images automatically. Then, how to make money with your application by using Google AdMob ads. Also, how to share your application with other users without going through the app stores. Finally, you learned how to test your application on the real physical devices and emulators.

In the next tutorial, I’m going to show you how to prepare the application for the Apple’s App Store and Google’s Play Store and finally, how to publish the application to both Apple’s App Store and Google’s Play Store.

Until next time, keep on coding ✌️

How to polish our existing @ionicframework 3 calculator application https://t.co/uBmiwsyHps

— Nikola Brežnjak (@HitmanHR) December 15, 2017

Ionic

How to make Ionic 1 app look good on iPhone X

In this post, I’ll show you how to make your Ionic 1 app look good on iPhone X. The instructions below assume you have the old Ionic CLI (1.x version) and not the new one (3.x version).

TL;DR

  • Add the new viewport meta tag
  • Update the status bar plugin
  • Update the splash screen plugin
  • Update Ionic to latest 1.x version
  • Build the app in Xcode 9
  • Add the new app icon

!TL;DR

Add the new viewport meta tag

Add the new viewport meta tag to your index.html file like this:

<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

Update the status bar plugin

Simply remove the plugin and add it back again, but using the GitHub repo (as it may not yet be merged), like this:

ionic plugin rm cordova-plugin-statusbar
ionic plugin add https://github.com/apache/cordova-plugin-statusbar.git

Update the splash screen plugin

Same as with the status bar plugin, you can do the following:

ionic plugin rm cordova-plugin-splashscreen
ionic plugin add cordova-plugin-splashscreen

Update Ionic to the latest 1.x version

Update Ionic to the latest 1.x version (1.3.5 as of this writing) with the following command:

bower install ionic-team/ionic-bower#1.3.5

You will be prompted to install some additional packages, and for those packages, you’ll have to choose the version of Angular you want to use with them. When asked, make sure to choose 1.5.3 (at the time of writing this).

Build the app in Xcode 9

Update Xcode to the newest version (9.1 currently). Execute the ionic prepare ios command, and open up the platforms/ios/MyApp.xcodeproj file using Xcode. If you have a platforms/ios/MyApp.xcworkspace file in there as well, open that one instead.

⚠️ Now, here’s a potentially tricky part. Say that you had Xcode 8 till recently and you updated everything as outlined above. Then, just before updating to Xcode 9, you have to do ionic prepare ios and open the project in Xcode and update to the new Swift syntax (in case you haven’t done this before). Then, when you open it up in Xcode 9, you’ll be able to update to Swift syntax 4. Again, this may only be a potential problem, but I’m stating it here since I ran into it. The problem is that Xcode 9 can’t update from Swift 2 to 4 syntax out of the box.

Add the new app icon

Finally, please note that for new submissions to the Apple Store, you’ll need to provide the new 1024×1024 px icon. You can add this image to your Image assets:

or you can upload it manually to iTunes when submitting a new version of your app.

Hope this helps someone ?

How to make #Ionic 1 app look good on #iPhone X https://t.co/sIh0dwexZW

— Nikola Brežnjak (@HitmanHR) November 22, 2017

Ionic

How to add PayPal to Ionic 1 apps

TL;DR

In this tutorial, I’m going to show you how to add the option of paying with PayPal to your Ionic 1 apps.

The demo project is on Github.

How to create this yourself step by step

  • Start a new Ionic 1 project

ionic start IonicPayPalDemo

  • Add the PayPal Cordova Plugin

cordova plugin add com.paypal.cordova.mobilesdk

As the official docs say:

The PayPal SDK Cordova/Phonegap Plugin adds 2 JavaScript files to your project:

  • cdv-plugin-paypal-mobile-sdk.js – a wrapper around the native SDK. The PayPalMobile object is immediately available to use in your .js files. You don’t need to reference it in index.html
  • paypal-mobile-js-helper.js – a helper file which defines the PayPalPayment, PayPalPaymentDetails and PayPalConfiguration classes for use with PayPalMobile

You must add <script type="text/javascript" src="js/paypal-mobile-js-helper.js"></script> to your www/index.html file, after the cordova.js import.

So, following the official advice, our www/index.html file will now look like this:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>

    <link rel="manifest" href="manifest.json">
    <link href="lib/ionic/css/ionic.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">

    <script src="lib/ionic/js/ionic.bundle.js"></script>
    <script src="cordova.js"></script>
    <script type="text/javascript" src="js/paypal-mobile-js-helper.js"></script>

    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
    <script src="js/services.js"></script>
</head>

<body ng-app="starter">
    <ion-nav-bar class="bar-stable">
        <ion-nav-back-button>
        </ion-nav-back-button>
    </ion-nav-bar>
    <ion-nav-view></ion-nav-view>
</body>
</html>

Next, set the contents of the www/templates/tab-dash.html to:

<ion-view view-title="Dashboard">
    <ion-content class="padding">
        <div class="list">
            <label class="item item-input">
                Price:  <input type="text" ng-model="subscriptionPrice"/>
            </label>
            <label class="item item-input">
                Subscription:  <input type="text" ng-model="subscriptionName"/>
            </label>
        </div>

        <button class="button button-positive button-block" ng-click="payWithPayPal()">
            <i class="icon ion-card"></i>
            Pay with PayPal
        </button>
    </ion-content>
</ion-view>

Set the contents DashCtrl controller in the www/js/controllers.js file to:

.controller('DashCtrl', function($scope, PaypalFactory) {
    $scope.subscriptionName = 'Some subscription';
    $scope.subscriptionPrice = 5;

    $scope.payWithPayPal = function() {
        PaypalFactory.initPaymentUI().then(function() {
            PaypalFactory.makePayment($scope.subscriptionPrice, $scope.subscriptionName).then(function(data) {
                console.dir(data);

                //make some additional logic based on returned data like saving to your database, showing a message to the user, etc.
                navigator.notification.alert(
                    "PayPal purchase completed successfully.",
                    null,
                    "Paypal Purchase",
                    "OK"
                );
            }, function(err) {
                console.dir(err);
                navigator.notification.alert(
                    err,
                    null,
                    "Paypal Purchase Canceled",
                    "Try Again"
                );
            });
        });
    };
})

You can see that we’re importing the PaypalFactory through dependency injection. We define this factory in js/services.js file like this:

.factory('PaypalFactory', ['$q', '$filter', '$timeout', '$ionicPlatform', 'APP_CONSTS', function($q, $filter, $timeout, $ionicPlatform, APP_CONSTS) {
        var init_defer;

        var that = {};

        /**
        * @ngdoc method
        * @name initPaymentUI
        * @methodOf app.PaypalFactory
        * @description
        * Inits the payapl ui with certain envs.
        * 
        * @returns {object} Promise paypal ui init done
        */
        that.initPaymentUI = function() {
            init_defer = $q.defer();
            $ionicPlatform.ready().then(function() {

                var clientIDs = {
                    "PayPalEnvironmentProduction": APP_CONSTS.payPalProductionId,
                    "PayPalEnvironmentSandbox": APP_CONSTS.payPalSandboxId
                };
                PayPalMobile.init(clientIDs, that.onPayPalMobileInit);
            });

            return init_defer.promise;
        }

        /**
        * @ngdoc method
        * @name createPayment
        * @methodOf app.PaypalFactory
        * @param {string|number} total total sum. Pattern 12.23
        * @param {string} name name of the item in paypal
        * @description
        * Creates a paypal payment object 
        *
        * @returns {object} PayPalPaymentObject
        */
        var createPayment = function(total, name) {
            // "Sale  == >  immediate payment
            // "Auth" for payment authorization only, to be captured separately at a later time.
            // "Order" for taking an order, with authorization and capture to be done separately at a later time.
            var payment = new PayPalPayment("" + total, "USD", "" + name, "Sale");
            return payment;
        }

        /**
        * @ngdoc method
        * @name configuration
        * @methodOf app.PaypalFactory
        * @description
        * Helper to create a paypal configuration object
        *
        * 
        * @returns {object} PayPal configuration
        */
        var configuration = function() {
            // for more options see `paypal-mobile-js-helper.js`
            var config = new PayPalConfiguration({ merchantName: APP_CONSTS.payPalShopName, merchantPrivacyPolicyURL: APP_CONSTS.payPalMerchantPrivacyPolicyURL, merchantUserAgreementURL: APP_CONSTS.payPalMerchantUserAgreementURL });
            return config;
        }

        that.onPayPalMobileInit = function() {
            $ionicPlatform.ready().then(function() {
                PayPalMobile.prepareToRender(APP_CONSTS.payPalEnv, configuration(), function() {
                    $timeout(function() {
                        init_defer.resolve();
                    });
                });
            });
        }

        /**
        * @ngdoc method
        * @name makePayment
        * @methodOf app.PaypalFactory
        * @param {string|number} total total sum. Pattern 12.23
        * @param {string} name name of the item in paypal
        * @description
        * Performs a paypal single payment 
        *
        * 
        * @returns {object} Promise gets resolved on successful payment, rejected on error 
        */
        that.makePayment = function(total, name) {
            var defer = $q.defer();
            total = $filter('number')(total, 2);
            $ionicPlatform.ready().then(function() {
                PayPalMobile.renderSinglePaymentUI(createPayment(total, name), function(result) {
                    $timeout(function() {
                        defer.resolve(result);
                    });
                }, function(error) {
                    $timeout(function() {
                        defer.reject(error);
                    });
                });
            });

            return defer.promise;
        }

        /**
        * @ngdoc method
        * @name makeFuturePayment
        * @methodOf app.PaypalFactory
        * @description
        * Performs a paypal future payment 
        * 
        * @returns {object} Promise gets resolved on successful payment, rejected on error 
        */
        that.makeFuturePayment = function(total, name) {
            var defer = $q.defer();
            $ionicPlatform.ready().then(function() {
                PayPalMobile.renderFuturePaymentUI(
                    function(authorization) {
                        defer.resolve(authorization);
                    },
                    function(cancelReason) {
                        defer.reject(cancelReason);
                    });
            });

            return defer.promise;
        }

        return that;
    }])

Also, you may notice that here we’re importing APP_CONSTS constant which we have to define in app.js file like this:

.constant('APP_CONSTS', {
    payPalSandboxId: 'Bfiudw1_kauwqxV8vqsPXyWv-6rbudyhnwbKd2Qhb57Rdwoj0NLT8dOGwOYug5g-vHL28aqVWLMkErdVop',
    payPalProductionId: '',
    payPalEnv: 'PayPalEnvironmentSandbox', // for testing: PayPalEnvironmentSandbox, for production PayPalEnvironmentProduction
    payPalShopName: 'Demo Shop',
    payPalMerchantPrivacyPolicyURL: 'https://www.demoshop.com/privacy',
    payPalMerchantUserAgreementURL: 'https://www.demoshop.com/terms'
  })

Of course, these won’t work and for the demo to work you need to change them to your settings. You can get these by registering your application over at PayPal Developers portal.

Running the application

When you run the application you should see a screen like this:

Then, if you click on the Pay with PayPal button you’ll get a nice looking interface:

Cool thing is that it also supports the option to take a picture of your credit card and automatically fill the fields (in case you would pay with a credit card):

Conclusion

Hope this demo app helps to show you how easy it is to add PayPal payments to your Ionic 1 app.

The demo only showcases the usage of a one-time payment. The so-called future payment UI is ready, but to get this fully working, you will need to implement proper functions on the backend as well. You can read more about this here.

How to add PayPal to Ionic 1 apps @ionicframework https://t.co/nwLE2vUxAu

— Nikola Brežnjak (@HitmanHR) October 17, 2017

Ionic

Cordova Ionic Plugin for Search Ads App Attribution API

TL;DR

Cordova plugin for reading Search Ads App Attribution on iOS 10.0+ only with an Ionic demo showcasing its usage.

The Search Ads API plugin for Cordova/Ionic didn’t exist, so I created it. You can check out the plugin code on Github, view the Ionic demo app code on Github or read onward for an example on how to use it.

You can also check out the step by step tutorial about How to create a native iOS app that can read Search Ads Attribution API information if you’re not into hybrid solutions ?

What is Search Ads App Attribution?

From Apple’s documentation:

Search Ads App Attribution enables developers to track and attribute app downloads that originate from Search Ads campaigns. With Search Ads App Attribution, iOS developers have the ability to accurately measure the lifetime value of newly acquired users and the effectiveness of their advertising campaigns.

How to use the plugin

If you want to test this on a blank project, you can create a new Ionic project like this:

ionic start ionicSearchAdsDemo tabs

Since the plugin has been added to npm repository, you can simply add it like this:

ionic plugin add cordova-plugin-searchads

Add the following code in the controllers.js file under the DashCtrl controller:

.controller('DashCtrl', function($scope, $ionicPlatform) {
    $scope.data = 'no data';

    $ionicPlatform.ready(function() {
        if (typeof SearchAds !== "undefined") {
            searchAds = new SearchAds();

            searchAds.initialize(function(attribution) {
                console.dir(attribution); // do something with this attribution (send to your server for further processing)
                $scope.data = JSON.stringify(attribution);
            }, function (err) {
                console.dir(err);
            });
        }
    });
})

Replace the content of the templates/tab-dash.html file with this:

<ion-view view-title="Dashboard">
  <ion-content class="padding">
    <div class="card">
      <div class="item item-text-wrap">
        {{data}}
      </div>
    </div>
  </ion-content>
</ion-view>

Prepare the project by executing the following command in your terminal:

ionic prepare ios && open platforms/ios

After this, open up the XCode project (*.xcodeproj) file:

Make sure the iAd.framework has been added to Linked Frameworks and Libraries:

This should have happened automatically when you’ve added the plugin, but it’s good to make sure. Add it yourself if you don’t see it here.

Run the project on your device, and you should get something like this:

Hope this proves to be useful to someone! ?

#Cordova #Ionic Plugin for Search Ads App Attribution API https://t.co/D4yMevINOf

— Nikola Brežnjak (@HitmanHR) September 14, 2017

Ionic

How to use deep linking in Ionic 1 apps with Ionic Native Deeplinks plugin

In this tutorial, I’m going to show you how to use deep linking in Ionic 1 apps with Ionic Native Deeplinks plugin that’s originally made for Ionic 2 and Ionic 3. Hopefully, I’ll save you some time so that you won’t have to figure this out on your own. I’ll note where the official documentation is lacking in terms of setting the correct link when passing additional parameters.

So, let’s get started!

Demo project

You can check out the code for this project on Github.

Here’s the app in action, where you’ll notice how by clicking the link you are taken to the app to a specific screen. Also, additional nesting (opening up the detail screen of one ‘chat’) also works:

Step by step

Here are the steps you can take to get to the same final working project like the one I’ve posted on Github.

Start a new Ionic 1 project based on tabs template:

ionic start IonicDeeplinkTest tabs

Add the deeplinks plugin:

ionic plugin add ionic-plugin-deeplinks --variable URL_SCHEME=nikola --variable DEEPLINK_SCHEME=http --variable DEEPLINK_HOST=nikola-breznjak.com --save

Few things to note here are:

  • URL_SCHEME – a string which you’ll put in your links so that once clicked your phone’s operating system will know to open your app. In my case, the links will look like nikola://something
  • DEEPLINK_SCHEME – most probably you’ll want to put https here, but I’ve put http because my website doesn’t (yet) have SSL support ?
  • DEEPLINK_HOST – you should put your domain here on which you’ll put the link

The output of the command above should be something like this:

Fetching plugin "ionic-plugin-deeplinks" via npm

Installing "ionic-plugin-deeplinks" for ios

Installing dependency packages: 

{
  "mkpath": ">=1.0.0",
  "xml2js": ">=0.4",
  "node-version-compare": ">=1.0.1",
  "plist": ">=1.2.0"
}

Now, as mentioned in the official plugin docs, this plugin is originally provided through Ionic Native for Ionic 2+ apps. But, we can use Ionic Native with Ionic 1 if we install it like this (official docs on this subject):

npm install ionic-native --save

Now, copy ionic.native.min.js from node_modules/ionic-native/dist folder into a new lib/ionic-native folder.

Then, in index.html add:

<script src="lib/ionic-native/ionic.native.min.js"></script>

just before the

<script src="cordova.js"></script>

line.

Next, run the following command:

npm install --save @ionic-native/deeplinks

Finally, open up the app.js file and add the following code inside the platform.ready callback:

$cordovaDeeplinks.route({
    '/chats/:chatId': {
        target: 'tab.chat-detail',
        parent: 'tab.chats'
    },
    '/account': {
        target: 'tab.account',
        parent: 'tab.account'
    },
    '/chats': {
        target: 'tab.chats',
        parent: 'tab.chats'
    }
}).subscribe(function(match) {
    $timeout(function() {
        $state.go(match.$route.parent, match.$args);

        if (match.$route.target != match.$route.parent) {
            $timeout(function() {
                $state.go(match.$route.target, {chatId: match.$args.chatId});
            }, 800);
        }
    }, 100); // Timeouts can be tweaked to customize the feel of the deeplink
}, function(nomatch) {
    console.warn('No match', nomatch);
});

Also, don’t forget to add ionic.native to the angular.module function in the app.js file:

angular.module('starter', ['ionic', 'starter.controllers', 'starter.services', 'ionic.native'])

and inject $cordovaDeeplinks in the .run function.

Just for reference, the full contents of the app.js file should now look like this:

// Ionic Starter App

// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
// 'starter.services' is found in services.js
// 'starter.controllers' is found in controllers.js
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services', 'ionic.native'])

.run(function($ionicPlatform, $cordovaDeeplinks, $timeout, $state) {
    $ionicPlatform.ready(function() {
        // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
        // for form inputs)
        if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
            cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
            cordova.plugins.Keyboard.disableScroll(true);

        }
        if (window.StatusBar) {
            // org.apache.cordova.statusbar required
            StatusBar.styleDefault();
        }

        $cordovaDeeplinks.route({
            '/chats/:chatId': {
                target: 'tab.chat-detail',
                parent: 'tab.chats'
            },
            '/account': {
                target: 'tab.account',
                parent: 'tab.account'
            },
            '/chats': {
                target: 'tab.chats',
                parent: 'tab.chats'
            }
        }).subscribe(function(match) {
            console.log('matching');
            console.dir(match);
            $timeout(function() {
                $state.go(match.$route.parent, match.$args);

                if (match.$route.target != match.$route.parent) {
                    $timeout(function() {
                        $state.go(match.$route.target, {chatId: match.$args.chatId});
                    }, 800);
                }
            }, 100); // Timeouts can be tweaked to customize the feel of the deeplink
        }, function(nomatch) {
            console.warn('No match', nomatch);
            console.dir(nomatch);
        });
    });
})

.config(function($stateProvider, $urlRouterProvider) {

    // Ionic uses AngularUI Router which uses the concept of states
    // Learn more here: https://github.com/angular-ui/ui-router
    // Set up the various states which the app can be in.
    // Each state's controller can be found in controllers.js
    $stateProvider

    // setup an abstract state for the tabs directive
        .state('tab', {
        url: '/tab',
        abstract: true,
        templateUrl: 'templates/tabs.html'
    })

    // Each tab has its own nav history stack:

    .state('tab.dash', {
        url: '/dash',
        views: {
            'tab-dash': {
                templateUrl: 'templates/tab-dash.html',
                controller: 'DashCtrl'
            }
        }
    })

    .state('tab.chats', {
            url: '/chats',
            views: {
                'tab-chats': {
                    templateUrl: 'templates/tab-chats.html',
                    controller: 'ChatsCtrl'
                }
            }
        })
        .state('tab.chat-detail', {
            params: {chatId: 0},
            url: '/chats/:chatId',
            views: {
                'tab-chats': {
                    templateUrl: 'templates/chat-detail.html',
                    controller: 'ChatDetailCtrl'
                }
            }
        })

    .state('tab.account', {
        url: '/account',
        views: {
            'tab-account': {
                templateUrl: 'templates/tab-account.html',
                controller: 'AccountCtrl'
            }
        }
    });

    // if none of the above states are matched, use this as the fallback
    $urlRouterProvider.otherwise('/tab/dash');
});

A few example links that you should put on your website look like this:

<a href="nikola://account">Open Account</a>
<a href="nikola://chats">Open Chats</a>
<a href="nikola://app/chats/4">Open chat detail 4</a>

I created a simple demo page so you can test this if you’d like and it’s here. Just open this link on your phone, after you’ve run the demo app on your phone and you should see the links working – meaning; taking you to proper sections in the app.

Just for reference, the contents of that file is this:

<!DOCTYPE html>
<html>
<head>
    <title>Deeplinking test</title>

    <style type="text/css">
        a {
            font-size: 80px;
            margin: 40px;
            display: block;
        }

        body {
            text-align: center;
        }
    </style>
</head>
<body>
    <a href="nikola://account">Open Account</a>

    <a href="nikola://chats">Open Chats</a>

    <a href="nikola://app/chats/4">Open chat detail 4</a>
</body>
</html>

The most important part here, on which I’ve wasted most of the time is the nikola://app/chats/4 part. Namely, at first I expected that you only should write it as nikola://chats/4, but by finding the bug report about this in the official repo I realized that you have to put something as a suffix (I’ve put app here).

Now, to run the app on your device, first prepare it:

ionic prepare ios

Then, open the platforms/ios/IonicDeeplinkTest.xcodeproj file:

And, make sure the string nikola (of course, change this string to your use case – usually the name of the app) is set as shown on the image:

For any potential additional info about setting up Xcode, check the official blog post.

Here’s the app in action, where you’ll notice how by clicking the link you are taken to the app to a specific screen. Also, additional nesting (opening up the detail screen of one ‘chat’) also works by passing in the argument.

Conclusion

I hope this was helpful and that it saved you some time in trying to figure this out.

In case you’re wondering how to do this for Ionic 2, 3+ apps, then it’s even easier as shown in the official docs.

How to use deep linking in Ionic 1 apps with Ionic Native Deeplinks plugin https://t.co/dAtt4Fwv3o

— Nikola Brežnjak (@HitmanHR) August 25, 2017

Ionic3

How to create icons and splash screen images automatically in Ionic framework 3

⚠️ In case you’re looking for how to create icons and splash screen images automatically in Ionic framework version 1, then you can check out this post. For more Ionic framework 3 posts check out the step by step info on How to get started with Ionic framework 3 on Windows and Mac.

TL;DR

Just execute ionic cordova resources command in your Ionic 3 project root folder.

!TL;DR

The icon is an important part of your application because it represents your application’s brand, and it helps to identify quickly where the app is on your phone. In case you’re familiar with creating apps then you will remember that it is a tedious process to create a lot of different size images both for iOS and Android platforms.

Also, the same goes for the so-called splash screen that shows up every time the application starts. Although having a splash screen is not mandatory, it certainly adds up to the feeling of a complete and professional application, which one would certainly want to convey with his application.

Ionic helps tremendously with this by providing a single Ionic CLI command to generate all the needed icon and splash screen sizes for us automatically. Also, Ionic created Photoshop Splash Screen Template, which you can download for free and use as a guideline for creating an icon. However, if you create you project by using ionic start command, as we have, then you’ll already have both the icon.png and splash.png files in the resources folder, and you can just edit them.

For when you’re creating a branded product having a custom made icon is definitely a must. However, in this case, I’ll show you how to use one of the free services to search for a free icon which you can then use in your application (even if your application is a commercial application).

I tend to use IconFinder a lot, and here are the settings which you have to use to filter out the calculator images that are Free (PRICE) and can be used in commercial applications and that don’t even require a link back (LICENSE TYPE).

Of course, you can also choose to buy an image if you happen to find one that you like. You can additionally search by format, size, and background. The filters should look like this (or use this prepared link which sets them automatically):

I’m going to use the last one in the first row. Simply click on it, and you should get to the download page that looks like this:

To download it just click on the green ‘Download PNG’ button.

Now, in the resources folder find and open the icon.png file with an image editor of your choice. I’m going to use Gimp in this example as it’s free cross-platform image editor available for all the major operating systems (Linux, OS X, Windows). You should see something like this:

Now we need to do a few steps:

  • select the whole area (Ctrl + a or Command + a) and press the DEL button on your keyboard. This should leave you with a blank white canvas.
  • drag the calculator icon on this white canvas and you should see something like this:
  • click on the scale tool
  • then click on the calculator image. You should get this popup:
  • change the values in the popup to 1024px for both Width and Height and click the Scale button.
  • click on the Alignment Tool, then click on the calculator image and then on the circled two buttons on the image below. This will align the calculator image horizontally and vertically

Now save the file by going to File->Overwrite icon.png:

Repeat the process to create the splash screen image and name it (overwrite it as) splash.png. The image I came up with looks like this:

Now that you have both icon.png and splash.png images ready navigate with your Terminal/Command prompt to the root folder of the application and run the following command:

ionic cordova resources

If at this point you get an error like this:

[ERROR] No platforms have been added. Please run: ionic cordova platform add

That means that you haven’t added any platforms yet to which Ionic should build. Since we’re going to build the app for both Apple Store and Android Play Store we’re going to use the following two commands:

ionic cordova platform add android
ionic cordova platform add ios

You should see an output similar to this:

ionic cordova platform add ios
> cordova platform add ios --save
✔ Running command - done!
Using cordova-fetch for cordova-ios@~4.4.0
Adding ios project...
Creating Cordova project for the iOS platform:
    Path: platforms/ios
    Package: io.ionic.starter
    Name: MyApp
iOS project created with [email protected]
Discovered plugin "cordova-plugin-console" in config.xml. Adding it to the project
Installing "cordova-plugin-console" for ios
Adding cordova-plugin-console to package.json
Saved plugin info for "cordova-plugin-console" to config.xml
Discovered plugin "cordova-plugin-device" in config.xml. Adding it to the project
Installing "cordova-plugin-device" for ios
Adding cordova-plugin-device to package.json
Saved plugin info for "cordova-plugin-device" to config.xml
Discovered plugin "cordova-plugin-splashscreen" in config.xml. Adding it to the project
Installing "cordova-plugin-splashscreen" for ios
Adding cordova-plugin-splashscreen to package.json
Saved plugin info for "cordova-plugin-splashscreen" to config.xml
Discovered plugin "cordova-plugin-statusbar" in config.xml. Adding it to the project
Installing "cordova-plugin-statusbar" for ios
Adding cordova-plugin-statusbar to package.json
Saved plugin info for "cordova-plugin-statusbar" to config.xml
Discovered plugin "cordova-plugin-whitelist" in config.xml. Adding it to the project
Installing "cordova-plugin-whitelist" for ios
Adding cordova-plugin-whitelist to package.json
Saved plugin info for "cordova-plugin-whitelist" to config.xml
Discovered plugin "ionic-plugin-keyboard" in config.xml. Adding it to the project
Installing "ionic-plugin-keyboard" for ios
Adding ionic-plugin-keyboard to package.json
Saved plugin info for "ionic-plugin-keyboard" to config.xml
--save flag or autosave detected
Saving ios@~4.4.0 into config.xml file ...
✔ Copying default image resources into ./resources/ios - done!

> cordova platform add android --save
✔ Running command - done!
Using cordova-fetch for cordova-android@~6.2.2
Adding android project...
Creating Cordova project for the Android platform:
    Path: platforms/android
    Package: io.ionic.starter
    Name: MyApp
    Activity: MainActivity
    Android target: android-25
Subproject Path: CordovaLib
Android project created with [email protected]
Installing "cordova-plugin-console" for android
Installing "cordova-plugin-device" for android
Installing "cordova-plugin-splashscreen" for android
Installing "cordova-plugin-statusbar" for android
Installing "cordova-plugin-whitelist" for android

               This plugin is only applicable for versions of cordova-android greater than 4.0. If you have a previous platform version, you do *not* need this plugin since the whitelist will be built in.

Installing "ionic-plugin-keyboard" for android
--save flag or autosave detected
Saving android@~6.2.3 into config.xml file ...
✔ Copying default image resources into ./resources/android - done!

After this, you can safely run the ionic cordova resources command, and you should get the following output:

✔ Collecting resource configuration and source images - done!
✔ Filtering out image resources that do not need regeneration - done!
✔ Uploading source images to prepare for transformations - done!
✔ Generating platform resources: 48 / 48 complete - done!
✔ Modifying config.xml to add new image resources - done!

From the output, you can see that 48 images were created and hopefully now you realize how much time this saved. All the needed configuration regarding the icons and splash screens was generated by Ionic and placed in the config.xml file.

It’s worth noting that you will not see the icon nor the splash screen when using the browser testing or Ionic View testing. Instead, you will only see these once you deploy them to the actual physical device or the emulator.

⚠️ You can add an iOS platform if you’re developing on a Windows machine, and ionic cordova resources command will generate icons and splash screens for it. However, keep in mind that you will not be able to build the project for iOS on your Windows machine. Instead, you’ll need a Mac computer to do so.

How to create icons and splash screen images automatically in Ionic framework 3 @ionicframework https://t.co/EKe3QDeo8E

— Nikola Brežnjak (@HitmanHR) August 23, 2017

Page 2 of 13«1234»10...Last »

Recent posts

  • Discipline is also a talent
  • Play for the fun of it
  • The importance of failing
  • A fresh start
  • Perseverance

Categories

  • Android (3)
  • Books (114)
    • Programming (22)
  • CodeProject (35)
  • Daily Thoughts (77)
  • Go (3)
  • iOS (5)
  • JavaScript (127)
    • Angular (4)
    • Angular 2 (3)
    • Ionic (61)
    • Ionic2 (2)
    • Ionic3 (8)
    • MEAN (3)
    • NodeJS (27)
    • Phaser (1)
    • React (1)
    • Three.js (1)
    • Vue.js (2)
  • Leadership (1)
  • Meetups (8)
  • Miscellaneou$ (77)
    • Breaking News (8)
    • CodeSchool (2)
    • Hacker Games (3)
    • Pluralsight (7)
    • Projects (2)
    • Sublime Text (2)
  • PHP (6)
  • Quick tips (40)
  • Servers (8)
    • Heroku (1)
    • Linux (3)
  • Stack Overflow (81)
  • Unity3D (9)
  • Windows (8)
    • C# (2)
    • WPF (3)
  • Wordpress (2)

"There's no short-term solution for a long-term result." ~ Greg Plitt

"Everything around you that you call life was made up by people that were no smarter than you." ~ S. Jobs

"Hard work beats talent when talent doesn't work hard." ~ Tim Notke

© since 2016 - Nikola Brežnjak