Convert Flask Application into a Cross-Platform Desktop App Executable

Python, powerful and versatile as it is, lacks a few key capabilities out of the box. For one, Python provides no native mechanism for compiling a Python program into a standalone executable package.

To be fair, the original use case for Python never called for standalone packages. Python programs have, by and large, been run in-place on systems where a copy of the Python interpreter lived. But the surging popularity of Python has created greater demand for running Python apps on systems with no installed Python runtime.

Several third parties have engineered solutions for deploying standalone Python apps. The most popular solution of the bunch, and the most mature, is PyInstaller. PyInstaller doesn’t make the process of packaging a Python app to go totally painless, but it goes a long way there.

Waitress WSGI Server

Waitress is a pure-Python WSGI server. At a first glance it might not appear to be that much different than many others; however, its development philosophy separates it from the rest. Its aim for easing the production (and development) burden caused by web servers for Python web-application developers.

The installation is pretty simple. It is highly recommended to create a virtual environment before you install Waitress via the pip install command:

pip install waitress

Then You need to first import waitress via the following command:

from waitress import serve

I will be using the app as the variable name for the Flask server. Modify this according to the name that you have set:

app = Flask(__name__)

Comment out the app.run in your main server and add the following code.

By default, Waitress binds to any IPv4 address on port 8080. You can omit the host and port arguments and just call serve with the WSGI app as a single argument. we overwrite it and set the port to 5000 for demostration on how to change them.

serve(
app.run()
   host="127.0.0.1",
   port=5000,
   threads=2
)

The most commonly-used parameters for serve are as follows:

  • host — Hostname or IP address (string) on which to listen, default 127.0.0.1, which means “all IP addresses on this host”. May not be used with the listen parameter.
  • port — TCP port (integer) on which to listen, default 8080. May not be used with the listen parameter.
  • ipv4 — Enable or disable IPv4 (boolean).
  • ipv6 — Enable or disable IPv6 (boolean).
  • threads — The number of threads used to process application logic (integer). The default value is 4.

Create Executable from Python Script using Pyinstaller

PyInstaller can be used to create .exe files for Windows, .app files for Mac, and distributable packages for Linux. Optionally, it can create a single file which is more convenient for distributing, but takes slightly longer to start because it unzips itself.

The installation is pretty simple. It is highly recommended to create a virtual environment before you install via the pip install command.

pip install pyinstaller

This file is saved in build.sh and runs this file using the following command in the terminal.

./build.sh

For windows is:

pyinstaller wsgi.py \
--onefile \
--distpath "./bin" \
--name "our_app" \
--add-data "db.sqlite3;." \
--hidden-import waitress \
--clean

For mac is:

pyinstaller wsgi.py \
--onefile \
--distpath "./bin" \
--name "our_app" \
--add-data "db.sqlite3;." \
--hidden-import waitress \
--clean

For ubuntu is:

pyinstaller wsgi.py \
--onefile \
--distpath "./bin" \
--name "our_app" \
--add-data "db.sqlite3:." \
--hidden-import waitress \
--clean

The most commonly-used parameters for build.sh are as follows:

  • --name — Change the name of your executable.
  • --onefile — Package your entire application into a single executable file. The default options create a folder of dependencies and an executable, whereas --onefile keeps distribution easier by creating only an executable.
  • --hidden-import — List multiple top-level imports that PyInstaller was unable to detect automatically. This is one way to work around your code using import inside functions and import(). You can also use --hidden-import multiple times in the same command.
  • --add-data and --add-binary — Instruct PyInstaller to insert additional data or binary files into your build. This is useful when you want to bundle in configuration files, examples, or other non-code data.

PyInstaller is complicated under the hood and will create a lot of output. So, it’s important to know what to focus on first. Namely, the executable you can distribute to your users and potential debugging information. By default, the pyinstaller command will create a few things of interest:

  • A *.spec file

    • where all configuration was put by pyinstaller
  • A build folder

    • The build folder is where PyInstaller puts most of the metadata and internal bookkeeping for building your executable.
    • The build folder can be useful for debugging, but unless you have problems, this folder can largely be ignored.
  • A bin folder

    • will be created After building, you’ll end up with a bin folder similar to the following:
      bin/
      |
      └── our_app/
      └── our_app  # this is the executable

The bin folder contains the final artifact you’ll want to ship to your end users. Inside the bin folder, there is a folder named after your entry-point. So in this example, you’ll have a bin/our_project folder that contains all the dependencies and executable for our application.

The executable to run is bin/our_app/our_app or bin/our_app/our_app.exe if you’re on Windows.

pyInstaller creates a temp folder and the name of that folder is __MEIPASS__. Generally a new e.g. __ME<Random_Value>__ file created at the time of each time we execute the file and previous __MEIPASS__ file deleted because of it’s volatile memory. So the previous data is removed from storage as we store our db and other files in the that temp folder using pyinstallers --add-data property, but we need to store previous data for the persistence. For this reason we create a hidden folder in the system's home directory and store data in this folder. But initially sqlite database file does not exist in this hidden folder. So at execution time we create a hidden folder in the system home directory when we execute the file and we have to copy that fresh db along with other files from the temp folder and save to the hidden folder. The code of copying and saving this db along with the other files given below:

import os, shutil
from pathlib import Path
from .resources import get_resources_path

APP_NAME = "our_app"
HOME_DIR = Path.home()
APP_DIR = HOME_DIR / f".{APP_NAME.lower()}"
if not APP_DIR.exists():  ## checking if our persistence hidden filder exists or not
   os.mkdir(APP_DIR)  ## create the hidden folder
data = get_resources_path() / data ## searching files in the temp folder
if not (APP_DIR /  data).exists():  ## checking if our persistence files already in the hidden directory or not
   try:
       shutil.copy(data, APP_DIR)
   except Exception as e:
       log.exception(e)

Get resources path function to find the __MEIPASS__ folder path link from where we can copy fresh data and can store to the hidden folder.

import pathlib
import sys
def get_resources_path(relative_path="."):
    rel_path = pathlib.Path(relative_path)
    prod_base_path = pathlib.Path(__file__).resolve().parent.parent

    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception as e:
        base_path = getattr(sys, "_MEIPASS", prod_base_path)

    return base_path / rel_path

Conclusion

PyInstaller can help make complicated installation documents unnecessary. Instead, your users can simply run your executable to get started as quickly as possible. The PyInstaller workflow can be summed up by doing the following:

  • Create an entry-point script that calls your main function.
  • Install PyInstaller.
  • Run PyInstaller on your entry-point.
  • Test your new executable.
  • Ship your resulting dist/ folder to users.

Your users don’t have to know what version of Pyt hon you used or that your application uses Python at all!

React: Form Validation (having nested schema) with Formik, Yup, and Material-UI

Nowadays forms are crucial in any application. Building forms are pretty simple but adding validation to them may become a bit of a challenge. It gets even trickier when our forms have a complex structure, like binding the form fields to nested object properties and validating them. You’ll see shortly what I meant by that. The official React documentation doesn't give you that much on how to handle forms. Well, I think this is ok since forms may have so many underlying business logic when it comes to validation. And React is only concerned about the UI not about the heavy business logic.

So, the background of this post is that a few days ago I was trying to validate a form and I struggled with it a bit as it contains nested schema. So I decided to write a blog for anyone having the same issues. I've used Material-UI TextField for building the form and used Formik and Yup for validating it. Now I’ll discuss the terms I’ve mentioned (Formik, Yup, Material-UI).

Formik is a small library that helps us with managing states, handling validations and error messages, and handling form submission, etc. You can learn more on https://formik.org/.

Yup is a schema builder that helps us to create a clean validation object, which then can be provided to the validationSchema property of Formik. You can learn more on https://github.com/jquense/yup.

Material-UI provides well-designed input fields and form structure. You can learn about all the form elements and much more on https://material-ui.com/.

Install the required packages:

Let’s get started by installing the required packages using the following command:

npm install @material-ui formik yup

Building the form

We'll build the form based on the following object:

const initialValues = {
    name: '',
    age: '',
    email: '',
    phone: '',
    social: {
      facebook: '',
      linkedin: ''
    },
    password: '',
    confirmPassword: ''
};

As you can see in this object initialValues there's a nested object social with two properties, this is the nested object that I've mentioned earlier. Now let's create the form.

We'll import some Material-UI components which are optional and we’re only using these for a well-designed UI.

import clsx from ‘clsx’;
import PropTypes from ‘prop-types’;
import { makeStyles } from ‘@material-ui/styles’;
import { Card, CardHeader, CardContent, CardActions, Divider, Grid, TextField, Button } from ‘@material-ui/core’;

Here's the full code of the form:

import React from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import {
  Card,
  CardHeader,
  CardContent,
  CardActions,
  Divider,
  Grid,
  TextField,
  Button
} from '@material-ui/core';

const useStyles = makeStyles((theme) => ({
  root: {
    padding: 0,
    height: '100%'
  },
  actions: {
    justifyContent: 'flex-end',
    padding: theme.spacing(2)
  }
}));

const SignUpForm = () => {
  const classes = useStyles();
  const initialValues = {
    name: '',
    age: '',
    email: '',
    phone: '',
    social: {
      facebook: '',
      linkedin: ''
    },
    password: '',
    confirmPassword: ''
  };

  return (
    <Card className={clsx(classes.root)}>
      <CardHeader title="Sign Up" />
      <Divider />
      <form autoComplete="off">
        <CardContent>
          <Grid container spacing={2}>
            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="Name"
                name="name"
                type="text"
                variant="outlined"
                size="small"
              />
            </Grid>
            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="Age"
                name="age"
                type="number"
                variant="outlined"
                size="small"
              />
            </Grid>
            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="Email"
                name="email"
                type="text"
                variant="outlined"
                size="small"
              />
            </Grid>

            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="Phone"
                name="phone"
                type="text"
                variant="outlined"
                size="small"
              />
            </Grid>
            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="Facebook"
                name="social.facebook"
                type="text"
                variant="outlined"
                size="small"
              />
            </Grid>
            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="LinkedIn"
                name="social.linkedin"
                type="text"
                variant="outlined"
                size="small"
              />
            </Grid>
            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="Password"
                name="password"
                type="password"
                variant="outlined"
                size="small"
              />
            </Grid>

            <Grid item md={6} xs={12}>
              <TextField
                fullWidth
                label="Confirm Password"
                name="confirmPassword"
                type="password"
                variant="outlined"
                size="small"
              />
            </Grid>
          </Grid>
        </CardContent>
        <Divider />
        <CardActions className={classes.actions}>
          <Button color="primary" type="submit" variant="contained">
            Save
          </Button>
        </CardActions>
      </form>
    </Card>
  );
};

SignUpForm.propTypes = {
  className: PropTypes.string
};

export default SignUpForm;

The form looks like the following:

Adding validations to the form

Our goal is to prevent users from submitting an invalid form. We'll use different validation criteria as you'll see shortly in the Yup validation schema. We'll make the button disabled and enable it once all the validation criteria are met.

Let's import the libraries required for the validation.

import * as Yup from ‘yup’;
import { Formik, getIn } from ‘formik’;

Let's have a look at the yup validation schema object:

validationSchema={Yup.object().shape({
          name: Yup.string().required('Name is required'),
          age: Yup.number()
            .required('Age is required')
            .min(13, 'You must be at least 13 years old'),
          email: Yup.string()
            .email('Please enter a valid email')
            .required('Email is required'),
          phone: Yup.string().required('Phone is required'),
          social: Yup.object().shape({
            facebook: Yup.string().required('Facebook username is required'),
            linkedin: Yup.string().required('LinkedIn username is required')
          }),
          password: Yup.string()
            .required('Please enter your password')
            .matches(
              /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/,
              'Password must contain 8 characters, one uppercase, one lowercase, one number and one special case Character'
            ),
          confirmPassword: Yup.string()
            .required('Please enter the password again')
            .oneOf([Yup.ref('password'), null], "Passwords didn't match")
  })}

You'll notice that the nested object social holds another Yup schema.

social: Yup.object().shape({
    facebook: Yup.string().required('Facebook username is required'),
    linkedin: Yup.string().required('LinkedIn username is required')
}),

Now, let’s bring everything together, then we’ll discuss it.

import React from 'react'; import clsx from 'clsx'; import PropTypes from 'prop-types'; import { makeStyles } from '@material-ui/styles'; import * as Yup from 'yup'; import { Formik, getIn } from 'formik'; import { Card, CardHeader, CardContent, CardActions, Divider, Grid, TextField, Button } from '@material-ui/core'; const useStyles = makeStyles((theme) => ({ root: { padding: 0, height: '100%' }, actions: { justifyContent: 'flex-end', padding: theme.spacing(2) } })); const SignUpForm = () => { const classes = useStyles(); const initialValues = { name: '', age: '', email: '', phone: '', social: { facebook: '', linkedin: '' }, password: '', confirmPassword: '' }; return ( <Card className={clsx(classes.root)}> <CardHeader title="Sign Up" /> <Divider /> <Formik initialValues={{ ...initialValues }} validationSchema={Yup.object().shape({ name: Yup.string().required('Name is required'), age: Yup.number() .required('Age is required') .min(13, 'You must be at least 13 years old'), email: Yup.string() .email('Please enter a valid email') .required('Email is required'), phone: Yup.string().required('Phone is required'), social: Yup.object().shape({ facebook: Yup.string().required('Facebook username is required'), linkedin: Yup.string().required('LinkedIn username is required') }), password: Yup.string() .required('Please enter your password') .matches( /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/, 'Password must contain 8 characters, one uppercase, one lowercase, one number and one special case Character' ), confirmPassword: Yup.string() .required('Please enter the password again') .oneOf([Yup.ref('password'), null], "Passwords didn't match") })} onSubmit={(values) => { console.log(values); }}> {({ errors, handleBlur, handleChange, handleSubmit, isSubmitting, isValid, dirty, touched, values }) => ( <form autoComplete="off" noValidate onSubmit={handleSubmit}> <CardContent> <Grid container spacing={2}> <Grid item md={6} xs={12}> <TextField error={Boolean(touched.name && errors.name)} fullWidth required helperText={touched.name && errors.name} label="Name" name="name" onBlur={handleBlur} onChange={handleChange} type="text" value={values.name} variant="outlined" size="small" /> </Grid> <Grid item md={6} xs={12}> <TextField error={Boolean(touched.age && errors.age)} fullWidth required helperText={touched.age && errors.age} label="Age" name="age" onBlur={handleBlur} onChange={handleChange} type="number" value={values.age} variant="outlined" size="small" /> </Grid> <Grid item md={6} xs={12}> <TextField error={Boolean(touched.email && errors.email)} fullWidth required helperText={touched.email && errors.email} label="Email" name="email" onBlur={handleBlur} onChange={handleChange} type="text" value={values.email} variant="outlined" size="small" /> </Grid> <Grid item md={6} xs={12}> <TextField error={Boolean(touched.phone && errors.phone)} fullWidth required helperText={touched.phone && errors.phone} label="Phone" name="phone" onBlur={handleBlur} onChange={handleChange} type="text" value={values.phone} variant="outlined" size="small" /> </Grid> <Grid item md={6} xs={12}> <TextField error={Boolean( getIn(touched, 'social.facebook') && getIn(errors, 'social.facebook') )} fullWidth required helperText={ getIn(touched, 'social.facebook') && getIn(errors, 'social.facebook') } label="Facebook" name="social.facebook" onBlur={handleBlur} onChange={handleChange} type="text" value={values.social.facebook} variant="outlined" size="small" /> </Grid> <Grid item md={6} xs={12}> <TextField error={Boolean( getIn(touched, 'social.linkedin') && getIn(errors, 'social.linkedin') )} fullWidth required helperText={ getIn(touched, 'social.linkedin') && getIn(errors, 'social.linkedin') } label="LinkedIn" name="social.linkedin" onBlur={handleBlur} onChange={handleChange} type="text" value={values.social.linkedin} variant="outlined" size="small" /> </Grid> <Grid item md={6} xs={12}> <TextField error={Boolean(touched.password && errors.password)} fullWidth required helperText={touched.password && errors.password} label="Password" name="password" onBlur={handleBlur} onChange={handleChange} type="password" value={values.password} variant="outlined" size="small" /> </Grid> <Grid item md={6} xs={12}> <TextField error={Boolean( touched.confirmPassword && errors.confirmPassword )} fullWidth required helperText={ touched.confirmPassword && errors.confirmPassword } label="Confirm Password" name="confirmPassword" onBlur={handleBlur} onChange={handleChange} type="password" value={values.confirmPassword} variant="outlined" size="small" /> </Grid> </Grid> </CardContent> <Divider /> <CardActions className={classes.actions}> <Button color="primary" disabled={Boolean(!isValid)} type="submit" variant="contained"> Save </Button> </CardActions> </form> )} </Formik> </Card> ); }; SignUpForm.propTypes = { className: PropTypes.string }; export default SignUpForm;

We've added noValidated to the form to prevent HTML5 default form validation. Now let's discuss the following text field:

<TextField error={Boolean(touched.name && errors.name)} fullWidth required helperText={touched.name && errors.name} label="Name" name="name" onBlur={handleBlur} onChange={handleChange} type="text" value={values.name} variant="outlined" size="small" />

Here error and helperText will be set conditionally if there's an error and the input field is touched. Now let's discuss the following text field with a slightly different syntax:

<TextField error={Boolean( getIn(touched, 'social.facebook') && getIn(errors, 'social.facebook') )} fullWidth required helperText={ getIn(touched, 'social.facebook') && getIn(errors, 'social.facebook') } label="Facebook" name="social.facebook" onBlur={handleBlur} onChange={handleChange} type="text" value={values.social.facebook} variant="outlined" size="small" />

Here, because of the nested object, we're setting error and helperText values differently. We're using a helper function getIn() provided by Formik. Also, notice the value and name prop and how we set the value by accessing values.social.facebook, etc.

You'll also notice we've conditionally made the button disabled for invalid form:

<Button color="primary" disabled={Boolean(!isValid)} type="submit" variant="contained"> Save </Button>

After running the final code snippet if we try to submit the invalid form the output looks like this:

blank

If you submit a valid form after filling out all the required fields, you'll get the values automatically passed to the onSubmit() function by Formik. Then you can write the necessary code to send those data to the backend if you want.

onSubmit={(values) => { console.log(values); }}

Summary

Here, we described how can we validate forms and show error messages with Formik, Yup, and Material-UI. Most importantly we've worked with the nested object and discussed how to validate the nested object properties. We also discussed how to access the submitted data.

That's it, I hope you've enjoyed this simple tutorial and this is helpful to you. To learn more about Formik, Yup and Material-UI please visit the following links in the Resources section.

Thanks!

Resources

  1. Formik: https://jaredpalmer.com/formik/docs/overview
  2. Material-UI: https://material-ui.com/
  3. Yup: https://github.com/jquense/yup

Issue Arises When Building A Desktop App Using Electron And Electron-Builder

Author: Eftakher Sazid
Designation: Intern at Vivasoft LTD

blank

Javascript and its available incredible user-friendly frameworks make it very easy to make web applications. But as it runs only on the web and browsers, it is not possible to create a desktop application using Javascript. Here ElectronJS comes to the rescue.

ElectronJS is an open-source framework that allows us to create desktop applications using web technologies. That means Javascript, HTML, CSS, and their entire arsenal of frameworks. That makes it significantly easy to design and create a GUI for a desktop application.

ElectronJS has all the tools as the browser and some additional tools to access the file system for reading and writing operations. So it is not a browser but more. It is pretty easy to create an application in development mode but the tricky part comes while packaging the app for production. We’ll explore all the challenges of creating one.

The very first step is to create a project. Now we can create a Javascript and HTML-only project pretty easily but the hassle begins while adding the frameworks like React and webpack. Now boilerplates for electron applications have solved the hassle of manually configuring the project along with all its dependencies. Popular boilerplates like electron-react-boilerplateelectron-forgeelectron-builder are widely used.

For my application, I used the electron-react-boilerplate. It has support for TypeScriptReactreact-router-dom out of the box and it uses electron-builder to package the application. It has a pretty straightforward project setup that you can find here. Then after project setup, you should get rid of all the unnecessary pre-installed dependencies and config files. Let me name some of them, .github.git(obviously, you don’t want to use that git), CHANGELOG.mdCODE_OF_CONDUCT.mdsrc/__test__ if you don't need them.

The first issue that you may face that electron-react-boilerplate has all the support needed for Typescript but if you use Javascript only like I did, you have to change the .erb/webpack.config.base.js file in the project directory. By default, while packaging this application using the electron-builder it will only accept files with the .tsx extension.

import path from 'path'; import webpack from 'webpack'; import { dependencies as externals } from '../../src/package.json'; export default { externals: [...Object.keys(externals || {})], module: { rules: [ { test: /\.tsx?$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { cacheDirectory: true, }, }, }, ], } } 

Now to configure for accepting files with .js.jsx and .ts extensions we need to change,

import path from 'path'; import webpack from 'webpack'; import { dependencies as externals } from '../../src/package.json'; export default { externals: [...Object.keys(externals || {})], module: { rules: [ { test: /\.ts|\.tsx|\.jsx|\.js?$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { cacheDirectory: true, }, }, }, ], } } 

Now we are good to go.

The second issue that I faced was using third-party UI libraries like semantic-uiant-design was that even after installing the dependencies the imports of their CSS files in the index.tsx was not working.

import 'react-datepicker/dist/react-datepicker.css'; import 'antd/dist/antd.css';

Now to solve this you have to add them inside the <head> tag of your src/index.html file as <link/> .

<head> <meta charset="utf-8" /> <title>App name</title> <link rel="stylesheet" type="text/css" href="../node_modules/react-toastify/dist/ReactToastify.css" /> <link rel="stylesheet" type="text/css" href="../node_modules/antd/dist/antd.css" /> ... </head>

Now all the CSS property of the UI components will be working fine.

For my project, I had to run the executable of my backend when my app was starting. To run an external application electron can use the exec and execFile from the child_process of the node.

import { exec, execFile } from 'child_process';

The execFile executes the executable for my backend. You can run pretty much any executable using this.

execFile( backEnd, { windowsHide: false, }, (err, stdout, stderr) => { if (err) { console.log(err); } if (stdout) { console.log(stdout); } if (stderr) { console.log(stderr); } } );

To close the backend while closing the app you can use exec.

exec(`taskkill /f /t /im ${executableName}`, (err, stdout, stderr) => { if (err) { log.error(err); console.log(err); return; } console.log(`stdout: ${stdout}`); console.log(`stderr: ${stderr}`); });

 

For this to work, I kept my backend in the src/Backend folder. Now to package this along with all the dependencies I need to add this folder to the package.json file. Any file or folder included in the “files” array, will be added to the app.asar file. That does not solve it, because while packaging using electron-builder all these files are exported to a format called .asar format. The problem with this format is that no executable or app can be executed from within this .asar format. So we have to unpack the backend from the packaged app.asar file. The file specified in the “asarUnpack” will be unpacked from the app.asar and will be executable.

"build": { "productName": "App name", "appId": "org.erb.appName", "asarUnpack": [ "Backend/bin/*" ], "files": [ "dist/", "node_modules/", "index.html", "main.prod.js", "main.prod.js.map", "package.json", "Backend" ], .... } },

Now while packaging yarn package will create the native installer for the OS in the release folder in the root directory.

To make OS-specific build:

  • Linuxyarn package --linux
  • Windowsyarn package --win
  • macOSyarn package --mac

The final build doesn't have any dev-tool support, so enable dev-tool in build version:

  • Build debugyarn cross-env DEBUG_PROD=true yarn package

This installer will be enough to run the application on any system.

Worker Pool in Golang

Tags: #advance #topic #golang #goroutine #channels #workerpool #threadpool

Often we end up with some work which is so time-consuming that if we're able to assign, multiple person/worker, to do that job the execution time will reduce the time which will save a lot of time for those particular tasks.

Today we're going to solve this problem by creating a worker pool also known as thread pool so that tasks are done by multiple workers concurrently. We're particularly using Golang's lightweight thread also known as Goroutine & Channels.

Prerequisites: Goroutine, Channels

Goroutine

A goroutine is a lightweight thread managed by the Go runtime unlike other languages like Python who's threads are managed by OS and also expensive to run. So goroutines are functions or methods that run concurrently with other functions or methods.

Channels

Channels are ways in which different goroutines communicate with each other. We can understand them as pipes through which you can connect with different concurrent goroutines. The communication is bidirectional by default, meaning that you can send and receive values from the same channel.

Let's define some workers so that we can solve the time issue using goroutines and channels.

Task

func task() { time.Sleep(time.Second) // some task to be executed }

Job

Note: Each job takes 1 second to complete

func job(workerID, jobID int) { fmt.Println("worker", workerID, "started job", jobID) task() fmt.Println("worker", workerID, "finished job", jobID) }

Worker

func worker(workerID int, jobs <-chan int, results chan<- int) { for j := range jobs { job(workerID, j) results<- j * 2 } }

In Golang, we define a channel with the keyword `chan`. Anyone get confused when seeing those arrow signs with channels, let's simplified those first...

chan // read-write <-chan // read-only chan<- // write-only

So we can say without any arrow in the `chan` keyword would mean the channel can read-write which is the default behavior, but if we want a read-only channel we put an arrow sign before the `chan` keyword like `<-chan` and if we want a write-only channel we put an arrow sign after the `chan` keyword like `chan<-` this.

So for our example above the `jobs` channel only reads and the `results` channel only writes data.

So let's continue on our worker pool example...

Our worker function will receive the jobs and send the results of the job in the results channel.

We make the job function to execute the task function to simulate an actual task running by the worker.

In the `task` function we put a sleep function which will wait for a second so that it behaves like expensive/time-consuming work.

Create a int channel with buffer

func makeIntChannels(buffer int) chan int { channel := make(chan int, buffer) return channel }

Worker pool

func execUsingWorkerPool(numOfJobs, numOfWorkers int) { defer duration(track("time using worker pool")) jobs := makeIntChannels(numOfJobs) results := makeIntChannels(numOfJobs) for w := 1; w <= numOfWorkers; w++ { go worker(w, jobs, results) } for job := 1; job <= numOfJobs; job++ { jobs<- job } close(jobs) // closing the job channel to indicate that's all the work we have. for i := 1; i <= numOfJobs; i++ { <-results } }

Without worker pool

func execWithoutUsingWorkerPool(numOfJobs, worker int) { defer duration(track("time without using worker pool")) for j := 1; j <= numOfJobs; j++ { job(worker, j) } }

Calculate execution time

func track(msg string) (string, time.Time) { return msg, time.Now() } func duration(msg string, start time.Time) { log.Printf("%v: %v\n", msg, time.Since(start)) }

whoo!!! lots of code right...
Let's go through the `main` function to understand what's happening

Main function

func main() { const numOfJobs = 5 const numOfWorkers = 3 execUsingWorkerPool(numOfJobs, numOfWorkers) execWithoutUsingWorkerPool(numOfJobs, 1) }

In the main function, we're defining the number of jobs and workers as a const a value so that we can reuse them in the worker pool function and single worker pool function.
Let's check out the `execUsingWorkerPool` function to understand what's happening.

defer duration(track("time using worker pool"))

In the first line, we use the `defer` keyword, which means that when `execUsingWorkerPool` function executes all other statements in the function block & the last command will be executed would be defined in the `defer` statement, cool right...

`duration` & `track` function here is a util function which allows us to track the execution time. `track` function passed as a parameter in the duration function as in the Golang, this is called higher-order function or first-class citizen which means is a function can be assigned to a variable, pass as a parameter to other function and return a function from another function.

jobs := makeIntChannels(numOfJobs) results := makeIntChannels(numOfJobs)

Next line we define two int buffer channels as jobs & results. In order to use our pool of workers, we need to send them jobs and collect their results.

for worker := 1; worker <= numOfWorkers; worker++ { go worker(worker, jobs, results) }

Next line This starts up workers, for our example, we use 3 workers, initially blocked because there are no jobs yet.

for job := 1; job <= numOfJobs; job++ { jobs<- job } close(jobs) // closing the job channel to indicate that's all the work we have.

Next, we send a total of 5 jobs and then close the jobs channel to indicate, that’s all the work we have right now.

for i := 1; i <= numOfJobs; i++ { <-results }

Finally, we collect all the results of the jobs we define. This also ensures that the worker goroutines have finished all the workers.

If you like, you can read the same article on my Personal Blog

You can read our other blog-posts Here

You can read my other blog-posts Here

Output

worker 3 started job 1 worker 1 started job 2 worker 2 started job 3 worker 2 finished job 3 worker 2 started job 4 worker 3 finished job 1 worker 3 started job 5 worker 1 finished job 2 worker 3 finished job 5 worker 2 finished job 4 2021/03/18 09:25:25 time using worker pool: 2.000943787s worker 1 started job 1 worker 1 finished job 1 worker 1 started job 2 worker 1 finished job 2 worker 1 started job 3 worker 1 finished job 3 worker 1 started job 4 worker 1 finished job 4 worker 1 started job 5 worker 1 finished job 5 2021/03/18 09:25:30 time without using worker pool: 5.001234313s

In Conclusion, we can say using the worker pool, execution time reduces to 2+ seconds where without worker pool, it's taking 5+ seconds. Hopefully, After this, we understand what is a worker pool and how to create one in Golang, and the benefit of using a worker pool.

Working with DocuSign, Authorization and Sending Document for Signature

DocuSign is a well known platform where users can send their document for signing via email or your app. I will try to show you how DocuSign authorize an user and how can we send a document to users for signing electronically and we will do that programmatically.

To use DocuSign at first we need a free developer account. Go there and select Developer Account button in the top left then Create Account. Log into this account. After login you need to create an app for your integration. Go to My Apps and Keys there you will see your Integrations. Click ADD APP & INTEGRATION KEY button. Give a name and select Authorization Code Grant for User Application. In secret keys generate one and save them somewhere because you won't be able to see it again. In Redirect URLs Add one which will be needed when we make URLs for authorization. An Integration key will be generated for your app we need this later.

We will use Authorization Code Grant to authorize users. Authorization process has 2 steps first we need to obtain Authorization Code and 2nd using that Authorization Code we need to obtain access token using rest api call.

To get Authorization Code we need to generate a url where we need to redirect ours users to that url. There user will login to their DocuSign Account and grant access to our app. After user authorizes our app user will be redirected to our whitelisted redirect url with a Authorization Code. Below is the URL format.

https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature&client_id=7c2b8d7e-xxxx-xxxx-xxxx-cda8a50dd73f&state=a39fh23hnf23&redirect_uri=http://example.com/callback/

client_id is the integration key and redirect_uri is the url where DocuSign will redirect users with a Authorization Code. After Redirected URL will contain a parameter called code. We need this code to generate access token.

To Get Access Token we need to call a rest api and we need to set header. The authorization header will contain integration key and secret key separated by a colon and converted it to base64 with prefixed by a word Basic. We can generate this base64 easily by using browser console. In Console write this and enter

 btoa('INTEGRATION_KEY:SECRET_KEY')

it will show the base64 of the string. set this string in the Authorization Header. This is a POST method so I am using curl to request this endpoint.

curl --header "Authorization: Basic BASE64_OF_YOUR_INTEGRATION_AND_SECRET_KEY" --data "grant_type=authorization_code&code=AUTHORIZATION_CODE_FROM_DOCUSIGN" --request POST https://account-d.docusign.com/oauth/token

The response of this request will have access_token, refresh_token and expires_in . We need this access token to make every DocuSign API Call.

I am using C# as an example for how to send a document for signing. We need to add a C# library to make DocuSign api call. Search and install eSignature API via  Nuget Package Manager Which is made by DocuSign. First we need to make an envelope here is the Code example to  make an Envelope.

private EnvelopeDefinition MakeEnvelope(string signerEmail, string signerName, string ccEmail, string ccName) { string doc2DocxBytes = Convert.ToBase64String(System.IO.File.ReadAllBytes(Config.docDocx)); string doc3PdfBytes = Convert.ToBase64String(System.IO.File.ReadAllBytes(Config.docPdf)); // Create the envelope definition EnvelopeDefinition env = new EnvelopeDefinition(); env.EmailSubject = "Please sign this document set"; Document doc1 = new Document(); string b64 = Convert.ToBase64String(document1(signerEmail, signerName, ccEmail, ccName)); doc1.DocumentBase64 = b64; doc1.Name = "Order acknowledgement"; // can be different from actual file name doc1.FileExtension = "html"; // Source data format. Signed docs are always pdf. doc1.DocumentId = "1"; // a label used to reference the doc Document doc2 = new Document { DocumentBase64 = doc2DocxBytes, Name = "Battle Plan", // can be different from actual file name FileExtension = "docx", DocumentId = "2" }; Document doc3 = new Document { DocumentBase64 = doc3PdfBytes, Name = "Lorem Ipsum", // can be different from actual file name FileExtension = "pdf", DocumentId = "3" }; // The order in the docs array determines the order in the envelope env.Documents = new List<Document> { doc1, doc2, doc3}; // create a signer recipient to sign the document, identified by name and email // We're setting the parameters via the object creation Signer signer1 = new Signer { Email = signerEmail, Name = signerName, RecipientId = "1", RoutingOrder = "1" }; // routingOrder (lower means earlier) determines the order of deliveries // to the recipients. Parallel routing order is supported by using the // same integer as the order for two or more recipients. // create a cc recipient to receive a copy of the documents, identified by name and email // We're setting the parameters via setters CarbonCopy cc1 = new CarbonCopy { Email = ccEmail, Name = ccName, RecipientId = "2", RoutingOrder = "2" }; // Create signHere fields (also known as tabs) on the documents, // We're using anchor (autoPlace) positioning // // The DocuSign platform searches throughout your envelope's // documents for matching anchor strings. So the // signHere2 tab will be used in both document 2 and 3 since they // use the same anchor string for their "signer 1" tabs. SignHere signHere1 = new SignHere { AnchorString = "**signature_1**", AnchorUnits = "pixels", AnchorYOffset = "10", AnchorXOffset = "20" }; SignHere signHere2 = new SignHere { AnchorString = "/sn1/", AnchorUnits = "pixels", AnchorYOffset = "10", AnchorXOffset = "20" }; // Tabs are set per recipient / signer Tabs signer1Tabs = new Tabs { SignHereTabs = new List<SignHere> { signHere1, signHere2} }; signer1.Tabs = signer1Tabs; // Add the recipients to the envelope object Recipients recipients = new Recipients { Signers = new List<Signer> { signer1 }, CarbonCopies = new List<CarbonCopy> { cc1 } }; env.Recipients = recipients; // Request that the envelope be sent by setting |status| to "sent". // To request that the envelope be created as a draft, set to "created" env.Status = RequestItemsService.Status; return env; } // The HTML of the first document in the envelope used by our example is defined here private byte[] document1(string signerEmail, string signerName, string ccEmail, string ccName) { return Encoding.UTF8.GetBytes( " <!DOCTYPE html>\n" + " <html>\n" + " <head>\n" + " <meta charset=\"UTF-8\">\n" + " </head>\n" + " <body style=\"font-family:sans-serif;margin-left:2em;\">\n" + " <h1 style=\"font-family: 'Trebuchet MS', Helvetica, sans-serif;\n" + " color: darkblue;margin-bottom: 0;\">World Wide Corp</h1>\n" + " <h2 style=\"font-family: 'Trebuchet MS', Helvetica, sans-serif;\n" + " margin-top: 0px;margin-bottom: 3.5em;font-size: 1em;\n" + " color: darkblue;\">Order Processing Division</h2>\n" + " <h4>Ordered by " + signerName + "</h4>\n" + " <p style=\"margin-top:0em; margin-bottom:0em;\">Email: " + signerEmail + "</p>\n" + " <p style=\"margin-top:0em; margin-bottom:0em;\">Copy to: " + ccName + ", " + ccEmail + "</p>\n" + " <p style=\"margin-top:3em;\">\n" + " Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie. Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée. Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice. Donut jujubes oat cake jelly-o. Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.\n" + " </p>\n" + " <!-- Note the anchor tag for the signature field is in white. -->\n" + " <h3 style=\"margin-top:3em;\">Agreed: <span style=\"color:white;\">**signature_1**/</span></h3>\n" + " </body>\n" + " </html>" ); }

And this is the code for sending this envelope to DocuSign.

public EnvelopeSummary SendEnvelope(string signerEmail, string signerName, string ccEmail, string ccName) { var accessToken = ACCESS_TOKEN; var basePath = BASE_PATH + "/restapi"; var accountId = ACCOUNT_ID; EnvelopeDefinition env = MakeEnvelope(signerEmail, signerName, ccEmail, ccName); var apiClient = new ApiClient(basePath); apiClient.Configuration.DefaultHeader.Add("Authorization", "Bearer " + accessToken); var envelopesApi = new EnvelopesApi(apiClient); EnvelopeSummary results = envelopesApi.CreateEnvelope(accountId, env); RequestItemsService.EnvelopeId = results.EnvelopeId; return results; }

ACCESS_TOKEN is the token we got from the authorization step. BASE_PATH will be https://demo.docusign.net as this is for development purpose and you will find it in the admin dashboard (My Apps and Keys) Page. ACCOUNT_ID is the API Account Id which is also in the dashboard.  If our SendEnvelope method is called successfully , Signers will be notified via email that he/she has a document to Sign. Go through their official doc if you want to dive deeply to know other features also.

 

How to use Event Aggregator in Aurelia

In a frontend Application we sometimes need to send some message or notify other components to update UI based on some data. We can achieve this using Event Aggregator in Aurelia. I will try to show you how it works. We need to use Dependency Injection here. This is a way to create a singleton object instance of a class (service methods or utils) in constructor and use it inside that class.

To simply demonstrate Event Aggregator I am creating 2 components (Custom Elements) Message and Form. Message is just display a message property and Form component has one text area and a button. We want to pass whatever user enters in the textbox when user clicks on the button. See how the codes will look like for these 2 components.

Message.js

import { inject } from "aurelia-framework"; import { EventAggregator } from "aurelia-event-aggregator"; @inject(EventAggregator) export class Message { message = "Default Text"; constructor(eventAggregator) { this.eventAggregator = eventAggregator; this.eventAggregator.subscribe("UpdateMessage", (payload) => { this.message = payload; }); } } 

Message.html

<template> <div> ${message} </div> </template>

To use dependency injector we need to use inject annotation which is from aurelia-framework. We need to tell in the inject method which classes we will use to initialize. Those class instances will be passed in the constructor. We are using EventAggregator from aurelia-event-aggregator.In eventAggregator object we have subscribe method which takes 2 parameters 1st one is channel name as string (I used "UpdateMessage") and 2nd one is a function. If it publishes any messages in the same channel this function will be called.

Form.js

import { inject } from "aurelia-framework"; import { EventAggregator } from "aurelia-event-aggregator"; @inject(EventAggregator) export class Form { message = ""; constructor(eventAggregator) { this.eventAggregator = eventAggregator; } send = () => { this.eventAggregator.publish("UpdateMessage", this.message); }; }

Form.html

<template> <div> <input value.bind="message"/> <button click.delegate="send()">Send</button> </div> </template> 

Here we publish the message property of Form class to the UpdateMessage channel. publish method takes 1st parameter for Channel Name and 2nd parameter is for payload data which will be passed to the function of subscribe method.

Sometimes we need to refresh some components data based on some user actions. That's how  components can communicate with each other.

Why Golang?

blank

When we start learning a new language, we try to find the purpose of the language first so that we can decide if we're going to use that language in our project or not.

So when starting with Go, it's common that you'll hear that **" it's a systems language"** as this was one of the earlier descriptions of language by the Go team. It was constructed to combine the speed and power of languages like C with the syntactical elegance of modern interpreted languages like Python in mind.

From Go FAQ,

Why Go was created?

Go was born out of frustration with existing languages and environments for systems programming.

Go hasn't been considered a web language until recently as its main initial purpose to handle system-related tasks.

Now Go is web-ready out of the box, it lacks a lot of the critical frameworks and tools people so often take for granted with web development now. As the community around Go grew, the scaffolding began to manifest in a lot of new and exciting ways. Combined with existing ancillary tools, Go is now a wholly viable option for end-to-end web development.

Back to that primary question:

Why Go?

An honest answer to the above question, it's not right for every web project, but any application that can benefit from high-performance, scalable as the business grows, secure web-serving out of the box with the added benefits of a beautiful concurrency model would make for a good candidate.

We can summarize Golang's effectiveness in six points:

1. Concurrency
2. Scalability
3. Error Checking
4. Compiled Language
5. Garbage Collection
6. Cross-Platform

Concurrency

Millions of Platform Users

Go has many built-in features designed to handle several concurrent web requests due to which it is very efficient as compared to other legacy languages such as python etc

Scalability

Grow with the Business

As an enterprise grows the programs used will be required to do several things at the same time. Golang can easily scale due to its ability to handle several simultaneous tasks.

Error Checking

Nil/Null Malfunction

Go comes with a built-in error type. It uses error values to indicate an abnormal state while writing the code. The developer can spot errors leading to nil/null malfunction.

Compiled Language

Fast Performance

As a compiled language runs source code files through a compiler, which reads source code and generates a binary, or executable, file that is used to run the program. Examples of other popular compiled languages include C, C++, and Swift. This way performance is way faster than other languages like python etc.

If you like, you can read the same article on my Personal Blog

You can read my other blog-posts Here

Garbage Collection

Boost App Speed

Golang's collection pauses are as low as 100 microseconds. As a result, it is predictable, better performance, and fast loading time.

Cross-Platform

Low Investment

Golang performs well across various platforms such as Windows, Linux, Unix, Android, IOS, and other operating systems as well as cloud applications. This means businesses don't have to spend much on ensuring cross-platform functionality.

Custom Elements and Compose View in Aurelia

Custom elements are useful when we have large page content and we need to break the pages into small pieces of components which can be reusable in the other pages also. To create a custom element we just need to create a view template and optionally a view model for it. Using a custom element to a page or another element is to just require it in that page and add a custom html tag for this.

Say we have a page that displays users information users personal info, his address and the blog posts which was posted by them. If we put all the information in a single component it will look like similar as below

<template>   <div>     Users Personal Information   </div>   <div>     Users Contact Information   </div>   <div>     Blog Posts     <ul>       <li>Post 1</li>       <li>Post 2</li>       <li>Post 3</li>     </ul>   </div> </template>

Here its not a large amount of code but definitely it will be very large in real scenarios. There we can refactor this code and break these into 3 components like PersonalInfo, ContactInfo, UserBlogPost then it will look clean and easy to understand and it will be highly maintainable. Basically as a frontend developer you will deal with hundreds of components and composing them. It is conventional to put the reusable pieces of components in a separate folder called components/partials you can use whatever names you like. So I am creating these custom elements in components folder  as like below.

PersonalInfo.html

<template> <div> Users Personal Information </div> </template> 

ContactInfo.html

<template> <div> Users Contact Information </div> </template> 

UserBlogPost.html

<template> <div> Blog Posts <ul> <li>Post 1</li> <li>Post 2</li> <li>Post 3</li> </ul> </div> </template>

Create 3 view models for each of these also PersonalInfo.js , ContactInfo.js and UserBlogPost.js.Make a class with the same file name (its optional though but its good to have a view model also). Now we will include them in our main user details page. I am using app.html file for these. See how our app.html look like

<template> <require from="./components/PersonalInfo"></require> <require from="./components/ContactInfo"></require> <require from="./components/UserBlogPost"></require> <personal-info></personal-info> <contact-info></contact-info> <user-blog-post></user-blog-post> </template>

If you run it you will see the exact same result but our code now looks clean and we can easily reuse them into other pages also. Now see how we require and placed it in the template. we need to require it and specify its relative path don't need to specify the extension. After requiring it we need to tell where to place it by html tag (hyphened version of the file name). That's it you now know about custom elements.

Another way to use it by using <compose> attribute. Using compose attribute code will look like these for our app.html file

<template> <compose view-model="./components/PersonalInfo"></compose> <compose view-model="./components/ContactInfo"></compose> <compose view-model="./components/UserBlogPost"></compose> </template>

Output will be the same. Compose is good when you want to render dynamic element based on some criteria. You can put a variable in the view-model attribute and change the path of the component from parent View Model. That is a difference between compose and custom element.

Now I will show you how you can pass a variable to the child component. Say we have user object with firstName and lastName in our app.js (Parent Component). We want to pass its value to the PersonalInfo component (Child Component). To make it work we need to declare a variable in  PersonalInfo.js and make it bindable properties. see how it do that

PersonalInfo.js

import { bindable } from "aurelia-framework"; export class PersonalInfo { @bindable user; }

And In the child component view template we will display the firstName and lastName info.

PersonalInfo..html

<template> <div> ${user.firstName } ${user.lastName } </div> </template>

Now to pass user object from parent model we need to add an attribute in the html tag for custom element. so the code will be

<template> <require from="./components/PersonalInfo"></require> <require from="./components/ContactInfo"></require> <require from="./components/UserBlogPost"></require> <personal-info user.bind="user"></personal-info> <contact-info></contact-info> <user-blog-post></user-blog-post> </template>

See user.bind here user is the child component property name (it will be hyphened version of the name) and you have to specify which parent property it will pass as value. You can also bind a function like this way. To know more about custom elements go through their official docs .

Configuring Aurelia Router and Its Basics

Routing is necessary for users to navigate through the whole web site. To make SPA project routing in frontend is must. Configure aurelia router in a aurelia project is pretty easy. First Create a fresh project using aurelia-cli. Check out this post how to do that.

To configure router we need to add configureRouter method in app.js file. See this code below.

export class App {
configureRouter(config, router) {
this.router = router;
config.title = "Aurelia Router";
config.map([
{
route: ["", "home"],
name: "home",
moduleId: PLATFORM.moduleName("home")
},
{
route: "user/:id",
name: "user",
moduleId: PLATFORM.moduleName("user")
},     {
route: "about",
name: "about",
moduleId: PLATFORM.moduleName("about")
},
{
route: "contact",
name: "contact",
moduleId: PLATFORM.moduleName("contact")
},
]);
}
}

We will create 4 pages Home, User , About and Contact Page. For each page there will be 2 files with the same name one will be .js and the other will be .html. So we need 8 files for these 4 pages. File contents will be as follows

home.js

export class Home {}

home.html

<template>
<h1>Home Page</h1>
</template>

user.js

export class User {
userId = '';
activate(data){
this.userId = data.id;
}
}

Here we define active method which will be called and data object will contain route parameters value as an object.

user.html

<template>
<h1>User Page</h1>
<p>User ID ${userId}</p>
</template>

about.js

export class About {}

about.html

<template>
<h1>About Page</h1>
</template>

contact.js

export class Contact {}

contact.html

<template>
<h1>Contact Page</h1>
</template>

You will see we are mapping routes by using config.map . This accepts an array of route object which registers url and the page it will display. route is the url pattern and we can specify route parameter also by using :param in the pattern, name is the route name, moduleId is the component/page name.

config.title is just setting the value of html title tag and a router instance is passed in the function parameter which is setting the router variable which will be used to redirect and change url programmatically and for other stuff.

Now only one thing left we need to tell in app.html where our page will be displayed. there is a <router-view> tag for this. So Our app.html file will look like this.

<template>
<ul>
<li><a href="/">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
<router-view></router-view>
</template>

router-view will be replaced by actual page based on the url. Now If you run the project you will see something like this. We put some links in the li tag so user can navigate by clicking. You will notice there is a # tag in the url which tells aurelia that match url pattern after # and render view accordingly.

blank

If you click on the links (Home, About, Contact), corresponding page will be displayed and if you go to http://localhost:8080/#user/1 it will display user page with the route parameter value which was passed with url.

Check out my next post about Custom Elements

Getting Started with Aurelia

In VivaSoft Limited We have some Clients who want to develop their project using aurelia in the frontend. So recently I have learned Aurelia to work with a Client. It is similar with Angular and VueJS. Here I will try to show you how to start a project with aurelia and its basic structure.

Aurelia is a Javascript Frontend Framework. It is very simple to understand and learn. As It is a frontend framework its purpose is to render UI in the browser. We can easily make SPA Web Applications using Aurelia.

Before learning it you must know JS as it is a JS framework and You should know Modern ES Syntax (ES6). You must know HTML and CSS very well also as it deals with Web UI.

You need to have NodeJS installed in your Machine as Aurelia uses NPM as Dependency Manager. NPM will be installed with NodeJS.

After Installing NodeJS run the following command in your cmd/bash terminal to install Aurelia CLI tool.

npm install -g aurelia-cli

Using aurelia-ali we can create a basic project with all the things we need to develop a project with Aurelia. To create a project go to the folder where you want to create your project and run `au new` command in your terminal. Give a name to your project and enter then a couple of options will appear select Default Setup. When it asks for “if you would like to install your new project’s dependencies” Select Yes to install Dependencies. Wait for a few seconds until this process finishes. After Successfully created go to the project folder and run `npm start` . it will start the project and You will see a Hello World! Page at http://localhost:8080/ in your Browser.

Now open the project folder in your Code Editor (I prefer VS Code for JS Development). If you see the Project Folder Structure you will see a src folder basically all our codes will be there. There you will see an app.js and app.html file which is rendering Hello World! Page. In Aurelia every page will have 2 files one will be a .js file and the other will be a .html file with the same name. HTML file will contain the html and css code related to this page which will be used for styling and UI layout and js will contain a Class with properties and methods which will be used for event handling and show dynamic content based on properties. JS file is called View Model and HTML file is called View.

In the view file there will be a <template> tag basically all your html will be wrapped by this tag. To show a value of a JS property in the View use ${PROPERTY_NAME}. In our generated project you will see ${message}. Message property is the property defined in our App Class (View Model) and the value of this property will be displayed in the browser. We can use any js expression here. ${ANY_EXPRESSION}

I will try to show you some basic things in Aurelia like data binding and event handling by doing a TODO Application. I will use app.js and app.html for now. I have created 3 properties and 2 methods. Here is the code below for our App Class.

export class App { constructor() { this.heading = ‘Todos’; this.tasks = []; this.description = ‘’; } addTask() { if (this.description) { this.tasks.push({ description: this.description, done: false }); this.description = ‘’; } } removeTask(task) { let index = this.tasks.indexOf(task); if (index !== -1) { this.task.splice(index, 1); } } }

You can see that the first method will add a new task to our tasks property and the second one will remove a task which the user wants to delete. To capture which value user types for description we will use description property. Heading property will be used to show the Header text.

Our View file will look like this.

<template> <h1>${heading}</h1> <form submit.trigger=”addTask()”> <input type=”text” value.bind=”description”> <button type=”submit” disabled.bind=”!description”> Add Task </button> </form> </template>

I am using h1 tag to show the heading. I have created a form to get input from the user. You can see that input tag has value.bind property and the value is description. Basically thats how we tell aurelia that the description property will be used to store the value from user. This is called 2 way data binding because if we change description from js file it will display that value and If user changes input from browser the value will be changed also. For button disabled.bind will enable/disable this button based on description property. In form tag submit.trigger is binded with addTask method. When user submits this form addTask method will be called. There are many types of html properties in aurelia check out their doc (https://aurelia.io/)to know all the available properties and their behavior.

Now we need to show the list of tasks in a ul tag. We will loop through tasks array using repeat.for. This will repeat the li tag and display the values of a single task. See the code below.

<template> <h1>${heading}</h1> <form submit.trigger=”addTask()”> <input type=”text” value.bind=”description”> <button type=”submit” disabled.bind=”!description”> Add Task </button> </form> <ul> <li repeat.for=”task of tasks”> <input type=”checkbox” checked.bind=”task.done”> <span css=”text-decoration: ${task.done ? ‘line-through’ : ‘none’}”> ${task.description} </span> <button click.trigger=”removeTask(task)”>Remove</button> </li> </ul> </template>

click.trigger is used to handle click event and checked.bind is check/uncheck the check box based on done property of a task. If you run this project a form will be displayed in the browser. If user submits form after writing something in the text box a new task will be created and will be displayed in a list. User can delete a task by clicking remove button. And User can check/uncheck a particular task.

I will try to show you How to use Custom Element (Components) , Aurelia Router, Event Aggregator in later posts. Check out next post about Aurelia Router.