Categories
JavaScript APIs

Checking Network Status with the Network Information API

With the advent of mobile devices like phones and tablets, knowing the connection status is very important since it can change any time, affecting user experience in the process. We also have to be aware of different kinds of internet connections since they vary widely in speed.

Fortunately, we have the Network Information API built into browsers to check for internet connection status.

This API is available for browser and worker contexts.

In this article, we’ll look at how to use the API to get network connection type changes and connection status.

Detect Connection Changes

Detecting connection changes is simple. We can use the navigation.connection object to listen to network type changes as follows:

const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
let type = connection.effectiveType;

const updateConnectionStatus = () => {
  console.log(`Connection type changed from ${type} to ${connection.effectiveType}`);
  type = connection.effectiveType;
}

connection.addEventListener('change', updateConnectionStatus);

We can then test the connection type changes by going to the Chrome developer console by pressing F12. Then go to the Network tab, and then on the top right, there’s a dropdown to change the connection type.

If we switch between them, we should see something like:

Connection type changed from 4g to 3g
Connection type changed from 3g to 4g
Connection type changed from 4g to 2g

from the console.log output.

The connection object is a NetworkInformation object instance and it has the following properties:

  • downlink — effective bandwidth estimate in megabits per second rounded to the nearest 25 kbps.
  • downlinkMax — maximum downlink speed, in Mbps for the underlying connection technology
  • effectiveType — the type of connection determined by a combination of recently observed round-trip time and downlink values. It can be one of ‘slow-2g’, ‘2g’, ‘3g’, or ‘4g’.
  • rtt — estimated effective round-trip time of the current connection rounded to the nearest multiple of 25 milliseconds.
  • saveData — boolean indicating if reduced data usage optional has been set
  • type — type of connection to communicate with the network. It can be one of bluetooth, cellular, ethernet, none, wifi, wimax, other, unknown

Compatibility

This API is new and experimental, so we’ve to be careful when we use it. Chrome supports most properties out of the box since Chrome 61. Some options like downlinkMax and type are only available for Chrome OS. Firefox and Edge do not support this API.

It’s also available for use in other Chromium-based browsers like Opera, Android Webview, and Chrome for Android.

With the Network Information API, we can get information about the network connection. This is useful for detecting the connection type of mobile devices and lets us change the web page to accommodate slower connections accordingly.

Categories
JavaScript APIs

Getting Browser User Permission with the Permissions API

We have to get user consent when we want to do anything that changes the user experience. For example, we need to get user permission to show notifications or access their hardware.

Modern browsers are standardized in how permissions are obtained. It’s all in the Permissions API.

APIs that rely on the Permissions API for permissions include the Clipboard API, Notifications API, Push API, and Web MIDI API.

The navigator object has the permissions property in both the standard browser and worker contexts. It gives us a way to get the permissions. However, the way we request permissions is different for each API.

This API is experimental so check if the browsers you target are supported.

Querying Permissions

The navigation.permissions object lets us query permissions set with the query method.

It returns a promise which gets us the state of the permission. For example, we can write the following to check for permission for the geolocation API:

(async () => {
  const result = await navigator.permissions.query({
    name: 'geolocation'
  });
  console.log(result.state);
})();

The possible values for result.state are 'granted', 'denied', or 'prompt'.

There’s also one for Web Workers that do the same thing.

The object that we passed into the query method is the permission descriptor object, which has the following properties:

  • name — name of the API which we want to get the permission for. Firefox only supports geolocation, notifications, push, and persistent-storage
  • userVisibleOnly — indicates whether we want to show notification for every message or be able to send silent push notifications. Default value is false .
  • sysex — applies to MIDI only. Indicates whether we need to receive system exclusive messages. Default value is false .

Requesting Permission

Requesting permission is different between APIs. For example, the Geolocation API will show a prompt for permission when we call getCurrentPosition() as follows:

navigator.geolocation.getCurrentPosition((position) => {
  console.log('Geolocation permissions granted');
  console.log(`Latitude: ${position.coords.latitude}`);
  console.log(`Longitude: ${position.coords.longitude}`);
});

The Notifications API has a requestPermission method to request permissions:

Notification.requestPermission((result) => {
  console.log(result);
});

Watching for Permission Changes

The navigator.permissions.query promise resolves to a PermissionStatus object. We can use it to set an onchange event handler function to watch for permission changes.

For example, we can expand on this example to add an event handler function to watch for changes for the permission of the Geolocation API:

(async () => {
  const result = await navigator.permissions.query({
    name: 'geolocation'
  });
  console.log(result.state);
})();

We can write:

(async () => {
  const permissionStatus = await navigator.permissions.query({
    name: 'geolocation'
  });
  permissionStatus.onchange = () => {
    console.log(permissionStatus.state);
  }
})();

The permissionStatus.state will have the permission for the Geolocation API.

It’ll log whenever the permission changes, such as when we change the permission setting in the browser.

Revoking Permissions

We can revoke permission by using the revoke method.

To call it, we can access it from the navigator.permissions object as follows:

navigator.permissions.revoke(descriptor);

Where descriptor is the permission descriptor which we described above.

It returns a Promise that resolves with the permission status object indicating the result of the request.

This method throws a TypeError if getting the permission descriptor failed or the permission doesn’t exist or is unsupported by browsers.

The name property of the permission descriptor can accept one of the following values 'geolocation', 'midi', 'notifications', and 'push'.

Other properties remain the same as the query method.

For example, we can use it by writing:

(async () => {
  const permissionStatus = await navigator.permissions.revoke({
    name: 'geolocation'
  });
  console.log(permissionStatus);
})();

Note that we’ve to set dom.permissions.revoke.enable to true in Firefox 51 or later to use this method. It’s not enabled in Chrome.

The Permissions API lets us check and revoke browser permissions in a uniform way.

Requesting permissions are different for each API. This isn’t changing any time soon.

This is an experimental API, so using it in production is probably premature.

Categories
Top Vue Packages

Vue Plugins You Don’t Know You May Need

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 some Vue packages you don’t know you want to add to your app.

Vue-Dummy

The vue-dummy package lets us add dummy text to our app when while we’re developing it so we won’t have to worry about generating the text ourselves. We can also add placeholder images with it.

We can install it by running:

npm install --save vue-dummy

It’s also available as a standalone script that we can add by adding:

<script src="https://unpkg.com/vue-dummy"></script>

to our HTML code.

Then we can use it as follows:

main.js :

import Vue from "vue";
import App from "./App.vue";
import VueDummy from "vue-dummy";

Vue.use(VueDummy);

Vue.config.productionTip = false;

new Vue({
  render: h => h(App)
}).$mount("#app");

App.vue :

<template>
  <div id="app">
    <p v-dummy="150"></p>
    <img v-dummy="'200x150'">
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

In the code above, we added a p element and bind it to the v-dummy directive. We set the value to 150 so that we get 150 fake words on the page.

Next, we added an image that’s 200px wide by 150px high.

We can also have a random number of words between a range for the dummy text. To use this feature, we can write:

<template>
  <div id="app">
    <p v-dummy="'100,130'"></p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Then we get a random block of text between 100 to 130 words. It can also be written as a directive argument. For instance, we can write:

<template>
  <div id="app">
    <p v-dummy:100,130></p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

to do the same thing we did in the previous example.

Likewise, dummy images dimensions can be set as directive arguments or modifiers.

To set dummy image dimensions as an argument, we can write:

<img v-dummy:300x200>

To set dummy image dimensions as a directive modifer, we can write:

<img v-dummy.400x300>

We can also set the width and height attributes of the img tag to set its dimensions:

<img v-dummy width="250" height="150">

We can also create randomly sized images:

<img v-dummy="'100,400x200,400'">

The code above will create images that’s between 100 to 400px wide and 200 to 400px high.

We can also put the numbers as a directive argument as follows:

<img v-dummy:100,400x200,400>

The dummy component is also available to generate the placeholder text or image as follows:

<dummy text="100"></dummy>
<dummy img="420x300"></dummy>

The first line creates a text block that’s 100 words long, and the 2nd creates a placeholder image that’s 420px wide by 300px high.

We can also create a table with fake content as follows:

<table v-dummy></table>

Vue.ImagesLoaded

The Vue.ImagesLoaded directive detects whether an image has been loaded.

It calls a callback when the image is loaded or fails to load. We can install it by running:

npm install vue-images-loaded --save

Then we can use it as follows:

<template>
  <div id="app">
    {{loaded}}
    <div v-images-loaded:on.progress="imageProgress">
      <img
        src="https://images.unsplash.com/photo-1562953208-602ead7f3d47?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=375&q=80"
      >
    </div>
  </div>
</template>

<script>
import imagesLoaded from "vue-images-loaded";

export default {
  name: "App",
  directives: {
    imagesLoaded
  },
  data() {
    return { loaded: "" };
  },
  methods: {
    imageProgress(instance, image) {
      this.loaded = image.isLoaded ? "loaded" : "broken";
    }
  }
};
</script>

In the code above, we registered the imagesLoaded directive from the vue-image-loaded package in our App component.

Then we added the v-images-loaded:on.progress=”imageProgress” directive with the imageProgress method as the value.

Then we can get whether the image is loaded from the image parameter’s isLoaded property.

The instance parameter has the elements property with an array for the parent of the image element that’s being loaded. The images property has an array of image elements for the images that are being loaded.

Other modifiers for the v-images.on directive include always, done, fail, progress to watch for all image loading events, watching only when the image is successfully loaded, watch only when it fails to load or watch the image when it’s loading respectively.

Conclusion

The v-dummy package lets us create placeholder text and images during development so we don’t have to do that ourselves.

To watch the progress of image loading, we can use the Vue.ImageLoaded package to do that.

Categories
Vue

How to Create a Vue Plugin

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 how to create a Vue plugin to expand our app’s functionality.

How to Create a Plugin

Creating a new Vue plugin is simple. We just have to create a module that exports an object with the install method inside it.

The install method has a Vue parameter for the Vue instance, and an options object that takes various options that we pass in by calling Vue.use to register the plugin.

With with steps above, we can build our own plugin. We can add a mixin with the Vue.mixin function.

Mixins let us access Vue lifecycle hooks.

Also, we can add properties to Vue.prototype to add methods to our components.

To create a bare-bones plugin, we can write the following:

plugin.js

export default {
  install(Vue, options) {
    Vue.mixin({
      created() {
        console.log("hello");
      }
    });
  }
};

main.js

import Vue from "vue";
import App from "./App.vue";
import plugin from "./plugin";

Vue.config.productionTip = false;
Vue.use(plugin);

new Vue({
  render: h => h(App)
}).$mount("#app");

In plugin.js , we exported an object with a default export that has the install method inside, which has the Vue parameter with the Vue instance and options with options, we pass into a Vue object.

We called Vue.mixin with the created method to modify the created hook globally so that whatever we have inside will be called when any component is initialized and the created hook is called.

Since we have console.log('hello') inside the mixin, that’ll be called when our component loads.

In main.js , we registered our plugin globally by calling Vue.use with the plugin we imported as the argument.

We’ll see 'hello' logged in the console log output.

To accept options with our plugin, we can pass them into Vue.use and use it our plugin as follows:

plugin.js

export default {
  install(Vue, options) {
    Vue.mixin({
      created() {
        const { greeting } = options;
        console.log(greeting);
      }
    });
  }
};

main.js

import Vue from "vue";
import App from "./App.vue";
import plugin from "./plugin";

Vue.config.productionTip = false;
Vue.use(plugin, { greeting: "hello jane" });

new Vue({
  render: h => h(App)
}).$mount("#app");

In the code above, we get the greeting property from the options parameter. Then since we passed in an object with the greeting property as the 2nd argument of the Vue.use , we’ll see 'hello jane' from the console log output.

Instance Properties

We can add our own instance properties to our plugin by adding properties to the Vue.prototype object.

The properties that we add should start with a $ symbol to distinguish them from properties that we define in our components.

For instance, we can add our own properties as follows:

plugin.js

export default {
  install(Vue, options) {
    Vue.prototype.$bold = text => {
      return `<b>${text}</b>`;
    };
  }
};

App.vue

<template>
  <div id="app" v-html="greeting"></div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      greeting: "hello"
    };
  },
  beforeMount() {
    this.greeting = this.$bold(this.greeting);
  }
};
</script>

In the code above, we have the following method in the install method:

Vue.prototype.$bold = text => {
  return `<b>${text}</b>`;
};

which can be called within our component since it’s part of the Vue prototype.

Then in App.vue , we can call it as follows:

this.greeting = this.$bold(this.greeting);

and then we can set the text by using the v-html directive as follows in App.vue :

<div id="app" v-html="greeting"></div>

Then we see the text shown on the screen.

Global Filters

We can add a global filter with the Vue.filter method.

The filter method takes a string with the filter name as the first argument and the function which takes an input and value and returns the value formatted as we like as the 2nd argument.

For instance, we can write the following code to do that:

plugin.js

export default {
  install(Vue, options) {
    Vue.filter("localeDateString", value => {
      if (!(value instanceof Date)) {
        return value;
      }
      return value.toLocaleDateString();
    });
  }
};

In the code above, we have the localeDateString filter, which returns the date string generated from a date object.

Then in App.vue , we can use it as follows:

<template>
  <div id="app">{{new Date() | localeDateString}}</div>
</template>

<script>
export default {
  name: "App"
};
</script>

And we get the locale-dependent date string displayed instead of the default date string.

Custom Directives

We can call the Vue.directive method to add a directive in our plugin.

The method takes a directive name string as the first argument and the directive object as the 2nd argument.

For instance, we can use it as follows:

plugin.js

export default {
  install(Vue, options) {
    Vue.directive("highlight", {
      inserted(el) {
        el.style.color = "red";
      }
    });
  }
};

In the code above, we have a highlight directive that changes the element’s content to the color red when we bind the directive to it.

Then when we use it as follows:

<template>
  <div id="app" v-highlight>foo</div>
</template>

<script>
export default {
  name: "App"
};
</script>

We get that ‘foo’ is red.

Conclusion

We can create our own Vue plugin by creating a module that exports and object with an install method.

The method takes a Vue parameter for the Vue instance and an options object with the options that we pass into Vue.use as the 2nd argument.

Then we can define Vue directive, mixins, and filters inside the install method.

Categories
Modern JavaScript

Best Features of ES2019 — Catch, Sort, and toString

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2019.

Catch Binding

catch binding is now optional with ES2019.

For instance, we can write:

let jsonData;
try {
  jsonData = JSON.parse(str);
} catch {
  jsonData = {};
}

Then we don’t need to get the thrown object in the parentheses after the catch .

However, other kinds of errors that aren’t related to parsing will be silently ignored, since we just set it the jsonData object to an empty object.

Another example would be to get a deeply nested property.

For instance, we can just write:

function logId(item) {
  let id;
  try {
    id = item.data.id;
  } catch {
    console.log(id);
  }
}

to let us safely get the deeply nested id property of item to id if it exists.

If it doesn’t exist, then the code in the catch block is run.

Stable Array.prototype.sort()

The Array.prototype.sort method is now guaranteed to be stable.

So if something is considered to be the same when sorting, then they’ll stay in the same order in engines that supports ES2019.

If we have:

const arr = [{
    key: 'foo',
    value: 1
  },
  {
    key: 'bar',
    value: 2
  },
  {
    key: 'foo',
    value: 3
  },
];
arr.sort((x, y) => x.key.localeCompare(y.key, 'en'));

Then arr would be in the same order regardless of which environment it’s in.

JSON.stringify

JSON.stringify can now take a lone surrogate.

So if we have:

JSON.stringify('u{D800}')

Then we get:

""ud800""

JSON superset

JSON strings now can have the '"u2028"' character.

Function.prototype.toString Changes

Function.prototype.toString now has some changes since ES2019.

The functions we defined returns a string with the original source code.

If we call toString with a built-in function, then we see [native code] .

For instance, if we have:

isNaN.toString()

Then we get:

"function isNaN() { [native code] }"

Functions created dynamically with the Function and GeneratorFunction engines must create the appropriate source code and attach it to functions.

All other cases would throw a type error.

Conclusion

Catch binding, function toString , sort methods all have changes with ES2019.