Click here to Skip to main content
15,890,741 members
Articles / Web Development

Web Page as a Set of Components - Part 1

Rate me:
Please Sign up or sign in to vote.
4.57/5 (5 votes)
26 Feb 2016CPOL10 min read 12.5K   117   8   3
A design pattern to separate the concerns in a web page. Here, each part of the page is considered as a component which is inherently reactive and provides an unified way to interact with other component in the page.

Introduction

While working on various web projects from small websites to big enterprise applications, we often face various challenges when it comes to maintenance of the UI, due to poorly structured front-end code. There are enough guidelines in the programming world to keep the code clean, structured and modular. However, when it comes to front-end web development, you won't see most of these things followed.

Often, with the typical server-side MVC architecture, the views are segregated but not the script. The maximum segregation that you can see in the application is, one script file per page.

Generally, a web page is a composition of multiple small/partial views (markups) and there will be one script file for manipulation (technically, it is possible to have multiple script files for a single page, but not recommended - let me know, if you are not sure, why so - Google might help as well :) ). This script file has access to the entire page and this is where the problem lies! - Since our script file has no limitation on what it can access on the page, we generally end up writing imperative code which deals with various parts of the page.

Now, what's wrong with this approach? - Well, the problem with this approach is managing the state of a UI portion or the page as a whole. Since any element in the page can be altered anywhere from the script file, it is likely that the same operation has repeated in multiple places and some logic otherwise united is scattered over the file.

The solution for this problem is obviously modularity with some restriction on what each module can access in the page; and this article is all about how to achieve this when not using any third-party UI frameworks (except jQuery which we use for DOM manipulation).

Background

[This portion contains how I arrived here and some history of the topic, you can skip this if you wish.]

In recent years, there have been immense changes in the front-end web development, lots of UI frameworks arrived such as knockout, angular, react, backbone and many more, each has its own set of rules or guidelines for the structure of your code. The common things that you can notice in these libraries/frameworks is modularity. Well, I am not talking about how modular the library is; instead, I am focusing on how it helps/forces us to keep our code modular (up-to some extent).

Though those libraries helps us to modularize our code, still there will be some loopholes. People will again look for guidelines to keep the code clean & organized within the respective communities. For instance, if I am using angularjs, I tend to follow these guidelines, similarly when I was working with knockout (along with ASP.NET MVC framework), I was structuring my code this way.

When I did a deep dive into JavaScript, I started to realize that we are not really following something which is more fundamental in terms of development strategy. We all know and are very well aware of the things, but when it comes to JavaScript, we miss applying it. May be, it is due to the weird nature of JavaScript (not really) which people used to hate for years and were using it by not having any choice! or may be, because there was not much code written in JavaScript for application development, all it used to have is validations. Anyways, now JavaScript is in the mainstream (I believe it deserves it!) but still there is some lack of standards for application development.

The one thing that attracted me more is AMD. The folks who develop re-usable JavaScript libraries do use it a lot but not most of the application developers. Also, there are not many guidelines available for application developers when modules are coupled with some UI parts. However, it has been a year now I have setup some rules for myself and enforced it into our team. Trust me, the UI productivity has doubled! and I would like to share this idea with the rest of the world. :)

Heading Towards Components

Here, we consider each functional part of the UI as a component and they together form a page.

Image 1

When you visualize your page this way, you are pretty clear about how many modules you need? what their roles are? how they interact with each other? how user interacts with them? and how your service deals with it?

Some points to note before proceeding:

  1. Each component is assigned with a responsibility and they serve just that.
  2. There are set of rules that each component must follow.

So, to proceed, let's define a Component in our context:

Quote:

The Component is an independent functional unit in the system, which represents a particular part in the page.

And let's set rules for the component:

  1. Each component can have a state.
  2. Only the component has the rights to alter its state.
  3. Any interaction that happens to the component should be consistent. Meaning, whether the interaction happens via user or by code, the behavior of the component should be same.
  4. It is component's responsibility to keep its state safe by exposing meaningful methods, properties and events (Note: Unfortunately, we cannot put a constraint on the code level).
  5. If a component wants to notify a change in its state, it should do it by triggering an event and the components which are interested in that should subscribe it (Observer Pattern).
  6. Each method in the component should have only one responsibility (Single Responsibility Principle).
  7. The event handlers of UI controls should not contain logic, the intention of the event should be satisfied by calling appropriate method of self or other component.

The components can be divided into 2 parts:

  1. System/Framework Component
  2. Business Component

As the name suggests, business component is a component which serves a particular business goal and framework component is a re-usable technical component.

Create a Component

We create a component with the help of JavaScript module pattern. So, technically in our context:

Quote:

A JavaScript module which obeys the defined rule to achieve the functional goal is a component.

As you might be aware, there are various ways to create modules in JavaScript. If you are not already aware, please go through this article.

Hey... wait a minute! why module pattern and why not object? The prototypal object model suits best to me, the component sounds more like an object rather than a module! In fact, I heard that component is a set of objects functioning together.

Well, it is possible to achieve the same thing using JavaScript's prototypal object model but I don't recommend it unless it is a singleton. If you are wondering why, it is due to the state of the object. Usually, component does have some markup as its state and it will easily get shared with multiple instances, thereby causing side-effects.

Creating Framework Component

Assume that you have a website where it has to show a couple of notifications such as warning, error and success message based on various scenario. As notification is something which is needed in various pages, we will consider it as a framework component.

So before we start implementing, let's decide its behavior.

  1. It should have a method to show the notification with the given message.
  2. It should have a method to hide.
  3. It should have a UI control using which user can trigger the hide.
  4. It should expose an event for onOpen and onClose so that calling code can perform further actions, if needed.

The behavior that we defined is basically a requirement of the application for a particular UI part. However, point 4 is a technical aspect and won't be there in the requirement.

Let's implement the module:

JavaScript
 var notification = (function () {

  // Keep a self reference for all good reasons :)
  var self = this;

  // Lets have a typed hold of markup identity (not necessarily meant to be id, it can be any selector)
  var notifcationDiv = "#notification";
  var messageSpan = "#msg";
  var close = "#close";

  // A private method to show the given message with success class
  var showSuccess = function (message) {
    $(messageSpan).text(message);
    $(notifcationDiv).addClass('alert-success').show();
  };

  // A private method to show the given message with danger class
  var showError = function (message) {
    $(messageSpan).text(message);
    $(notifcationDiv).addClass('alert-danger').show();
  };

  // A private method to show the given message with warning class
  var showWarning = function (message) {
    $(messageSpan).text(message);
    $(notifcationDiv).addClass('alert-warning').show();
  };

  // A private method to hide the notification
  var hide = function () {
    // trigger onClose event, if registered
    if (this.onClose && typeof this.onClose === 'function') {
      this.onClose();
    }
    // Do the clean-up and hide it
    $(notifcationDiv).removeClass().addClass('alert').hide();
  };

  // A private helper method to show the notification.
  var show = function (action, message) {
    // hide any existing notification
    hide.call({});
    // trigger the onOpen event, if registered
    if (this.onOpen && typeof this.onOpen === 'function') {
      this.onOpen();
    }
    // perform the requested operation
    action(message);
  };

  // Register the handler for click event of the close button
  $(close).click(function () {
    // Call the hiding logic using notification. 
    // This is important, when you call a method using the callbacks
    hide.call(self);
  });

  // Revealing the showSuccess method, which takes help of show method to perform its job.
  self.showSuccess = function (message) {
    show(showSuccess, message);
  };

  // Revealing the showError method, which takes help of show method to perform its job.
  self.showError = function (message) {
    show(showError, message);
  };

  // Revealing the showWarning method, which takes help of show method to perform its job.
  self.showWarning = function (message) {
    show(showWarning, message);
  };

  // Revealing the hide method.
  self.hide = hide;

  // Add support for events.
  self.onOpen = null;
  self.onClose = null;

  //------------------------------------------------------------------------
  // NOTE: Technically it is not required to register the events/callbacks 
  //    but it is good to have as the consumer of your module can inspect it
  //------------------------------------------------------------------------

  // Finally expose yourself!
  return self;

}());

And here is its state - The markup for notification

HTML
<div id="notification" class="alert" 
style="display: none">
    <button id="close" type="button" 
    class="close"><span aria-hidden="true">×</span></button>
    <span id="msg"></span>
</div>

I have set-up a code-pen for you. You can play with the code here - http://codepen.io/knaveenbhat/pen/dGxere

In the bottom of the page, there is a button called console. On click of that, it will open a REPL. You can interact with the our notification component with it.

So, in the console, let's register a handler for the onOpen event:

JavaScript
notification.onOpen = function(){ console.log('open...') }

Let's register a handler for onClose event as well:

JavaScript
notification.onClose = function(){ console.log('close...') }

Now type this:

JavaScript
notification.showSuccess('my first success notification!')

Notice the white area, it should be displaying a notification! Also, in the console, you will be seeing a message open... as a result of our callback.

Play with showError, showWarning, hide and see whether it behaves as expected. The notification has a close button as well, using which you should be able to hide the notification.

Now our notification framework component is ready and good to go!

For the folks who want to use plain browser instead I have created an html file for them. Please find the attachment. Apologies for creating a zip for a single file. CodeProject was not allowing me to attach the html file.

Let's review whether we followed all the rules properly:

  1. Each component can have a state.
    Yes we have (The markup)
  2. Only the component has the rights to alter its state.
    Kind of yes. Technically it is possible for any other part of the script to touch this markup but we should not do it! - unfortunately we can't enforce it :(
  3. Any interaction that happens to the component should be consistent. Meaning, whether the interaction happens via user or by code, the behavior of the component should be same.
    Yes, you hide the notification by clicking on the close button or by calling hide method from the console, the behavior is same. It is true for other methods as well.
  4. It is component's responsibility to keep its state safe by exposing meaningful methods, properties and events (Note: Unfortunately, we cannot put a constraint on the code level).
    It is somewhat related to the 2nd point and yes, we are exposing some meaningful methods and events using which other code can interact with it.
  5. If a component wants to notify a change in its state, it should do it by triggering an event and the components which are interested in that should subscribe it (Observer Pattern).
    Yes, we did it via onOpen and onClose event (which is optional).
    Note: With our current implementation, only one user can subscribe it. If required, we can support multiple subscriptions using array.
  6. Each method in the component should have only one responsibility (Single Responsibility Principle).
    Yes, each methods in the notification has only one responsibility
  7. The event handlers of UI controls should not contain logic, the intention of the event should be satisfied by calling appropriate method of self or other component.
    Yes, we have one click event and it doesn't contain any logic, rather it relies on hide method there by satisfying 3rd point.

Congrats! you have successfully followed the rules and hence the module that you implemented is a Component. :)

Candidates for Upcoming Parts

  1. Creating a business component
  2. Dealing with backend services
  3. Handling some complex scenario, where it is tough to identify the components.

Points of Interest

Defining the behavior for UI parts and interacting with them using console is a real joy! There are lot of benefits we get with this approach. Since we are trying to make a particular part of the UI as a function complete by defining its behavior, there will be a clear sense of possible states of that part and possibility of creating bugs are very few. Even if a bug is raised, you know exactly where to look. The side effects of altering a UI part is very minimal.

Initially, it might take some time to wrap your head around this but once you do, it comes naturally. You can just concentrate on what your client wants.

One important thing to note is, if you are working in a team, every team member should be aware of these rules and they must follow them. As I reiterated several times in the article, some of the rules cannot be enforced via any tooling. It should be a mutual understanding between the team members.

It is still a continuous learning process for me. Your feedback, opinion and suggestions are highly appreciated.

History

  • February 24, 2016 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Aditi Technologies
India India
Naveen Bhat is a self thought passionate programmer, he loves to write code which human can understand and computers can interpret! He started his professional career with .Net Framework 3.5 using C# as a language, but while in college he used to code in C, C++ and VB6.

He often shares his technical experience in his blog http://www.naveenbhat.in/ and here.

Later in his career, he mesmerized with the beauty of javascript development, which he used to hate once. This credit goes to Douglas Crockford for his book "Javascript Good Parts". Since then he started to explore javascript more and enjoys it as same as C#. So, Node.js became his another server-side development choice. While exploring more on javascript, he got attracted with functional programming. Soon he realized that his understanding of functional development was wrong and he has to re-learn it! Well, he is exploring Scala and Clojure now.

With his current employer, he got a chance to work with Microsoft Azure. Everyday he finds something new and exciting in it.

Comments and Discussions

 
GeneralMy vote of 2 Pin
Chamadness29-Feb-16 3:17
Chamadness29-Feb-16 3:17 
Do your homework, check for existing standards.
http://webcomponents.org/
GeneralRe: My vote of 2 Pin
K. Naveen. Bhat29-Feb-16 4:48
K. Naveen. Bhat29-Feb-16 4:48 
GeneralMy vote of 5 Pin
Pradip Jalu23-Feb-16 20:22
Pradip Jalu23-Feb-16 20:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.