DEV Community

John Peters
John Peters

Posted on

Separation of Concerns

One of the best things I've learned in my career is the Separation of Concerns Principal which guides us in setting up some rules for our web component (WC) library.

Rules

Here's the first three rules we will follow:

  • Use convention over configuration
  • All functions or methods to have only one concern.
  • Function or Method names must be carefully determined. This communicates what it does. It's a huge clue to developers down the road.

Convention over Configuration

Our convention, in order:

  • Declare a custom Element: customElements.define(
  • Define the tag name such as bing-search and remember it must have a hyphen. The tag name should always describe its sole function.

  • Declare a class and extend from HtmlElement as shown:

customElements.define(
  "bing-search",
  class extends HTMLElement {
    constructor() {
      super();
      this.initializeElements();
      this.addEventHandlers();

Enter fullscreen mode Exit fullscreen mode
  • Add a constructor with the first line being this: super(); This is a requirement of WC.

  • Create generic method names that will be used in all other WCs where needed.

Functions Have One Responsibility

Don't write monolithic code unless you want a new job.

We can avoid this by using the Single Responsibility Principle; which works at both the ECMA module layer as well as the class and function layers.

The initializeElements Function

Notice this method initializes components by creating them and then adding them to the WC.

It does not do anything else!

    initializeElements() {
      this.bingsearch = "bingsearch";
      this.input = document.createElement("input");
      this.input.id = this.bingsearch;
      this.input.title = this.bingsearch;
      this.input.type = "input";
      this.input.value = this.getAttribute("searchstring");

      this.button = document.createElement("input");
      this.button.type = "submit";
      this.button.value = "Submit";
      this.bingsearchsubmit = "bingSearchSubmit";
      this.button.id = this.bingsearchsubmit;

      this.appendChild(this.input);this.appendChild(this.button);
    }

Enter fullscreen mode Exit fullscreen mode

The addEventHandlers Function

addEventHandlers() {
      this.addEventListener("change", function (ev) {
        let element = ev.target;
        if (ev.target.id == this.bingsearch) {
          this.OpenSearch();
          return;
        }
      });

      this.addEventListener("click", function (ev) {
        let element = ev.target;
        if (ev.target.id == this.bingsearchsubmit) {
          this.OpenSearch();
          return;
        }
      });
    }

Enter fullscreen mode Exit fullscreen mode

Both of these methods are different ways of doing the same thing, that's why we have a function named OpenSearch.

The OpenSearch Function

This function has a single responsibility, but was called in two different ways; therefore, it must be able to service both callers.

    //searchPattern = "https://www.bing.com/search?q=";
    OpenSearch() {
      let searchtext = this.input.value;
      let replaced = searchtext.replace(" ", "+");
      let url = `${this.searchPattern}${replaced}`;
      let win = window.open(url);
    }
Enter fullscreen mode Exit fullscreen mode

The GUI

Bing Search GUI

The Result

Bing Search Result

How to Reuse this WC

In your HTML just import the bingsearch.js script, and make a reference to <bing-search>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="bingsearch.js" defer></script>
    <title>Display Grid</title>
</head>
<body>

<h1>Bing Search</h1>
 <bing-search searchstring="single responsibility"></bing-search>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

The Element View

Image description

Notice anything missing? Well yes we should always include ids and or names and titles on our custom elements.

Top comments (0)