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

Using Puppeteer to automate ASC Analytics screenshots

TL;DR

I was trying to automate my manual process of logging into ASC (App Store Connect) and taking a screenshot of the crashes, and preparing a report that I do weekly. This blog post explains the exact steps of how I achieved that.

Why Puppeteer?

I'm not affiliated with it in any way, and I also tried Cypress and CasperJS but ran into rather weird problems during installation. Puppeteer just worked straight out of the box, and that's why I continued down that path.

Install Puppeteer

On Puppeteer's Github page it's defined as:

a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

Here are a few examples of what you can do with Puppeteer (although they say that you can automate almost everything that you do manually in the browser):

  • Crawl a SPA (Single-Page Application) and generate pre-rendered content, and take screenshots
  • Automate form submission, UI testing, keyboard input
  • Create an automated testing environment: run your tests directly in the latest version of Chrome

To install it, make sure you have Node.js installed, and then run npm install puppeteer in your terminal.

A simple example using Puppeteer

Here's a simple example to get you started; create a google.js file and paste this code in it:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://google.com');
  await page.screenshot({ path: 'google-screenshot.png' });

  await browser.close();
})();

This script will navigate to https://google.com and save a screenshot in an image named google-screenshot.png.

Execute the script in the command line with: node google.js and you should get a PNG image of how the https://google.com page would look like in your browser:

Mind you, the text you're seeing is Croatian, as that's my locale setting.

Looks a bit 'small', doesn't it? That's because Puppeteer sets an initial page size to 800 × 600 px, which defines the screenshot size. The page size can be customized with Page.setViewport().

Here's the code example that's using 1920 x 1080 px viewport size:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.setViewport({
    width: 1920,
    height: 1080,
    deviceScaleFactor: 1,
  });

  await page.goto('https://google.com');
  await page.screenshot({ path: 'google-screenshot.png' });

  await browser.close();
})();

Run it again with node google.js, and the screenshot should now look like this:

For more options (as I won't go into explaining every API call of the final code) make sure to check their documentation.

The Code™

Below is the quick and dirty code that JustWorks™ in logging into your App Store Connect account (please make sure to edit the script with your credentials), navigating to the Analytics screen and taking a screenshot.

I say quick and dirty because there are a myriad of improvements that could be done on this code:

  • Cookie support (so you don't have to log in and insert the OTP every time)
  • Typescript (for type safety, and other benefits)

The code is also available on Github, and if you've got the skills and the desire, please make those changes and create a PR.

const ACCOUNT = '[email protected]';
const PASSWORD = 'YourVeryMuchSuperStrongPa$$word';
const LOGIN_FRAME = '#aid-auth-widget-iFrame';
const ASC_URL = 'https://appstoreconnect.apple.com/';
const ANALYTICS_URL = 'https://appstoreconnect.apple.com/analytics';
const APP_CRASHES_URL = 'theSpecificUrlToYourSpecificApp'; //figure this out by copying the URL manually

const puppeteer = require('puppeteer');
const readline = require('readline');

const log = (msg) => {
    console.log(msg);
}

const clickSignInButton = async (frame) => {
    log('Clicked the Sign In button');

    const element = await frame.waitForSelector(
        '#stepEl > sign-in > #signin > .container > #sign-in:not(disabled)'
    );

    await element.click();
};

const clickTrustBrowser = async (frame) => {
    log('Clicked the Trust Browser button');

    const selector = 'button.trust-browser';
    const element = await frame.waitForSelector(selector);
    await element.click();
};

const askForVerificationCode = () => {
    log('Asking for verification code');

    const readlineInterface = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
    });

    return new Promise(resolve => {
        readlineInterface.question(
            'Please enter your code: ',
            answer => {
                console.log(`Thanks, you entered: ${answer}`);
                readlineInterface.close();
                resolve(answer);
            }
    );
  });
};

const login = async (page, user, password) => {
    log('Login page');
    const frameElement = await page.$(LOGIN_FRAME);
    if (!frameElement) {
        throw new Error(`Missing frame ${LOGIN_FRAME}`);
    }

    const frame = await frameElement.contentFrame();
    if (!frame) {
        throw new Error(`Missing frame ${LOGIN_FRAME}`);
    }

    const ACCOUNTInputSelector = '#account_name_text_field';
    await frame.waitForSelector(ACCOUNTInputSelector);

    await frame.focus(ACCOUNTInputSelector);
    await page.keyboard.type(user);

    await clickSignInButton(frame);

    const passwordInputSelector = '#password_text_field';
    await frame.waitForSelector(passwordInputSelector);
    await frame.waitForTimeout(2000);

    await frame.focus(passwordInputSelector);
    await page.keyboard.type(password);
    await clickSignInButton(frame);

    const verifyDeviceSelector = 'verify-phone';
    await frame.waitForSelector(`${verifyDeviceSelector}`);
    const isVerifyDevice = await frame.$(verifyDeviceSelector);

    if (isVerifyDevice) {
        log('Verify phone step');
        const verificationCode = await askForVerificationCode();
        await page.keyboard.type(verificationCode);
        await clickTrustBrowser(frame);
    }
};

const main = async () => {
    const browser = await puppeteer.launch({
        // set this to false if you want to open a browser instance and see what your
        // script is doing, where it's clicking, what it's filling out, etc.
        headless: false 
    });

    const page = await browser.newPage();
    await page.setViewport({
        // settings for my personal need, set this as you wish
        width: 1440,
        height: 815,
        deviceScaleFactor: 1,
    });

    log(`Oppening ${ASC_URL}`);
    await page.goto(ASC_URL);

    await page.waitForSelector(`${LOGIN_FRAME}`);
    await login(page, ACCOUNT, PASSWORD);

    await page.waitForNavigation({ waitUntil: 'networkidle2' });
    await page.waitForSelector('.main-nav-label');

    await page.goto(`${ANALYTICS_URL}`);
    await page.goto(`${APP_CRASHES_URL}`);

    await page.waitForNavigation({ waitUntil: 'networkidle2' });
    await page.waitForSelector('#appanalytics');
    // sometimes the selector will load, but not the whole content, so you may
    // want to play with the waitForTimeout. The argument is in mili seconds
    //await page.waitForTimeout(5000); 

    await page.screenshot({ path: 'app_analytics_crashes.png' });

    log('Closing the browser page');
    await browser.close();
};

main().catch(error => console.error(error));

When you run this code, in the terminal you should see this output:

➜ node apple.js               
Oppening https://appstoreconnect.apple.com/
Login page
Clicked the Sign In button
Clicked the Sign In button
Verify phone step
Asking for verification code
Please enter your code: 866216
Thanks, you entered: 866216
Clicked the Trust Browser button
Closing the browser page

The OTP code to log in to Apple, in my case, was sent to me via an SMS.

The screenshot (blurred for obvious reasons), looks like this in my case:

That's great, but what if my login requires CAPTCHA?

In my particular case, the login was rather simple (except for the fact that I had to select the frame and search within it for the element IDs).

But, what if your login requires you to enter the CAPTCHA? You know, those "are you human" popups that you get when logging into some website. Then I had a 'great idea' - what if you make a service where people actually read and type these CAPTCHAs for you?

⚠️ Now, let me be very clear - just because something is possible to do automatically, it doesn't give you the right to use that in a bad way (spammers be gone!). Do with this information what you wish, but don't come crying back saying "he made me do it".

Yes, no drumrolls needed, as almost everything today - this also already exists. CAPTCHA solving software exists and it's a thing, and I found a blog posts that reviews 10 CAPTCHA-solving software.

One that caught my eye was 2captcha.com, and I found a blog post that did a thorough teardown of the service. The reason that one caught my eye was that it seems that they offer their solution through libraries for most of the popular programming languages (Python, PHP, JS,Go, C#, Ruby).

A trip down the memory lane

Sorry for the sidetrack (and you can safely skip this section 😅), but I just remembered how, years ago, I attended a conference where Photomath's (the app that solves math problems and shows you the steps of how to solve them) CEO was talking about how they're solving the OCR problem (they supposedly had a breakthrough when they applied ML). They built an awesome business, that's actually useful, and I saw that they recently integrated into Snapchat via their Snapchat Solver 🤯 . I digress, yes, but with that good 'tech', I see them integrated into everything and that makes me proud (since, at the time, they were a Croatian-based startup).

Conclusion

Hope this straight-to-the-point post (with one slight detour 😅) showed you how you can make use of the amazing Puppeteer for your (legit) purposes.

Stay safe and code on 💪

Daily Thoughts, JavaScript

const life = change();

They (supposedly, originally, Heraclitus) say that the only true constant in life is change.

A JavaScript programmer in you might write that like this:

const life = change();

And, the trick in real life is that the function's implementation regularly looks like this:

function change() {
    return Math.random();
}

However, when you'd try to output the value of the life constant (see it in action in JS Fiddle), it would be exactly that, a constant (no matter how many times you'd call it or output it).

Sidenote: if you're puzzled by the fact that you can assign a function to a constant (or to a variable for that matter) before it was defined in the code, then go and learn about hoisting in JavaScript.

Now, you may write the above statement like this:

const life = function change() {
    return Math.random();
}

Now, if you call the life function (again, see it in action), it will return a different value every time you call it.

⚠️ JavaScript gurus among you may chuckle at assigning a function to a constant, but check this StackOverflow answer for its applicability - plus, you'll learn (or, refresh your memory) about hoisting in JS.

Switching gears; the point of all this is that you can't expect to be doing the same thing, and getting different results, and no matter what obstacles you face, the key to overcoming them is not in changing the event, but in changing yourself and how you react to it. And that itself is a process. A process that begins with the desire or acceptance to be teachable and improve for the better.

Hope you like this attempt at mixing programming with personal growth topics (for more, check my daily thoughts entries).

Stay safe, friends ❤️


This is why I write these 'daily thoughts' posts.

JavaScript, Vue.js

Getting started with Vue.js 3 by building a Giphy search application

TL;DR

In this post, you'll learn how to use Vue CLI to build a Vue.js 3 application for searching Giphy's GIFs by using their API.

Introduction - The War is Over

Let's be honest; even those of us that actually don't hate the JavaScript ecosystem can understand the frustration with the fact that new JS frameworks are popping up all the time.

This was especially true some 2-3 years ago. However, it seems that the craziness has settled down a bit and that the war is seemingly over. For now. These top four (top 5 lists are overrated 😉) frameworks established themselves:

  • Angular (71k)
  • React (165k)
  • Svelte (45k)
  • Vue.js (181k)

I added the number of Github `*'s (at the time of this writing), but I don't want you to read into that too much 🤗

Analysis paralysis will, well, paralyze you!

You could argue that there are other options like Ember, Mithril, or good 'ol jQuery even! However, this tweet says it all:

Developers are fighting over which frontend framework is better.. it's not like users know or care. They mostly care about the User Experience. UX should be our focus on the frontend.

Personally, I stuck to the Angular bandwagon ever since version 1.0, but that started losing it's ooomph after version 2.

I believe that Vue.js (due to its progressive and flexible nature) is a perfect fit for the teams that have to rewrite old codebases one step at a time. However, you can also use it as a full-blown framework if you wish.

Personally, React's JSX just seems wrong, and some people, smarter than me, swear by that being TheRightWay™, and I guess we're not here to discuss tastes...

General' framework war' kind of questions I tend to answer in the following way:

Please just stop with the analysis paralysis already.

Do your research, pick a framework (any framework for that matter) that the community is using, use it, and see how far it gets you.

All these talks about X being slow or Y being better just make no sense until you try it yourself for your use case and preference.

Besides, nowadays the speed will not be a deciding factor among the top JS frameworks.

With all this out of the way, fasten your seatbelts, take a venti (or trenta) sized cup of coffee, and let's go do something practical! 💪

If you care about the hardcore metrics ⏱️ and want to check out some detailed comparisons between popular frontend frameworks, here are a few blog posts.

The Demo App

As said in the intro, we'll build an application for searching (and showing) Giphy GIFs by using their API.

You can fork the complete source code on Github.

I'm using Vue.js 3, and I'll be referring to it as just Vue.js in the rest of this post.

Prerequisites

Make sure that you have the following tools installed:

  • Node.js - here's a free but outdated step by step guide/book for both Windows and Mac. Or, really, just go to the main website and download the executable for your machine
  • Git - here's a fun getting started tutorial in case you're new to it

Vue CLI

As their docs say:

Vue CLI aims to be the standard tooling baseline for the Vue ecosystem. It ensures the various build tools work smoothly together with sensible defaults so you can focus on writing your app instead of spending days wrangling with configurations. At the same time, it still offers the flexibility to tweak the config of each tool without the need for ejecting.

To install Vue CLI, run the following command (in your Terminal):

npm install -g @vue/cli

You can confirm that the installation went well if you run vue and get an output with usage information.

⚠️ Just for reference (in case you follow this tutorial at a later stage and something is not the same as I output it here), my version (get the output by running vue --version in your Terminal) as of this writing is @vue/cli 4.5.12.

Start a new app with Vue CLI

We'll call our app, originally 🙈, giphy-search. So, to start a new app using Vue CLI, run:

vue create giphy-search

Make sure you select the option ❯ Default (Vue 3 Preview) ([Vue 3] babel, eslint) and press ENTER.

You should get an output similar to this:

Vue CLI v4.5.12
✨  Creating project in /Users/nikola/DEV/BLOG/giphy-search.
🗃  Initializing git repository...
⚙️  Installing CLI plugins. This might take a while...
🚀  Invoking generators...
📦  Installing additional dependencies...
📄  Generating README.md...
🎉  Successfully created project giphy-search.

After this command finishes, let's cd into the project and run it:

cd giphy-search
npm run serve

You should get this output:

DONE  Compiled successfully in 3178ms

App running at:
  - Local:   http://localhost:8080/
  - Network: http://192.168.8.189:8080/

Note that the development build is not optimized.
To create a production build, run npm run build.

You should see the following page in your browser if you open http://localhost:8080.

Folder structure

Now, let's open this project in the editor of your choice (I'm using Visual Studio Code), and you should see something like this:

This is an introduction tutorial, to get you running fast, so I won't be going into any specific details and will be only focusing on the src folder.

Add content

OK, so let's add something to our app.

But, where to start? 🤔

Well, one of the first things I do when I come to a project for the first time is to look at the generated output. Then I try to find the strings corresponding to that output within the source code.

So, if you search for the string Welcome to Your Vue.js App, you'll see the string is within the App.vue file. This file contains the following code:

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Without knowing much about Vue.js, you can see where you would change the Welcome to Your Vue.js App text. So, let's change that to Welcome to GiphySearch. While you do that, also remove the contents of the style tag.

In the App.vue file, we have a template tag that contains a standard HTML img tag and an HelloWorld tag, which is a so-called component. Here's the link to the documentation where you can learn more about components.

Add an input and a button

Our basic application should have one input field and one button.

To do this, adjust the template in the HelloWorld.vue file to contain only this:

<div class="hello">
    <h1>{{ msg }}</h1>

    <input name="search" />
    <button>Search</button>
</div>

Actions

Having a simple search input field and a button doesn't do much. We want to click the button, and we want to output something to the console just to verify it's working correctly.

So, this is how you define a function that will handle a button click in Vue.js:

<button @click="performSearch">Search</button>

But, if you open your DevTools now and click the Search button, you'll get an error like:

runtime-core.esm-bundler.js?5c40:38 [Vue warn]: Unhandled error during execution of native event handler
  at <HelloWorld msg="Welcome to GiphySearch" >
  at <App>
warn @ runtime-core.esm-bundler.js?5c40:38
logError @ runtime-core.esm-bundler.js?5c40:211
handleError @ runtime-core.esm-bundler.js?5c40:203
callWithErrorHandling @ runtime-core.esm-bundler.js?5c40:157
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js?5c40:163
invoker @ runtime-dom.esm-bundler.js?830f:327
runtime-core.esm-bundler.js?5c40:217 Uncaught TypeError: _ctx.performSearch is not a function
    at Object.onClick._cache.<computed>._cache.<computed> (HelloWorld.vue?fdab:6)
    at callWithErrorHandling (runtime-core.esm-bundler.js?5c40:154)
    at callWithAsyncErrorHandling (runtime-core.esm-bundler.js?5c40:163)
    at HTMLButtonElement.invoker (runtime-dom.esm-bundler.js?830f:327)

That's because we haven't defined the performSearch function anywhere.

Let's do that now. In the HelloWorld.vue file, add the following function definition inside the script tag, methods object property:

<script>
export default {
    name: 'HelloWorld',
    props: {
        msg: String
    },
    methods: {
        performSearch() {
            console.log('clicked');
        }
    }
};
</script>

We defined the performSearch function, which doesn't accept any parameters and it doesn't return anything. It just outputs clicked in the console.

Gather input

What if we would like to output (to the console) the string that someone typed in the input field?

Well, first we need to add a new attribute to the input field:

<input name="title" v-model="searchTerm">

The v-model is a directive that instructs Vue.js to bind the input to the new searchTerm variable.

Finally, change the performSearch function to this:

performSearch: function() {
    console.log(this.searchTerm);
}

Technically, you could write this a bit differently, passing the searchTerm variable into the performSearch function as a parameter:

performSearch: function(searchTerm) {
   console.log(searchTerm);
}

<button @click="performSearch(searchTerm)">Search</button>

I'd love to hear from you in the comments which approach is better in your opinion.

Giphy search API

Finally, we come to the cool part 🥶, and that is to fetch some data from the Giphy API and show it in our app.

How do we get this API? Well, if you do a simple Google search for giphy api and open the first link, you'll get the documentation for their API.

You need to follow these instructions to create an account (and an app) to get an API key. You don't need an SDK solution at this point (you may want to go that route if you're going to make a mobile app that shows these images).

After you obtain your API key through their dashboard, you're going to be able to make search (they offer a bunch more) API requests through their API explorer.

An API URL (obviously, with the fake API key) may look like this: https://api.giphy.com/v1/gifs/search?api_key=AbC&q=cats&limit=10&offset=0&rating=g&lang=en

Great, now we see what kind of a request we need to create to search Giphy's GIF database for a certain term.

If you open this link in the browser, you'll see that the service returns something like:

In the next section, we'll cover retrieving this data within our app.

⚠️ Since Giphy's rules now require you to add attribution, make sure you add it. I added the image in the footer with a link to their docs.

Vue.js HTTP requests

We have several options for making HTTP requests in Vue.js, and some may see this flexibility as a pro, and some may see it as a con. Either way, for more options, check out this post.

I chose to go with Axios, as it's a very popular JavaScript library for making HTTP requests. It's an HTTP client that makes use of the modern Promises API by default (instead of the not so nice JavaScript callbacks 🙂) and runs on both the client and the server (i.e., Node.js).

In your Terminal enter the following command to install Axios via npm:

npm install axios --save

Import Axios in the HelloWorld.vue file just after the script tag:

import axios from "axios";

The performSearch function should now look like this:

let API_KEY = 'getYourOwn';
let link = `https://api.giphy.com/v1/gifs/search?api_key=${API_KEY}&limit=25&offset=0&rating=g&lang=en&q=`;
let apiLink = link + this.searchTerm;

axios
    .get(apiLink)
    .then((response) => {
        console.log(response);
    })
    .catch((error) => {
        console.log(error);
    });

Just for reference, to put it all in one listing, the contents of the HelloWorld.vue file should be:

<template>
    <div class="hello">
        <h1>{{ msg }}</h1>

        <input name="search" v-model="searchTerm" />
        <button @click="performSearch()">Search</button>
    </div>
    <a href="https://developers.giphy.com/docs/api#quick-start-guide">
        <img src="giphy.png" alt="" class="giphyAttribution" />
    </a>
</template>






If you run the app and enter something in the input box, and then click the search button, you'll see something like this in your console log:

You can see that we're getting an object that in its data property has another data property (so meta 😎) that's an array with 25 objects, which holds the information about the images we'll show in our app.

Well, after a tongue twister in the previous sentence, this is all great, but we don't want to be logging our objects to the console; we want to show them in our app! 🙂

To show the GIF, you can choose any of the properties under the images object, and use the url property. I used fixed_height_small in my example.

We don't want to just show one image but all of the images. We can use the v-for directive for that:

<img v-for="g in giphies" :key="g.id" :src="g.images.fixed_height_small.url" />

For reference, here's the full listing of HelloWorld.vue file:

<template>
    <div class="hello">
        <h1>{{ msg }}</h1>

        <input name="search" v-model="searchTerm" />
        <button @click="performSearch()">Search</button>

        <img v-for="g in giphies" :key="g.id" :src="g.images.fixed_height_small.url" />
    </div>
    <a href="https://developers.giphy.com/docs/api#quick-start-guide">
        <img src="giphy.png" alt="" class="giphyAttribution" />
    </a>
</template>






B e a utiful

At this point, if we take a look at the app and search, for example, for 'cat coding' we'll get this:

Although the result doesn't look sleek (ha!, far from it 😳), our code does exactly what it's supposed to do. If you want it to look nicer, feel free to add more CSS.

Bulma is a cool framework that I used in the JWT authentication in an Angular application with a Go backend post in case you're interested in taking a look.

Conclusion

In this tutorial, you learned how to get started with using Vue.js 3 by building an application for searching Giphy's GIFs by using their API.

Please leave any comments and feedback in the discussion section below, and thank you for reading!

JavaScript

Intro to OOP in JavaScript

TL;DR

This post will not be a regular step by step post as I usually do it; this will rather be a collection of some notes from a recent lecture I did. However, I hope you still find it useful.

OOP - Object Oriented Programming

  • an object has a state (represented by properties) and a behavior (represented by methods)
  • functions on an object are called methods
  • a developer doesn't need to know how an object’s methods work under the hood to be able to use them (encapsulation)
  • a lot of times it makes sense to model real-life objects as objects in the code

⚠️ interesting thing in JavaScript is that arrays are actually objects

You can test it like this: console.log(typeof []);

It's an object because it has a property like length, and methods like pop and push

object literal

const person = {
    name: 'Nikola',
    age: 33,
    walk: function() {
        console.log("I'm walking");
    }
}

console.log(person.name);

There's also this thing called 'bracket notation':

person['name']

person['walk']();

Here's how you would output all of the object's properties with jQuery:

$.each(person, function(i,o){
    console.log(i, o);
});

this in JavaScript

  • normal function calls
function f(){
    console.log(this); // Window object
}

f();
  • object methods
var obj = {
    a: "test",
    b: "test 2",
    log: function() {
        console.log(this.a);
    }
}

obj.log();
  • constructed object methods
var Person = function(name, surname) {
    this.name = name || 'Marko';
    this.surname = surname || 'Maric';

    this.log = function() {
        console.log(this.name + " " + this.surname);
    }
}

var pero = new Person('Pero', 'Peric'); // constructor function
pero.log();

JavaScript Class Syntax

Object literals fall short when you have to create multiple of them. Having to type each of them out and then in case of a change, change it in all the places would be unmanageable.

Class is basically a blueprint for objects that will be created with this blueprint. It contains some base set of properties and methods. The class syntax is new to JS in ES2015 and it's actually something called 'syntactic sugar'. It has the same functionality as the prototype syntax, and it's actually still using prototypes under the hood.

class Pet {
    constructor(animal, age, bread, sound) {
        this.animal = animal;
        this.age = age;
        this.bread = bread;
        this.sound = sound;
    }

    speak() {
        console.log(this.sound);
    }
}

const djoni = new Pet('dog', 1, 'bulldog', 'vau vau');

console.log(djoni);

getters

class Pet {
    constructor(animal, age, bread, sound) {
        this.animal = animal;
        this.age = age;
        this.bread = bread;
        this.sound = sound;
    }

    get activity() {
        const today = new Date();
        const hour = t.getHours();

        if (hour > 8 && hour < 20) {
            return 'playing';
        }
        else {
            return 'sleeping';
        }
    }

    speak() {
        console.log(this.sound);
    }
}

const djoni = new Pet('dog', 1, 'bulldog', 'vau vau');

console.log(djoni.activity); // activity is a so-called 'computed property'.

setters

get owner() {
    return this._owner.
}

set owner(owner) { // MUST have exactly one formal parameter!
    this._owner = owner; // this is called a 'backing property'
}

djoni.owner = 'Nikola';
JavaScript

Making AJAX calls using the Fetch API

TL;DR

In this post we'll do everything we did in the second post, but with Fetch API.

What's this Fetch API?

The almighty docs say

The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest (see the first post in the series on how to do that), but the new API provides a more powerful and flexible feature set.

I prepared this demo page so that you can test.

You'll remember from the last post that in order to make an AJAX call with jQuery, you have to do something like this:

$('#result').load('http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola');

Go ahead and run that code on the demo page in the browser's dev tools Console (consult the last post if you're stuck).

Now, the very same thing with the Fetch API looks like this:

fetch('http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola')
  .then(function(response) {
    return response.text();
  })
  .then(function(text) {
    $('#result').html(text);
  });

Go ahead and try it in the Console. Change the ime parameter, and you'll see that the text on the page will change to Hello [name], where [name] will be the parameter you entered. Note that in the example above I still used jQuery to set the content of the div with id result.

The docs have way more info on this but, as they say, the difference between fetch() and $.ajax() is in two main things:

  • The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
  • By default, fetch() won't send or receive any cookies from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials init option must be set).

⚠️ At a later point you may want to read a bit more about Promises in Javascript.

Rewriting the mini project

I encourage you to try it for yourself and then check your solution to mine.

The mini project (which you can test here) would be rewritten like this:

var link = 'http://nikola-breznjak.com/_testings/ajax/test2.php';
fetch(link)
    .then(function(response){
        return response.json()
    })
    .then(function(result){
        var oglasiHTML = '';
        $.each(result, function(index, oglas){
            var klasaCijene = '';
            if (oglas.cijena < 100){
                klasaCijene = 'is-success';
            }
            else if (oglas.cijena >= 100 && oglas.cijena < 300){
                klasaCijene = 'is-info';
            }
            else if (oglas.cijena >= 300){
                klasaCijene = 'is-danger';
            }

            oglasiHTML += `
                <div class="columns">
                    <div class="column is-one-quarter">
                        <span class="tag ${klasaCijene}">${oglas.cijena}</span>
                    </div>
                    <div class="column">${oglas.tekst}</div>
                </div>
            `;
        });

        $('#oglasi').html(oglasiHTML);
    });

There are a few things that I'd like to point out here:

  • I used the response.json() because I know that this API returns a JSON response (you can check by opening that link in the browser).
  • I used the jQuery each function to iterate over the result.
  • I used the template literals to construct the final oglasiHTML in a much cleaner way than we did that in the previous post with using concatenation.

I want more examples

Say you have this demo page, and you need to make the newsletter signup form use AJAX instead of going to a new page when you click the Subscribe button. Right now the API (located at http://nikola-breznjak.com/_testings/ajax/email.php) doesn't actually process what you send to it but try to send data with the request as well for practice.

How would you do it? Where would you start?

Well, here are a few search terms you could Google:

  • `fetch api submit data'

Again, here's my solution, but I encourage you to try it yourself first.

$('form').submit(function(ev){
    ev.preventDefault();
    var url = $(this).attr('action');
    var formData = $(this).serialize();
    fetch(url, {method: 'post', body: formData})
        .then(function(response){
            $('#newsletter').html('Thank you for subscribing, please check your email.');
        });
});

There are a few things that I'd like to point out here:

  • I was able to use the form selector without any id because it's the only form on the page. If there were more forms on the page, I'd have to distinguish them by using an id (which is always a good practice, even if it's just one form)
  • I used jQuery's submit function to set an event handler that will be executed when the form will be submitted (Subscribe button clicked, or ENTER pressed while in one of the fields)
  • I used the ev.preventDefault() to prevent the form from "doing its thing" and loading the 'email.php' page, as its default behavior
  • I used $(this).attr('action') to extract the API URL from the form definition it the HTML (feel free to check it out in the element inspector)
  • I used $(this).serialize() to encode a set of form elements as a string which I then passed to body attribute of the fetch settings object. This function sends a request to the server using the POST method (using the method setting on the settings object), which (as we learned in the previous post) is the preferred way of sending some sensitive data that needs to be handled on the server
  • I used the html function on a div with the id newsletter to set its new content

Can I have one more, please?

Sure, but promise you'll do it yourself first!

I do ?

OK then, let's do this ?

Here's a new demo page that you'll use for this example.

⚠️ I'm using Bulma to make the site look pretty. As their website says:
'Bulma is a free, open-source CSS framework based on Flexbox and used by more than 150,000 developers.'
I'll just say it's really great and easy to learn, so make sure you check it out.

In this challenge, you have to create a gif search application using the Giphy Search API. As the docs state, you'll need to get the API key by creating an app. Once you do that, you'll be able to search their API like, for example, this: http://api.giphy.com/v1/gifs/search?q=funny+cat&api_key=dc6zaTOxFJmzC. You can use this API key, but be sure to create your own if this one gets banned for too many requests ?‍

Once you fetch the gifs (or still images if you so choose), show them in the div with a class result (yes, class, not id ?).

Here's the part where you roll up your sleeves, create it, feel proud of yourself ?, and then come here to check how I did it.

$('form').submit(function(ev){
    ev.preventDefault();
    var searchTerm = $('input').val();

    var apiLink = "http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=" + searchTerm;

    var output = '';
    fetch(apiLink)
        .then(function(response){
            return response.json();
        })
        .then(function(images){
            $.each(images.data, function(i, im){
                output += `<img src="${im.images.downsized.url}"/>`;
            });

            $('.result').html(output);
        });
});

As you may notice, very few things changed from the last post, and it's quite easy to exchange the jQuery AJAX calls with Fetch API.

Conclusion

That's all for this blog post. I hope it was useful and that you've seen how easy it is to make AJAX requests with the Fetch API. I also hope you liked the demo exercises ?
This wraps it up with these three post series. Have a great time and see you soon! ?

JavaScript

Making AJAX calls using jQuery

TL;DR

In this post we'll do everything (and a bit more ?) we did in the first post, but with jQuery.

Why is jQuery better?

I prepared this demo page so that you can test.

You'll remember from the last post that in order to make an AJAX call in pure JavaScript, you have to do something like this:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4){
        document.getElementById('result').innerHTML = xhr.responseText;
    }
};
xhr.open('GET', 'http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola');
xhr.send();

Go ahead and run that code on the demo page in the browser's dev tools Console (consult the last post if you're stuck).

Now, to do the very same thing with jQuery, all you have to do is this:

$('#result').load('http://nikola-breznjak.com/_testings/ajax/test1.php?ime=Nikola');

Go ahead and try it in the Console. Change the ime parameter, and you'll see that the text on the page will change to Hello [name], where [name] will be the parameter you entered.

⚠️ I won't go into the basics of using jQuery, as it's been here for a very long time, and there's a vast amount of tutorials written about it. Start with the official docs if you want to learn more.

The important thing to note here is that .load must be chained to a so-called jQuery selection, as mentioned in the .load() documentation. We loaded the data from the server and placed the returned text into the element with an id result.

Shorthand methods

By looking at the docs, you can see that there are more of these so-called shorthand methods for making AJAX calls, all of which could be done with the most versatile ajax function.

The example from before could be done using the get shorthand method:

    var link = 'http://nikola-breznjak.com/_testings/ajax/test1.php';
    var data = {ime:"Nikola"};
    var callback = function(result) {
        $('#result').html(result);
    }
    $.get(link, data, callback);

If you don't want to pass any parameters, it gets even simpler:

    var link = 'http://nikola-breznjak.com/_testings/ajax/test1.php';
    var callback = function(result) {
        $('#result').html(result);
    }
    $.get(link, callback);

Rewriting the mini project

I prepared the same demo page as in the previous post, but with jQuery already included so you can test this by just pasting the code below in the Console. I encourage you first to try it yourself and then compare your solution with mine below.

var link = 'http://nikola-breznjak.com/_testings/ajax/test2.php';
$.getJSON(link, function(result){
    var oglasiHTML = '';
    $.each(result, function(index, oglas){
        var klasaCijene = '';
        if (oglas.cijena < 100){
            klasaCijene = 'is-success';
        }
        else if (oglas.cijena >= 100 && oglas.cijena < 300){
            klasaCijene = 'is-info';
        }
        else if (oglas.cijena >= 300){
            klasaCijene = 'is-danger';
        }

        oglasiHTML += `
            <div class="columns">
                <div class="column is-one-quarter">
                    <span class="tag ${klasaCijene}">${oglas.cijena}</span>
                </div>
                <div class="column">${oglas.tekst}</div>
            </div>
        `;
    });

    $('#oglasi').html(oglasiHTML);
});

There are a few things that I'd like to point out here:

  • I used the getJSON function to call my API located on the URL that's saved in the link variable. This is because I know that this API returns a JSON response (you can check by opening that link in the browser).
  • I used the jQuery each function to iterate over the result.
  • I used the template literals to construct the final oglasiHTML in a much cleaner way than we did that in the previous post with using concatenation.
  • The core logic remained the same as in the first post, with the exception of not having to use the for loop and indexes to access each element of the array.

I want more examples

Say you have this demo page, and you need to make the newsletter signup form use AJAX instead of going to a new page when you click the Subscribe button. Right now the API (located at http://nikola-breznjak.com/_testings/ajax/email.php) doesn't actually process what you send to it, but try to send data with the request as well for practice.

How would you do it? Where would you start?

Well, here are a few search terms you could Google:

  • `form submit jquery'
  • post data

Again, here's my solution, but I encourage you to try it yourself first.

$('form').submit(function(ev){
    ev.preventDefault();
    var url = $(this).attr('action');
    var formData = $(this).serialize();
    $.post(url, formData, function(response){
        $('#newsletter').html('Thank you for subscribing, please check your email.');
    });
});

There are a few things that I'd like to point out here:

  • I was able to use the form selector without any id because it's the only form on the page. If there were more forms on the page, I'd have to distinguish them by using an id (which is always a good practice, even if it's just one form)
  • I used jQuery's submit function to set an event handler that will be executed when the form will be submitted (Subscribe button clicked, or ENTER pressed while in one of the fields)
  • I used the ev.preventDefault() to prevent the form from "doing its thing" and loading the 'email.php' page, as its default behavior
  • I used $(this).attr('action') to extract the API URL from the form definition it the HTML (feel free to check it out in the element inspector)
  • I used $(this).serialize() to encode a set of form elements as a string which I then passed to the jQuery post shorthand function. This function sends a request to the server using the POST method, which (as we learned in the previous post) is the preferred way of sending some sensitive data that needs to be handled on the server
  • I used the html function on a div with the id newsletter to set its new content

Can I have one more, please?

Sure, but promise you'll do it yourself first!

I do ?

OK then, let's do this ?

Here's a new demo page that you'll use for this example.

⚠️ I'm using Bulma to make the site look pretty. As their website says:
'Bulma is a free, open source CSS framework based on Flexbox and used by more than 150,000 developers.'
I'll just say it's really great and easy to learn, so make sure you check it out.

In this challenge, you have to create a gif search application using the Giphy Search API. As the docs state, you'll need to get the API key by creating an app. Once you do that, you'll be able to search their API like, for example, this: http://api.giphy.com/v1/gifs/search?q=funny+cat&api_key=dc6zaTOxFJmzC. You can use this API key, but be sure to create your own if this one gets banned for too many requests ?‍

Once you fetch the gifs (or still images if you so choose), show them in the div with a class result (yes, class, not id ?).

Here's the part where you roll up your sleeves, create it, feel proud of yourself ?, and then come here to check how I did it.

$('form').submit(function(ev){
    ev.preventDefault();
    var searchTerm = $('input').val();

    var apiLink = "http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=" + searchTerm;

    var output = '';
    $.getJSON(apiLink, function(images){
        $.each(images.data, function(i, im){
            output += `<img src="${im.images.downsized.url}"/>`;
        });

        $('.result').html(output);
    });
});

The only new, and potentially tricky, part here is the traversal of the JSON object that you get back from the API, but with just a bit training, you'll get good at it.

If you open up this URL in your browser, you will see this

The returned JSON object consists of three properties:

  • data
  • pagination
  • meta

The data is an array of objects, and in one of its properties it has an array of images. In my example, I choose to display the downsized gif by accessing its url property.

Here's a short gif of this in action:

Conclusion

That's all for this blog post. I hope it was useful and that you've seen how much easier it is to make AJAX requests with jQuery vs. plain JavaScript. I also hope you liked the demo exercises ?
In the next blog post, we'll see how to do this with the modern fetch API.

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.

Page 1 of 131234»10...Last »

Recent posts

  • Using Puppeteer to automate ASC Analytics screenshots
  • Do it when you don’t feel like it
  • Self-mastery
  • Writing well
  • const life = change();

Categories

  • Android (3)
  • Books (111)
    • Programming (22)
  • CodeProject (35)
  • Daily Thoughts (71)
  • Go (3)
  • iOS (5)
  • JavaScript (123)
    • Angular (4)
    • Angular 2 (3)
    • Ionic (61)
    • Ionic2 (2)
    • Ionic3 (8)
    • MEAN (3)
    • NodeJS (27)
    • Phaser (1)
    • Three.js (1)
    • Vue.js (1)
  • Meetups (8)
  • Miscellaneou$ (76)
    • Breaking News (8)
    • CodeSchool (2)
    • Hacker Games (3)
    • Pluralsight (7)
    • Projects (2)
    • Sublime Text (2)
  • PHP (6)
  • Quick tips (36)
  • Servers (7)
    • Heroku (1)
    • Linux (2)
  • 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