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.
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:
- Each component is assigned with a responsibility and they serve just that.
- 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:
- Each component can have a state.
- Only the component has the rights to alter its state.
- 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.
- 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).
- 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).
- Each method in the component should have only one responsibility (Single Responsibility Principle).
- 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:
- System/Framework Component
- 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.
- It should have a method to show the notification with the given message.
- It should have a method to hide.
- It should have a UI control using which user can trigger the hide.
- 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:
var notification = (function () {
var self = this;
var notifcationDiv = "#notification";
var messageSpan = "#msg";
var close = "#close";
var showSuccess = function (message) {
$(messageSpan).text(message);
$(notifcationDiv).addClass('alert-success').show();
};
var showError = function (message) {
$(messageSpan).text(message);
$(notifcationDiv).addClass('alert-danger').show();
};
var showWarning = function (message) {
$(messageSpan).text(message);
$(notifcationDiv).addClass('alert-warning').show();
};
var hide = function () {
if (this.onClose && typeof this.onClose === 'function') {
this.onClose();
}
$(notifcationDiv).removeClass().addClass('alert').hide();
};
var show = function (action, message) {
hide.call({});
if (this.onOpen && typeof this.onOpen === 'function') {
this.onOpen();
}
action(message);
};
$(close).click(function () {
hide.call(self);
});
self.showSuccess = function (message) {
show(showSuccess, message);
};
self.showError = function (message) {
show(showError, message);
};
self.showWarning = function (message) {
show(showWarning, message);
};
self.hide = hide;
self.onOpen = null;
self.onClose = null;
return self;
}());
And here is its state - The markup for notification
<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:
notification.onOpen = function(){ console.log('open...') }
Let's register a handler for onClose
event as well:
notification.onClose = function(){ console.log('close...') }
Now type this:
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:
- Each component can have a state.
Yes we have (The markup) - 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 :( - 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. - 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. - 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. - Each method in the component should have only one responsibility (Single Responsibility Principle).
Yes, each methods in the notification has only one responsibility - 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
- Creating a business component
- Dealing with backend services
- 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