Categories
JavaScript TypeScript

TypeScript Advanced Types — Literal Types and Discriminated Unions

TypeScript has many advanced type capabilities and which makes writing dynamically typed code easy. It also facilitates the adoption of existing JavaScript code since it lets us keep the dynamic capabilities of JavaScript while using the type-checking capability of TypeScript. There’re multiple kinds of advanced types in TypeScript, like intersection types, union types, type guards, nullable types, and type aliases, and more. In this article, we’ll look at intersection and union types.

Intersection Types

An intersection type lets us combine multiple types into one. The structure of an object that has an intersection type has to have both the structure of all the types that form the intersection types. It’s denoted by an & sign. All members of all the types are required in the object of an intersection type.

For example, we can use the intersection type like in the following code:

interface Animal {  
  kind: string;  
}

interface Person {  
  firstName: string;  
  lastName: string;  
  age: number;  
}

interface Employee {  
  employeeCode: string;  
}

let employee: Animal & Person & Employee = {  
  kind: 'human',  
  firstName: 'Jane',  
  lastName: 'Smith',  
  age: 20,  
  employeeCode: '123'  
}

As we can see from the code above, each type is separated by an & sign. Also, the employee object has all the properties of Animal , Person , and Employee . Each property has a type that’s defined in each interface. If the structure doesn’t match exactly, then we’ll get error messages like the following from the TypeScript compiler:

Type '{ kind: string; firstName: string; lastName: string; age: number; }' is not assignable to type 'Animal & Person & Employee'.Property 'employeeCode' is missing in type '{ kind: string; firstName: string; lastName: string; age: number; }' but required in type 'Employee'.(2322)input.ts(12, 3): 'employeeCode' is declared here.

The error above occurs if we have the following code:

let employee: Animal & Person & Employee = {  
  kind: 'human',  
  firstName: 'Jane',  
  lastName: 'Smith',  
  age: 20    
}

TypeScript looks for the employeeCode property since the employeeCode property is in the Employee interface.

If 2 types have the same member name but different type, then it’s automatically assigned the never type, when they’re joined together as an intersection type. This means that we can’t assign anything to it. For example, if we have:

interface Animal {  
  kind: string;  
}

interface Person {  
  firstName: string;  
  lastName: string;  
  age: number;  
}

interface Employee {  
  employeeCode: string;  
  age: string;  
}

let employee: Animal & Person & Employee = {  
  kind: 'human',  
  firstName: 'Jane',  
  lastName: 'Smith',  
  age: 20  
}

Then we get the following error from the TypeScript compiler:

Type 'number' is not assignable to type 'never'.(2322)input.ts(8, 3): The expected type comes from property 'age' which is declared here on type 'Animal & Person & Employee'

If we omit the property then the compiler will also raise an error about the age property being missing. Therefore, we should never have types that have the same member name if we want to create intersection types from them.

Union Types

Union types create a new type that lets us create objects that have some or all of the properties of each type that created the union type. Union types are created by joining multiples with the pipe | symbol.

For example, we can define an object that has a union type like in the following code:

interface Animal {  
  kind: string;  
}

interface Person {  
  firstName: string;  
  lastName: string;  
  age: number;  
}

interface Employee {  
  employeeCode: string;  
}

let employee: Animal | Person | Employee = {  
  kind: 'human',  
  firstName: 'Jane',  
  lastName: 'Smith',  
  age: 20    
}

The code above has an employee object of the Animal | Person | Employee type which means that it can have some of the properties of the Animal, Person, or Employee interfaces. Not all of them have to be included, but if they’re included, then the type has to match the ones in the interface.

With union types, we can have 2 types that have the same member name but with different types. For example, if we have the following code:

interface Animal {  
  kind: string;  
}

interface Person {  
  firstName: string;  
  lastName: string;  
  age: number;  
}

interface Employee {  
  employeeCode: string;  
  age: string;  
}

let employee: Animal | Person | Employee = {  
  kind: 'human',  
  firstName: 'Jane',  
  lastName: 'Smith',  
  age: '20'  
}

Then we can assign both a number or a string to the age property. This fits with the dynamic nature of JavaScript while letting us assign data types to objects. This is different from traditional object-oriented code where we may abstract common members into a parent class and then derive sub-classes from it which has specialized members.

The pipe symbol means the object of a union type can take on none, some, or all properties of each type.

Accessing members in an object of a union type is different from how members are accessed from an intersection type. Objects that have intersection types have to have all the properties listed in the members of each type, so logically, we can access all the properties that are defined in the object. However, this isn’t the case with union types since some members may be available to only some of the types that make up the union type.

If we have a union type, then we can only access members that are available in all the types that form the union type. For example, if we have:

interface Animal {  
  kind: string;  
}

interface Person {  
  firstName: string;  
  lastName: string;  
  age: number;  
}

interface Employee {  
  employeeCode: string;  
  age: string;  
}

let employee: Animal | Person | Employee = {  
  kind: 'human',  
  firstName: 'Jane',  
  lastName: 'Smith',  
  age: '20'  
}

console.log(employee)

Then we can’t access any properties of the employee object since none of the members are available in all the types. If we try to access a property like the kind property, we’ll get the following error:

Property 'kind' does not exist on type 'Animal | Person | Employee'.Property 'kind' does not exist on type 'Person'.(2339)

If we want to make some property accessible, we can write something like the following code to let us access a property:

interface Animal {  
  kind: string;  
}

interface Person {  
  kind: string;  
  firstName: string;  
  lastName: string;  
  age: number;  
}

interface Employee {  
  kind: string;  
  employeeCode: string;  
}

let employee: Animal | Person | Employee = {  
  kind: 'human',  
  firstName: 'Jane',  
  lastName: 'Smith',  
  age: 20,  
  employeeCode: '123'  
}

console.log(employee.kind)

In all 3 interfaces above, we have the kind member. Since they’re in all 3 interfaces, which we used in the union type, we can access the employee.kind property. Then we would get the text ‘human’ in the console.log statement.

An intersection type lets us combine multiple types into one. The structure of an object that has an intersection type has to have both the structure of all the types that form the intersection types. It’s formed by joining multiple types by an & sign. Union types create a new type that lets us create objects that have some or all of the properties of each type that created the union type. Union types are created by joining multiples with the pipe | symbol. It lets us create a new type that has some of the structure of each type that forms the union type.

Categories
JavaScript Vue

Render Elements Conditionally with Vue.js with v-if

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at various ways to render items conditionally on the screen with Vue.js by using the v-if , v-else-if and v-else directives.

v-if

The v-if directive is used to conditionally render a block. The block with the v-if attribute will be rendered only if the directive’s expression returns a truthy value.

For example, in src/index.js we put:

new Vue({  
  el: "#app",  
  data: {  
    show: true  
  }  
});

Then in index.html , we put:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <div v-if="show">  
        Hi  
      </div>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then Hi will be shown since show is set to true .

We can also use v-else in addition to v-if if we want to show something if an expression returns false .

For example, if we change true to false in src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    show: false  
  }  
});

Then in index.html , if we have:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <div v-if="show">Hi</div>  
      <div v-else>Bye</div>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we see Bye because show is set to false and we have v-else directive added to an element that is the sibling of the element that immediately follows the element with thev-if directive.

v-if on template Elements to Toggle Groups of Elements

We can add v-if to a group of elements by wrapping them around a template element.

For example, we can write:

<template v-if="show">  
  <p>foo</p>  
  <p>bar</p>  
</template>

Then foo and bar will be shown if show is set to true .

v-else

We can use the v-else directive to display something when the expression in the v-if directive in a sibling element that immediately follows the element with v-if returns false .

For example, we can display messages randomly as follows. In index.html , we put:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <div v-if="Math.random() > 0.5">  
        Hi  
      </div>  
      <div v-else>  
        Bye  
      </div>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

And in src/index.js , we put:

new Vue({  
  el: "#app",  
  data: {}  
});

v-else-if

v-if can also be followed with an element with the v-else-if directive, so that we can have multiple elements conditionally displayed.

For example, we can write index.html as follows:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <div v-if="Math.random() > 0.8">  
        A  
      </div>  
      <div v-else-if="Math.random() > 0.5 && Math.random() <= 0.8">  
        B  
      </div>  
      <div v-else>  
        C  
      </div>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we’ll have A, B and C displayed according to what’s returned from Math.random() .

This is available since Vue 2.1.0.

Controlling Reusable Elements with key

Vue tries to reuse existing elements as much as possible when conditionally rendering items.

This means sometimes we get things that we don’t expect.

For example, if we have the following in src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    loginType: "username"  
  },  
  methods: {  
    toggleType() {  
      this.loginType = this.loginType === "username" ? "email" : "username";  
    }  
  }  
});

And the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button @click="toggleType">Change Login Type</button>  
      <template v-if="loginType === 'username'">  
        <label>Username</label>  
        <input placeholder="Enter your username" />  
      </template>  
      <template v-else>  
        <label>Email</label>  
        <input placeholder="Enter your email address" />  
      </template>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we click the Change Login Type button after entering something to the input, we get the same input value displayed after changing the template.

To make the input render from scratch, we can add a key attribute to the the key attribute with different values for each input.

So instead of what we have above for index.html, we can write:

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button @click="toggleType">Change Login Type</button>  
      <template v-if="loginType === 'username'">  
        <label>Username</label>  
        <input placeholder="Enter your username" key="username" />  
      </template>  
      <template v-else>  
        <label>Email</label>  
        <input placeholder="Enter your email address" key="email" />  
      </template>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then the inputs will be cleared.

Everything else is still reused efficiently since we didn’t add the key attribute.

Conclusion

We can render things conditionally with v-if . The element with v-if added will be displayed when the expression that we set as the value returns true .

We can use it in conjunction with v-else-if and v-else , where we can provide elements that are displayed when the condition in v-if returns false .

Elements with v-else-if are displayed when the condition returns a truthy value and the one in v-if returns a falsy value. It must be added immediately below an element with v-if .

An element with v-else can be added below an element with v-if if there’s no element with v-else-if . If there’s one or more elements with v-else-if , then the v-else element has to be added below it.

v-else can be used to display something if all other conditions are falsy.

Categories
JavaScript React

State and Lifecycle of React Components

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at the lifecycle of React components and how to change their internal state.

Changing Internal State

Props are immutable since we want to keep React components pure with respect to props. This means that when we pass in the same props, we always get the same output assuming nothing else changes.

This isn’t very useful in most cases since we want to change things inside a component.

To do this, we can manipulate the internal state of a component.

All class-based React components have internal state accessible via this.state, and we can change it by calling this.setState.

For example, if we want to create a Clock component that shows the current time and updates it every second, we can do that as follows:

class Clock extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = { date: new Date(), timer: undefined };  
  } 

  componentWillMount() {  
    this.updateDate();  
  } 

  componentWillUnmount() {  
    clearInterval(this.state.timer);  
  } 

  updateDate() {  
    const timer = setInterval(() => {  
      this.setState({ date: new Date() });  
    }, 1000);  
    this.setState({ timer });  
  } 

  render() {  
    return (  
      <div>  
        <p>It is {this.state.date.toLocaleTimeString()}.</p>  
      </div>  
    );  
  }  
}

In the code above, we declared a class Clock that extends React.Component. This means the code is a React component, which can have its own lifecycle methods and the render method.

Inside the Clock component, we declared the state field in the constructor which has the initial state date, and a timer state to hold the timer object that’s returned by setInterval.

Also, we called super(props) since Clock extends React.Component, and the React.Constructor takes props as an argument.

Then we have our componentDidMount lifecycle hook to load the initialization code when the Clock component is mounted into the DOM.

The updateDate method is called in componentDidMount to continuously update the this.state.date every second.

To update the date inside the setInterval callback, we called this.setState with an object inside.

The object has the state’s name as the key as we defined it in the first line, and the value is the new value that we want to set the state to.

We also called setState to set the timer field so that we can call clearInterval with it inside the componentWillUnmount. The componentWillUnmount hook is called when the component is removed from the DOM.

It’s a good place to run any code that’s used to clean things up.

Finally, in the render method, we render the date. This method is called whenever this.state changes by calling this.setState, so if we put this.state properties in there, the latest value of it will always be shown.

This means that this.state.date will be the latest date after:

this.setState({ date: new Date() });

is called.

componentDidMount and componentWillUnmount are lifecycle methods. They’re called only in specific parts of a React component’s lifecycle.

this.setState is asynchronous, so we shouldn’t assume that code that’s listed after a this.setstate call will run immediately after this.setState.

To mount it in the DOM, we call the ReactDOM.render method with Clock passed in as follows:

import React from "react";  
import ReactDOM from "react-dom";

class Clock extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = { date: new Date(), timer: undefined };  
  } 

  componentWillMount() {  
    this.updateDate();  
  } 

  componentWillUnmount() {  
    clearInterval(this.state.timer);  
  } 

  updateDate() {  
    const timer = setInterval(() => {  
      this.setState({ date: new Date() });  
    }, 1000);  
    this.setState({ timer });  
  } 

  render() {  
    return (  
      <div>  
        <p>It is {this.state.date.toLocaleTimeString()}.</p>  
      </div>  
    );  
  }  
}

const rootElement = document.getElementById("root");  
ReactDOM.render(<Clock />, rootElement);

Using State Correctly

We should never modify the state directly because it won’t trigger a re-rendering of a component.

So we shouldn’t write:

this.state.foo = 'bar';

Instead, we should always use this.setState as follows:

this.setState({foo: 'bar'});

State Updates May Be Asynchronous

State updates are asynchronous, so we shouldn’t depend on it run one after the other.

Also, we can’t just combine props and state values within setState .

For example, the following may fail:

this.setState({  
  count: this.state.count + this.props.increment,  
});

Instead, we write:

this.setState((state, props) => ({  
  count: state.count + props.increment  
}));

to get the latest values from props to update the code.

Regular and arrow functions both work, so we can also write:

this.setState(function(state, props) {  
  return {  
    count: state.count + props.increment  
  };  
});

State Updates are Merged

When we call setState, React merges the object that we pass into setState into the current state.

For example, if we have 2 states, firstName and lastName, then when we called this.setstate({ lastName });, this.state.firstName stays intact, while this.state.lastName is updated with the latest value of lastName.

Data Flows Down

States are always inside a component, and it can be passed down to child components and elements as props.

React apps has a unidirectional data flow that goes from parent to child.

Conclusion

We can create a React component that is dynamic by storing states inside a component.

The state object is stored in the state field of the React component class.

We can set the state by running this.setState.

React components have lifecycles that trigger methods in the React component class. These are called lifecycle hooks.

We can use them to run code at certain stages of a React component’s lifecycle.

Categories
JavaScript

Using JavaScript’s Object Constructor — Part 3

In JavaScript, the Object constructor lets us create an object wrapper with the given values. It will create an empty object if null or undefined is passed into the Object constructor.

If the value passed into the constructor is an object already then it will return the object.

The Object constructor has two properties. It has a length property that is always 1, and like all other objects, the Object constructor has a prototype to get all the property additions to the type Object.

In this article, we look at how to prevent changes and getting keys and values.


Object.isSealed()

The Object.isSealed() method is used to check whether an object is sealed.

Sealing an object means that we prevent new properties from being added to an object and mark all existing ones as non-configurable, which means that the property descriptor of the property can’t change and the property can’t be deleted.

A property descriptor is an object with the property names as keys and the properties writable, configurable, enumerable, and value as properties of the property name keys.

The writable means that the property’s value can be changed. configurable means the property descriptor may be changed and whether the property can be deleted from the object.

The enumerable property means that the property shows up during enumeration of the properties with the for...in loop, and value is the value of the property.

It returns true is the object is sealed, otherwise returns false. For example, we can use the function as follows:

let obj = {};  
const objSealed = Object.isSealed(obj);  
console.log(objSealed);

This is the simplest case where the object has not been explicitly sealed or has the property descriptors modified. The console.log statement above would return false since the object isn’t sealed.

let obj = {};  
Object.preventExtensions(obj);  
const objSealed = Object.isSealed(obj);  
console.log(objSealed);

If we call the Object.preventExtensions() method to prevent properties from being added, then objSealed in the code above would return true since we prevented properties from being added with the Object.preventExtensions() method and there’re no existing properties in the object. This only applies to empty objects.

If we set a property’s descriptor with the configuration property to false and we call Object.preventExtensions() on the object, then the Object.isSealed() will return true since we prevented an object’s property’s property descriptor from being modified or deleted.

For example, if we have:

If we call the Object.seal() method on an object, then it does everything to seal the object, which includes preventing properties from being added, preventing existing properties’ property descriptors from being modified, and also preventing object’s properties from being deleted.

For example, if we have:

let sealedObj = { a: 1 };  
Object.seal(sealedObj);  
console.log(Object.isSealed(sealedObj));

The console.log above will log true since we called Object.seal() on the object.


Object.keys()

The Object.keys() method returns an array of an object’s property names in strings that are set to be enumerable. They are returned in the same order as a normal loop.

If an array is passed in as an argument, then we have the indexes of the array returned. For example, if we have:

const obj = {  
  a: 1,  
  b: 2,  
  c: 3  
};  
console.log(Object.keys(obj));

Then we get [“a”, “b”, “c”] when we run console.log since we have those property names in the object obj.

We can also pass arrays into the method. For example, we can write:

const arr = [1, 2, 3];  
console.log(Object.keys(arr));

Then we get [“0”, “1”, “2”] when we run console.log since we have those properties as indexes in the arr array. If we have an array-like object that has all numerical keys, then we get the keys returned in order.

For example:

const obj = {  
  2: 1,  
  3: 2,  
  1: 3  
};  
console.log(Object.keys(obj));

Then we get [“1”, “2”, “3”] when we run console.log since we have numerical keys and the Object.keys() method will return the numerical keys in ascending order.

If we have a mix of numerical and non-numerical keys, then the numerical keys will be returned in ascending order before the rest is returned. For example, if we have:

const obj = {  
  a: 1,  
  3: 2,  
  1: 3  
};  
console.log(Object.keys(obj));

Then we get [“1”, “3”, “a”] when we run console.log.

Only names of enumerable properties are returned by Object.keys(). For example, if we have:

Then the console.log will return an empty array since the property a has a property descriptor where enumerable is set to false.


Object.preventExtensions()

The Object.preventExtensions() method prevents new properties from being added to an object. New properties can’t be added after Object.preventExtensions() is called on it.

However, existing properties can still be deleted. An object can’t be made extensible again after it has been made non-extensible. Its return value is the object that has been made no-extensible. For example, if we have:

let obj = {};  
Object.preventExtensions(obj);  
Object.defineProperty(obj, 'a', {})

We will get an error message saying: ‘Cannot define property a, object is not extensible.’

This is what’s expected since we stopped properties from being able to be added to an object with the Object.preventExtensions() method.

If we try to add a property to it with the dot notation, like in the code below:

let obj = {};  
Object.preventExtensions(obj);  
obj.a = 1;  
console.log(obj);

We get an empty object logged since we called Object.preventExtension() on it. If we run the code above in strict mode like in the code below, we will get ‘Cannot define property a, object is not extensible.’:

'use strict';  
let obj = {};  
Object.preventExtensions(obj);  
obj.a = 1;  
console.log(obj);

If we pass in a primitive value to the method, it will be returned as-is as if it is a non-extensible object. For example, if we have:

console.log(Object.preventExtensions(1));

Then we get 1 since it returns primitive values as-is. In ES5, the same code will log a TypeError.


Object.seal()

The Object.seal() method seals an object. Sealing an object means that we prevent new properties from being added to an object and mark all existing ones as non-configurable, which means that the property descriptor of the property can’t change and the property can’t be deleted.

The values of the properties can still be modified unless their property descriptor’s writable property is explicitly set to false. The prototype chain isn’t modified, but the __proto__ property is also sealed.

Before Object.seal() is called on an object, properties can be added, their property descriptors modified, or properties can be deleted at will. For example, we can write:

If we have run console.log on obj, then we see that we get {a: 3} because we modified the value of a and deleted the b property.

By default, JavaScript objects aren’t sealed, so you may change the properties however you like.

Once we call Object.seal() on an object, like in the code below:

We get {a: 3, b: 2} in the console.log output since Object.seal() prevents properties from being deleted, but doesn’t stop the property’s values from being assigned. For example, if we have the following code:

let obj = {  
  a: 1,  
  b: 2  
};  
Object.seal(obj)  
console.log(Object.getOwnPropertyDescriptors(obj));

Then we get:

In the console.log since the whole purpose of Object.seal() is to set the configurable property of each property descriptor to false.

This means that their property descriptors can no longer be changed and the properties can’t be deleted from the object.


Object.setPrototypeOf()

The Object.setPrototypeOf() method lets us set the prototype of an object. It takes two parameters. The first parameter is the object you want to change the prototype of, and the second parameter is the prototype object that you want to set it to.

This means that we change the object that the object where the Object.setPrototypeOf() is being called on is inheriting from.

The method can’t be called on non-extensible objects, that is, the ones that have Object.isExtensible() returned false. It does nothing if the prototype parameter isn’t an object or it’s null.

For example, if we have a normal object, then we can set the prototype of the object with the Object.setPrototypeOf() method. The object with the prototype set is returned. For example, if we have:

Then we get {a: 3, b: 4} as the value of newObj with its prototype being the proto object. We can also set an object’s prototype to null with this method:

let obj = {  
  a: 3,  
  b: 4  
};  
const newObj = Object.setPrototypeOf(obj, null);  
console.log(newObj);

Then we get {a: 3, b: 4} as the value of newObj with its prototype being null. The method doesn’t work if the object where the prototype is being set is not extensible. For example, if we have:

Then we will get a TypeError because the object is sealed, which means that all properties, including the prototype property, can’t be added.

Likewise, if we freeze an object, which seals an object and also prevents properties’ values from being changed, it will also fail when we call this method. For example, if we have:

Then we will get a TypeError.


Object.values()

The Object.values() method returns an array of objects that are the values of the enumerable top-level properties in an object. They’re returned in the same order as in the for...in loop.

However, Object.values() do not return the values of properties that are up the prototype chain. For example, if we have:

const obj = {  
  a: 1,  
  b: 2,  
  c: 3  
};console.log(Object.values(obj));

Then we get [1, 2, 3] because we have three properties listed in the obj object. If we have an object like array with all numerical keys, then they’re enumerated in ascending order. For example, if we have:

const obj = {  
  6: 1,  
  5: 2,  
  4: 3  
};  
console.log(Object.values(obj));

Then we get [3,2,1] logged because the keys are ordered in ascending order since they’re all numbers. If we have an array, then the array is returned as-is. For example, if we have:

const arr = [1, 2, 3]  
console.log(Object.values(arr));

Then we get [1, 2, 3] logged because we passed in an array to the Object.values() method. If we have values of properties that aren’t enumerable then they won’t be listed. For example, if we have:

Then we get [“a”] logged because we only have the property a being enumerable.


Conclusion

The Object constructor has many more methods for constructing objects from an array with key and value of properties, and also methods to get property descriptors from objects, property names, and property symbols.

It gets the keys of an object and prevents properties from being added or deleted or modifying their property descriptors.

They’re very useful for controlling how properties behave since JavaScript objects are dynamic by default. These methods can prevent accidental changes more easily.

Categories
Express JavaScript Nodejs

Processing File Upload with the Express Multer Middleware

Multer is a middleware that lets us process file uploads with our Express app.

In this article, we’ll look at how to use it to process uploads and process text data in multipart forms.

How to Use it?

We can install it by running:

npm install --save multer

Once it’s installed we have to make a form that has the encoding type multipart/form-data .

It won’t process any form that’s not multipart, i.e. has the enctype set to multipart/form-data .

Simple Usage

The simplest usage is to allow upload of one file at a time. We can do this by calling the multer function and setting the path to store the uploaded file as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const multer = require('multer');  
const upload = multer({ dest: './uploads/' });  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
app.get('/', (req, res) => {  
  res.sendFile('public/index.html');  
});
app.post('/upload', upload.single('upload'), (req, res) => {  
  res.send('file uploaded')  
});
app.listen(3000, () => console.log('server started'));

Then we can create index.html in the public folder and add:

<!DOCTYPE html>  
<html>
<head>  
  <meta charset="utf-8">  
  <meta name="viewport" content="width=device-width">  
  <title>Upload</title>  
  <link href="style.css" rel="stylesheet" type="text/css" />  
</head>
<body>  
  <form action="/upload" method="post" enctype="multipart/form-data">  
    <input type="file" name="upload" />      
    <input type='submit' value='Upload'>  
  </form>  
</body>
</html>

The code for index.html will make the form submit multipart form data.

Also, the value of thename attribute has to match with the name string that’s passed into the upload.single() method.

Once the file is upload, a random ID will be generated for the uploaded file’s name.

req.files will have the file objects and req.body will have the values of the text fields.

Uploading Multiple Files

We can also process the upload of multiple files with the upload.array method as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const multer = require('multer');  
const upload = multer({ dest: './uploads/' });  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
app.get('/', (req, res) => {  
  res.sendFile('public/index.html');  
});
app.post('/upload', upload.array('uploads', 5), (req, res) => {  
  res.send('files uploaded')  
});
app.listen(3000, () => console.log('server started'));

Then we have to change index.html to:

<!DOCTYPE html>  
<html>
<head>  
  <meta charset="utf-8">  
  <meta name="viewport" content="width=device-width">  
  <title>Upload</title>  
  <link href="style.css" rel="stylesheet" type="text/css" />  
</head>
<body>  
  <form action="/upload" method="post" enctype="multipart/form-data">  
    <input type="file" name="uploads" multiple />  
    <input type='submit' value='Upload'>  
  </form>  
</body>
</html>

We add the multiple attribute to the form to enable multiple uploads in addition to using the upload.array method.

The 5 in the second argument of the upload.array call is the maximum number of files that we can upload.

Once the file is upload, a random ID will be generated for the uploaded file’s name.

Multiple File Fields

We can enable upload of multiple files as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const multer = require('multer');  
const upload = multer({ dest: './uploads/' });  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
app.get('/', (req, res) => {  
  res.sendFile('public/index.html');  
});
const multiUpload = upload.fields([  
  { name: 'photos', maxCount: 3 },  
  { name: 'texts', maxCount: 5 }  
])  
app.post('/upload', multiUpload, (req, res) => {  
  console.log(req.files);  
  res.send('files uploaded');  
});
app.listen(3000, () => console.log('server started'));

Then when we have the following in public/index.html :

<!DOCTYPE html>  
<html><head>  
  <meta charset="utf-8">  
  <meta name="viewport" content="width=device-width">  
  <title>Upload</title>  
  <link href="style.css" rel="stylesheet" type="text/css" />  
</head><body>  
  <form action="/upload" method="post" enctype="multipart/form-data">  
    <label>Photos</label>  
    <br>  
    <input type="file" name="photos" multiple />  
    <br>  
    <label>Texts</label>  
    <br>  
    <input type="file" name="texts" multiple />  
    <br>  
    <input type='submit' value='Upload'>  
  </form>  
</body></html>

We get the files uploaded after we select the files and submit them. The code:

const multiUpload = upload.fields([  
  { name: 'photos', maxCount: 3 },  
  { name: 'texts', maxCount: 5 }  
])

Gets the files from the fields photos and texts and upload to 3 and 5 files respectively.

In the console.log , we see the files of the POST /upload route that we uploaded.

Upload Nothing

To skip the processing of upload files, we can use the upload.none() method as follows:

const express = require('express');  
const bodyParser = require('body-parser');  
const multer = require('multer');  
const upload = multer();  
const app = express();
app.use(bodyParser.json());  
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public'));
app.get('/', (req, res) => {  
  res.sendFile('public/index.html');  
});
app.post('/submit', upload.none(), (req, res) => {  
  res.send(req.body)  
});
app.listen(3000, () => console.log('server started'));

Then when have the following for public/index.html :

<!DOCTYPE html>  
<html><head>  
  <meta charset="utf-8">  
  <meta name="viewport" content="width=device-width">  
  <title>Upload</title>  
  <link href="style.css" rel="stylesheet" type="text/css" />  
</head><body>  
  <form action="/submit" method="post" enctype="multipart/form-data">  
    <input type="text" name="name" />      
    <input type='submit' value='Submit'>  
  </form>  
</body></html>

We get the submitted POST body in req.body as we can see from the response after typing in something and clicking Submit.

Conclusion

Multer is very useful for processing any kind of multipart forms. It can process uploads of a single file or multiple files. It can also process uploads for multiple fields.

The files uploaded are renamed to an automatically generated by default and are stored in the folder that we specified when we called the multer function to create the middleware.

Also, it can ignore uploaded files and just parse the text request body.