Categories
JavaScript Best Practices

JavaScript Best Practices — Naming, Style, and Comments

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are lots of tricky parts to JavaScript, and this means there are certain patterns or features we should avoid. We can follow some best practices to make our JavaScript code easy to read.

In this article, we’ll look at some best practices for styling code to be easy to read. We look at variable and function naming, avoiding global variables, keeping a strict coding style, and commenting code in moderation.

Easy, Short, and Readable Names for Variables, Functions, and Classes

Function names should be short and meaningful. For example, if we want to create a variable to store someone’s birthday, it should have a name that indicates that.

This means that it shouldn’t be a name like b, but it also shouldn’t be something with extraneous words like veryVeryVeryLongBirthdayName. Naming it birthday would be appropriate.

Also, something to avoid is combining value with functionality in names. For example, if we want to write a function to check if someone is over the age of majority, we should name it something like isAdult() instead is isOverEighteen().

This also makes more sense since not every place has age 18 as the age of majority.

We can also consider Hungarian notation for naming variables and functions. The notation combines both the return type and the name into one. Since JavaScript function doesn’t have a fixed return type, we can add a prefix for the type for the function.

For example, if we want to return a boolean with our isAdult() function, we can write bIsAdult() with the b indicating that it’s a boolean.

Keeping names in English is also a good idea since it keeps everything consistent with the rest of the code. All the reserved words like function and new are all in English, so it’s consistent to keep everything else in English.

English is also a more international language so it’s more likely that developers know English than not.

Classes are functions that construct other objects, so their naming should reflect that. For example, if we have a class that construct person objects, then they should be named Person.

Class names should be upper case and indicates what kind of object we’re creating from it.

Avoid Global Variables and Functions

In JavaScript, everything runs in the file runs in the same scope. This is a real problem because things can easily be accidentally modified and referenced unintentionally.

Fortunately, since ES6, JavaScript has several features to deal with this. One is the use of modules. With modules, only the things that we explicitly export are available for other modules to use.

JavaScript strict mode should also be enabled to prevent the accidental declaration of global variables. With strict mode something like x = 1 will cause an error without the keywords var, let, or const before it.

Also let and const are keywords for declaring block-scoped variables and constants respectively, so we can’t accidentally reference it outside a block.

For example, with var if we write the following code:

if (true) {
  var x = 1;
}
console.log(x);

We’ll see 1 logged from the console.log since it’s not block scoped.

On the other hand, if we use let instead of var:

if (true) {
  let x = 1;
}
console.log(x);

Then we get the error ‘Uncaught ReferenceError: x is not defined.’

As we can see, we can’t reference things in places that we don’t expect.

An alternative way would be to wrap the stuff we want to be private in a function.

For example, we can write:

(function() {
  var x = 1;
})();

console.log(x);

We will also get the error ‘Uncaught ReferenceError: x is not defined.’ A function that runs immediately after it’s declared as we have above is called an Immediately Invoked Function Expression or IIFE for short.

If we want to expose something to the outside in the IIFE we have above we can just return what we want as follows:

let foo = (function() {
  var x = 1;
  return {
    bar: 2
  }
})();
console.log(foo.bar);

We returned an object with the bar property and assigned it to foo. Then we should get 2 from the console.log output.

Photo by Mohau Mannathoko on Unsplash

Keep a Strict Coding Style

Every developer in the team will have their own style if they decide how to style the code.

This means that the code will have different styles in different places, making the code hard to read.

Because of that, it’s a good idea to keep the style consistent. We can style our code automatically with programs like ESLint or JSLint, which does static checks on our code to make sure that it sticks to the style that’s set.

There’re also tools like Prettier to format the code in a consistent way through our codebase.

Clean code is easy to read and maintain. It shouldn’t have hacky things that make it hard to read and debug.

Comment When Needed

While lots of code are self-explanatory, some code needs to be clarified. For example, if the code is part of a complex system, then it might need some clarification with comments.

A simple function that’s a few lines long probably don’t need much explanation.

If we write comments, we should use multi-line comments /* */ instead of single-line comments // since they’re more versatile.

One handy trick for comments is to write it with the double slashes before closing the multi-line comments as follows:

/*
  function foo(){
    return 1
  };
// */

This way, we can uncomment the code by putting a slash before the open slash in the first line to uncomment the code:

//*
  function foo(){
    return 1
  };
// */

The code above is uncommented.

Many text editors should also be smart enough to comment and uncomment JavaScript code automatically.

Variable and functions should be named in a way that indicates what they store or do respectively. Classes should start with a capital letter in general and they should be named for the kind of object that they instantiate.

We should avoid global variables since they can be referenced accidentally and cause unintentional modification. We can keep things private inside functions or modules.

To make code easy to read. It’s important to keep a strict coding style so that we don’t get any confusion when reading code.

Finally, we can comment on code in moderation if there’re parts that are harder to understand.

Categories
JavaScript Best Practices

JavaScript Best Practices: Objects

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There’re lots of tricky parts to JavaScript, so there’re many things to avoid. We can follow some best practices easily to make our JavaScript code easy to read.

In this piece, we look at how to deal with objects in an easily maintainable way. We go through the traps that we can fall into when we define objects.


Use Literals for Primitive Values

In JavaScript, there’re multiple ways to declare primitive variables. Primitive variables include variables of any type other than objects.

One way is to use literals like so:

let x = 1;  
let y = true;  
let z = '';

In the code above, we set the literal value for number, boolean, and string to each variable.

Alternatively, we can write:

let x = new Number(1);  
let y = new Boolean(true);  
let z = new String('');

We can also write:

let x = Number(1);  
let y = Boolean(true);  
let z = String('');

Of the three ways to declare primitive variables above, some ways are better than the others. The best ways are to set the literals directly. The other way is to use the functions as we have in the third example.

Why shouldn’t we use the constructor to create variables with primitive values? First, anything defined with the new operator will have the type ‘object’ even though they have primitive values. This makes comparison between these objects difficult.

For example, if we write:

new String('foo') === new String('foo')

We get false because they’re both of type ‘object’, and === will evaluate to false if two objects don’t have the same reference in JavaScript. This means comparison is difficult.

Comparing with == will also evaluate to false for the same reason.

Since they’re both of type ‘object’, it’s harder to know whether they’re actually strings, booleans, or numbers.

The other two ways are much better because they’ll get us the right type. For example, the following will get us the type ‘number’:

let x = 2;  
console.log(typeof x);

This also applies to other primitive data types.

There’re no reasons to use the new operator to declare things with primitive values. It just makes life harder.

Using new is also slower since the JavaScript interpreter has to do one more operation than necessary to declare something that has a primitive of type ‘object’.

The Number , String and Boolean functions are useful for converting objects from one type to another. For example, if we have:

let x = '2';

Then we can convert it to a number with a Number function as follows:

let y = Number(x);

Use Literals to Declare Objects Whenever They Exist

Some objects have literals associated with them. For example, arrays have the [...] literal. Regular expressions can be declared by surround patterns with slashes. Functions can be declared with the function keyword or using the fat arrow.

It’s confusing to define values with constructors sometimes. For example, arrays have two constructors. One has one parameter with the array length as the parameter. The other is a comma-separated list of entries.

This means that Array(1) will get us an empty array with length 1 and no content. On the other hand, Array(1,2,3) will get us [1,2,3].

As we can see, it’s just not as clear to declare an array by using the constructor.

For functions, we have the function keyword or Function constructor.

Using the Function constructor doesn’t make sense since we have to pass strings that have the code for our functions and strings for the parameter names. It opens things up for code injection attacks, and it’s a pain to write code as a string.

The function keyword is much more clear. And it lets us write code that’s recognized by text editors as function code. There’s no reason to declare a function with the Function constructor unless we want to define a function that has dynamic code.

Likewise, the RegExp constructor is good for constructing regular expression objects dynamically, but otherwise is the same as the regular expression literal. The regular expression literal and constructor are the same for static regular expressions, so there’s some use for the constructor.

The Object constructor just makes us type more code than object literals; otherwise, they’re the same. This means it’s kind of pointless to use it to declare objects.


Automatic Type Conversions

For primitive values, JavaScript can convert things to different types depending on the context.

For example, suppose we have:

1 == '1'

Then the string 1 will be converted to a number.

Suppose we have:

1 + '1'

Then the number 1 will be converted to a string so that we get '11'. JavaScript just assumes that we’re concatenating.

On the other hand, suppose we write:

1 - '1'

We get 0 because it assumes that we’re subtracting two numbers.

Suppose we write:

1 - 'a'

Because the result isn’t a number, we get NaN since we can’t subtract a number with a non-numeric string.

In an expression that evaluates to a boolean, the variables or values inside are evaluated to their truthy or falsy values.

Falsy values include:

  • 0
  • null
  • undefined
  • empty string
  • false
  • NaN

Everything else is truthy. Suppose we have the following:

0 || undefined || null || 1

We get 1. The JavaScript interpreter evaluates all the falsy values and returns 1 since that’s the last thing left.

To make sure we get the types we expect, we should convert everything to the type that we actually expect or we should check for the type.

To convert primitive values like numbers, booleans, and strings, we can use the Number , Boolean , and String functions respectively. We just pass in whatever objects to these functions.

We can also use the + sign before something to convert it to a number.

!! also converts things to boolean according to their truthiness. The first exclamation mark converts the value according to its truthiness and then negates it. Then the second exclamation mark converts it back to the original truthiness value.

To check the type of primitive values, we can use the typeof operator. However, note that null has the type ‘object’. Everything has the type that we expect.


In most cases, object literals are the clearest way to define objects. The only exceptions are when we need functions with dynamically generated code or regular expressions that are dynamically generated. We should never use the new keyword to declare things that have primitive values. Finally, we should be careful of automatic type conversions and check the types or convert them according to our needs.

Categories
JavaScript Best Practices

JavaScript Best Practices — Unexpected Values and More

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are lots of tricky parts to JavaScript, and there are many things to avoid. We can follow some best practices to make our JavaScript code easy to read.

In this article, we look at the best way to compare objects and function parameters, adding a default value to switch statements and function parameters and avoiding the use of the eval function.

Use === For Comparing Objects

In JavaScript, there are 2 operators for equality comparison. One is the == operator and the other is the === operator. == compares the operands by converting them to the same type automatically.

=== compares by both comparing the content and the type of both operands.

We want to use === because it compares both the type and the content so that we won’t get any mismatch in the type.

For example, if we use the == operator, we can the following expressions evaluated to true :

0 == "";  
0 == null;  
0 == undefined;  
null == undefined;  
1 == "1";

In the first 4 lines, everything is falsy so they’re all converted to false before comparing for equality with the == operator.

In the last line, '1' is converted to 1 before comparison so that we get the value true returned.

To avoid these confusing results, we can use the === operator, which compares both the type and the content of the values. With ===, everything above will be evaluated to false .

If we want to compare things with the same content and different types with the === operator, we can convert them to the same type first.

For example, if we want compare 1 and '1' , we can write:

+1 === +'1'

Then they’ll both be converted to 1 and the expression will evaluate to true.

This way, we don’t get anything with unexpected types being compared.

Default Parameters

JavaScript functions can take in any number of parameters even though they have their own signatures. This means that we can pass in fewer parameters than we expect.

With ES6, we get the default parameter value feature. This lets us set the default value for function parameters.

For example, we can write:

const add = (a, b = 0) => a + b;

Then we don’t have to pass in the second argument for the function to return a numerical value. If we write:

console.log(add(1));

We get 1 since the value of b will be set 0 if nothing is passed, so we get 1 + 0 which is 1.

Photo by Kristina Paparo on Unsplash

Add Default Value for Switch Statements

We should always add a default block to switch statements, since we might have unexpected values being passed into the switch statement.

For example, we should write:

const getDayName = (dayOfWeek) => {  
  let day;  
  switch (dayOfWeek) {  
    case 0:  
      day = "Sunday";  
      break;  
    case 1:  
      day = "Monday";  
      break;  
    case 2:  
      day = "Tuesday";  
      break;  
    case 3:  
      day = "Wednesday";  
      break;  
    case 4:  
      day = "Thursday";  
      break;  
    case 5:  
      day = "Friday";  
      break;  
    case 6:  
      day = "Saturday";  
      break;  
    default:  
      day = "Unknown";  
  }  
  return day;  
}

To get the day of the week, because we can pass in anything to the getDayName function. For example, we can write:

console.log(getDayName(0));  
console.log(getDayName(1));  
console.log(getDayName(1000));

If we pass in 1000, we need to handle that, so we need a default value.

Don’t Use Eval

The eval function accepts a string that contains JavaScript code, which will be run by this function.

We shouldn’t use it because it lets code from unknown sources run, which is a big security risk.

Also, it’s also slower than normal code since it’s interpreted on the fly. This is because the code can’t be optimized by JavaScript engines.

Modern interpreters convert code to machine code, which can’t be done with code passed into eval . This means that the interpreter has to do variable lookups on the fly to set things to the right variables.

Any operations that are done to variables will force the browser to re-evaluate the code again if it’s running code passed into the eval function.

Conclusion

When we compare objects for equality, we should use the === operator since it compares both the content and the type of the object. Also, we should put default values for parameters in case they aren’t passed into the function when it’s called. Also, we should add a default block to switch statements to deal with unexpected values. Finally, don’t use the eval function because it’s a big security that allows people to inject malicious code to our programs and it’s slow to run code with eval .

Categories
JavaScript Best Practices

JavaScript Best Practices — Variables

Like any other programming language, JavaScript has its own list of best practices to make programs easier to read and maintain. There are lots of tricky parts to JavaScript, so there are many things to avoid.

We can follow some best practices to make our JavaScript code easy to read.

In this article, we look at how to declare variables in an easy-to-read way. Also, we look at ways to avoid declaring global variables and hiding private variables from the outside.


Avoid Global Variables

We should avoid the use of global variables as much as possible for various reasons.

One is that they’re easy to overwrite in different places since they’re available everywhere. They can also overwrite things in the window object since global variables are properties of the window object.

These two are real issues that make our code hard to follow. Therefore, we should define local variables as much as possible. We can define local variables by using the var, let, or const keywords.

Variables defined with var are available at the level where they are defined and below before they are defined. For example, if we write:

Then we get undefined for the first console.log and one for the second log.

It’s the same as writing:

Variables declared with let are available only after they’re defined, so if we gave:

We get the error:

Uncaught ReferenceError: Cannot access ‘x’ before initialization

With the const keyword, we define constants that can only be assigned once and never again. It’s also available only after it’s declared, unlike var.

For example, we can write:

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

const x = 1;
log();


Calling `log` before `const x = 1` will also get us:

Uncaught ReferenceError: Cannot access ‘x’ before initialization


If we want variables that are available in different parts of a program, we should use JavaScript modules and then build the modules into one or several large files when we release our code. This is available since ES6.

We can `export` our variables and `import` it in other modules. There’s also `export default` to export the whole module. This way, we only export things that should be available outside the module and keep everything else private.

We can also use closures to keep variables inside a function so they can’t be accessed outside. An example of a simple closure would be:

const multiply = () => {
const x = 3;
return () => x * 2;
}


We keep `x` inside the `multiply` function so that it can’t be accessed outside and return a function that does something with it.

Then, we call it by writing:

console.log(multiply()());


* * *

### Always Declare Local Variables

As a corollary, we should always declare local variables. We should always declare local variables and constants with `var`, `let`, or `const`.

Otherwise, it’ll be declared as a global variable as a property of `window` which we definitely don’t want.

For example, we should never write:

x = 1;


Instead, we should write:

let x = 1;


Fortunately, JavaScript strict mode doesn’t allow undeclared variables, so we can’t accidentally create global variables.

Also, JavaScript modules have strict mode enabled by default so we also can’t define global variables accidentally in there.

![](https://cdn-images-1.medium.com/max/800/0*XDF6_FrkR3Js4-Cw)Photo by [Francesco De Tommaso](https://unsplash.com/@detpho?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)

* * *

### Putting Variables and Constant Declarations on Top

Putting variable and constant declarations on top makes our code cleaner as everything is on top. It also stops us from accidentally referencing things declared with `let` or `const` before they are defined.

If strict mode is off, we also avoid defining global variables. And we also avoid accidental re-declarations of variables. Re-declarations should be caught by many text editors, but it’s still a possibility.

We can make multiple variable declarations in one line by separating variable names with commas. For example, we can write:

let x, y;
x = 1;
y = 1;
console.log(x, y);


Then we get:

1 1


From the `console.log`.

We can do the same for loops:

let i;

for (i = 0; i < 10; i++) {  
  console.log(i);  
}
```

* * *

### Initialize Variables When Declaring Them

It’s a good idea to initialize our variables or constants with values when declaring them. It prevents undefined value errors, and we can both declare the variable or constant and set an initial value to it all in one line.

Otherwise, we have one line for declaring the variable or constant and another line for setting its value.

For example, we can write:

```
let x = 1,  
  y = 1;
```

To declare both `x` and `y`.

This is much shorter than writing:

```
let x;  
let y;  
x = 1;  
y = 1;
```

We can declare and initialize multiple variables by separating each declaration and assignment with a comma.

* * *

### Conclusion

Here are some basic best practices for declaring and initializing variables.

We looked at how to declare variables cleanly and how to avoid declaring global variables. There is a lot more to writing clean JavaScript programs so stay tuned for more.
Categories
Angular

How to Make Android Text Based Games with Ionic

Ionic is a hybrid application framework that converts a web app into an Android or iOS app. It is handy for making mobile applications quickly with decent performance. It can easily be extended to create simple text games for entertainment.

If you don’t know how to create apps with Angular and Ionic, see:

In this story, we will build a game where you guess the number until you guessed it right, akin to the Bulls and Cows game or Mastermind game. The game provides you with a number guessing game, and you can change the settings for the number of digits of the number that you’re guessing. It will also be able to save the fastest time.

Building the Game

As usual, we start with the boilerplate code.

To start building, we can use Ionic’s CLI. If you used Angular CLI, then it should be familiar to you. To install the Ionic CLI, run npm install -g ionic.

Then to make generate skeleton code for our app, run ionic start guess-the-number-game sidemenu . guess-the-number-game is our app’s name and sidemenu specifies that we want a side menu in our app.

We need to install some libraries. We can do that by running npm i moment random-js .

Now we can build the game. We make 2 pages by running ionic g component HomePage , andionic g SettingsPage . After that we get 2 empty pages.

In home-page.component.ts , we replace the default code with the following:

import { Component, OnInit } from '@angular/core';
import { Random, browserCrypto } from "random-js";
import * as moment from 'moment';
import { NgForm } from '@angular/forms';
declare const require: any;
const words = require('an-array-of-english-words');

@Component({
  selector: 'app-home-page',
  templateUrl: './home-page.component.html',
  styleUrls: ['./home-page.component.scss'],
})
export class HomePageComponent implements OnInit {
  question: any = <any>{};
  answer: string;
  timer;
  started: boolean;
  rightNumber: number;
  inputtedAnswers: string[] = [];
  elapsedTime;
  message: string;
  numDigits: number;

constructor() { }

  ngOnInit() {
    if (!localStorage.getItem('numDigits')) {
      localStorage.setItem('numDigits', '4');
    }
  }

  ngOnDestroy() {
    this.stop();
  }

  start() {
    this.message = '';
    this.started = true;
    const random = new Random(browserCrypto);
    this.numDigits = +localStorage.getItem('numDigits');
    this.rightNumber = random.integer(Math.pow(10, (this.numDigits - 1)), Math.pow(10, this.numDigits) - 1);
    console.log(this.rightNumber);
    let numSeconds = 0;
    this.timer = setInterval(() => {
      numSeconds++;
      this.elapsedTime = moment.utc(numSeconds * 1000).format('H:mm:ss');
    }, 1000)
  }

  recordFastestTime() {
    const currentFastTime = moment(this.getFastestTime(), 'H:mm:ss');
    const newFastestTime = moment(this.elapsedTime, 'H:mm:ss')
    if (currentFastTime > newFastestTime) {
      localStorage.setItem(
        'fastestTime',
        this.elapsedTime
      );
    }
  }

  stop() {
    this.started = false;
    this.inputtedAnswers = [];
    clearInterval(this.timer);
  }

  checkAnswer(answerForm: NgForm) {
    if (answerForm.invalid) {
      return;
    }
    this.inputtedAnswers.push(this.answer);
    if (+this.answer == this.rightNumber) {
      this.stop();
      this.recordFastestTime();
      this.message = `You win! The correct number is ${this.rightNumber}`;
    }
    this.answer = '';
  }

  getFastestTime() {
    return localStorage.getItem('fastestTime') || '1000000:00:00';
  }

  getNumBullsandCows(answer: number) {
    const rightAnsStr = this.rightNumber.toString().split('');
    const answerStr = answer.toString().split('');
    const numBulls = rightAnsStr.filter((r, i) => rightAnsStr[i] == answer[i]).length;
    const numCows = answerStr.length - numBulls;
    return `${numBulls} bulls, ${numCows} cows`;
  }
}

The code above will generate the number you guess according to the numDigits setting stored in local storage. By default, the number will have 4 digits. After the number is generated, it will start a timer to start calculating the time. Both are in the start function.

inputtedAnswers store the answers you entered. The checkAnswer function checks if you entered anything, then calls the stop function which calls clearInterval on the timer object returned by the setInterval function if your answer is right. The getNumBullsandCows function checks which digits are right and which one are wrong. numBulls is the number of correct digits and numCows the number of wrong digits in your guess. When the guess is right, the fastest time will be computer by comparing with the last fastest time, which is store in local storage by calling the recordFastestTime function and if the current elapsed time is shorter than the saved one, will save the shorter one into the local storage.

Then in home-page.component.html , replace what’s there with the following:

<form #answerForm='ngForm' (ngSubmit)='checkAnswer(answerForm)'>
  <ion-list>
    <ion-item no-padding *ngIf='started' id='instruction'>
      How to Play: Guess the correct number. The closeness to the correct number will be indicated by the number of
      bulls and cows. The number of bulls means the number of digits with the right value and position in your guess.
      The number of cows is the
      number of digits with the wrong value and position in your guess.
    </ion-item>
    <ion-item no-padding *ngIf='started && getFastestTime()'>
      <ion-label class="ion-float-left">
        <h2>Your Fastest Time {{getFastestTime()}}</h2>
      </ion-label>
    </ion-item>
    <ion-item no-padding *ngIf='started'>
      <ion-label class="ion-float-left">
        <h2>Time {{elapsedTime}}</h2>
      </ion-label>
    </ion-item>
    <ion-item no-padding *ngIf='started'>
      <ion-label>Answer</ion-label>
      <ion-input [(ngModel)]='answer' #ans='ngModel' name='ans' required type="text" pattern="d*"
        [minlength]='numDigits' [maxlength]='numDigits'></ion-input>
    </ion-item>
  </ion-list>
  <ion-button type='submit' *ngIf='started'>Answer</ion-button>
  <ion-button (click)='start();' *ngIf='!started'>Start Game</ion-button>
  <ion-button (click)='stop(); started = false' *ngIf='started'>Stop Game</ion-button>
</form>
<ion-content [scrollEvents]="false">
  <ion-list *ngIf='started'>
    <ion-item no-padding *ngFor='let a of inputtedAnswers; let i = index'>
      <ion-label>
        {{i+1}}
      </ion-label>
      <ion-label>
        {{a}}
      </ion-label>
      <ion-label>
        {{getNumBullsandCows(a)}}
      </ion-label>
    </ion-item>
  </ion-list>
  <ion-list *ngIf='message'>
    <ion-item no-padding>
      {{message}}
    </ion-item>
  </ion-list>
</ion-content>

This creates a form for you to enter your number guess and list your guesses, along with the number of right and wrong digits for each guess.

Next we build the settings page. To do this, add the following to settings-page.component.ts :

import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { ToastController } from '@ionic/angular';

@Component({
  selector: 'app-settings-page',
  templateUrl: './settings-page.component.html',
  styleUrls: ['./settings-page.component.scss'],
})
export class SettingsPageComponent implements OnInit {
  settings: any = <any>{};
  fastestTime: string;

  constructor(
    public toastController: ToastController
  ) { }

  ngOnInit() {
    if (!localStorage.getItem('numDigits')) {
      localStorage.setItem('numDigits', '4');
    }
    this.settings.numDigits = +localStorage.getItem('numDigits');
    this.fastestTime = localStorage.getItem('fastestTime');
  }

  save(settingsForm: NgForm) {
    if (settingsForm.invalid) {
      return;
    }
    localStorage.setItem('numDigits', this.settings.numDigits || 4);
    this.presentToast();
  }

  clear() {
    localStorage.removeItem('fastestTime');
    this.fastestTime = localStorage.getItem('fastestTime');
  }

  async presentToast() {
    const toast = await this.toastController.create({
      message: 'Your settings have been saved.',
      duration: 2000
    });
    toast.present();
  }
}

In settings-page.component.html , we replace what’s there with:

<ion-content [scrollEvents]="true">
  <form #settingsForm='ngForm' (ngSubmit)='save(settingsForm)'>
    <ion-list>
      <ion-item no-padding>
        <ion-label>Number of Digits</ion-label>
        <ion-range min="4" max="8" [(ngModel)]='settings.numDigits' #numDigits='ngModel' name='numDigits'>
          <ion-label slot="start">4</ion-label>
          <ion-label slot="end">8</ion-label>
        </ion-range>
      </ion-item>
      <ion-item no-padding>
        <ion-label>Fastest Time</ion-label>
        <ion-label>{{fastestTime}}</ion-label>
        <ion-button (click)='clear()'>Clear Score</ion-button>
      </ion-item>
    </ion-list>
    <ion-button type='submit'>Save</ion-button>
  </form>
</ion-content>

You can clear local storage from the settings form and set the number of digits of the number you guess in this page. The numDigits control is a slider.

In app-routing.module.ts , add:

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { HomePageComponent } from './home-page/home-page.component';
import { SettingsPageComponent } from './settings-page/settings-page.component';

const routes: Routes = [
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full'
  },
  {
    path: 'home',
    component: HomePageComponent
  },
  {
    path: 'settings',
    component: SettingsPageComponent
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule {}

In app.component.ts , add:

import { Component } from '@angular/core';

import { Platform } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  public appPages = [
    {
      title: 'Home',
      url: '/home',
      icon: 'home'
    },
    {
      title: 'Settings',
      url: '/settings',
      icon: 'settings'
    }
  ];

constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar
  ) {
    this.initializeApp();
  }

initializeApp() {
    this.platform.ready().then(() => {
      this.statusBar.styleDefault();
      this.splashScreen.hide();
    });
  }
}

The last 2 files will add the menu entries for the side menu and allow us to visit the pages that we created.

In app.module.ts , write:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular")';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { HomePageComponent } from './home-page/home-page.component';
import { AdMobFree } from '@ionic-native/admob-free/ngx';
import { SettingsPageComponent } from './settings-page/settings-page.component';
import { FormsModule } from '@angular/forms")';

@NgModule({
  declarations: [
    AppComponent,
    HomePageComponent,
    SettingsPageComponent
  ],
  entryComponents: [],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    FormsModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

This is the final wiring that allows all the pages to be seen.