A Polymorphic Commentable Widget?
To be specific, it is:
- the commenting functionality is not tied to any specific object. In other words, the same code can be used to comment against an Article, a Cat Photo or any business object without any modification
- the same code should display previous comments in a nested fashion
- provide a facility to reply to a comment
- provide a facility to edit or delete any of your own comments
Source Code and Demo
A live comment can be seen against the Equality Bill. Feel free to register (no email/identity information required) to add your comments.
The Backbone.js Way
Dynamic HTML elements are often rendered and appended into specific locations in the DOM, often specified by a DOM Element ID or class name(s). This is not too disimilar from how jQuery, for example, is traditionally used to manipulate/render dynamic HTML.
A sample code fragment in Backbone.js could look something like:
- the code is still coupled to the DOM; in the above code the
replyHTML fragment is dependant on
.content-containerDOM Element’s class. Unfortunately and in personal experience, DOM structure and class names ALWAYS change
Introducing the AngularJS Way
I must confess that when I first started converting the Commentable Backbone.js widget into AngularJS I was still fixed in a DOM mindset; my Directive was full of CSS selectors that defined DOM targets for HTML fragment placement. This quickly became cumbersome and I found myself working against AngularJS and towards a dead end.
AngularJS advocates a Declarative approach for building UIs. The following example should clarify this:
- it is quickly apparent from the above code that two states exist in a comment: editing and replying. A further test
comment.mineis made to determine whether the current logged in user owns the comment
- it is also quickly apparent how each of these three states influence Element visibility
- crucially, all state based visibility logic is not tied to the DOM. A designer can add/remove CSS classes, even move elements with little risk of breaking functionality
reply.html are trivial:
A Reusable Component
The goal is to develop a Directive that injects a Commentable behaviour into a page.
The above is a sample fragment of a Tab component with a Summary and a Comments tab. Crucially Comments are injected via the
commentable="bill" attribute. Since polymorphic behaviour is
required, the following additional data is supplied:
- commentable=”bill” . Here
billis an object defined in the current scope
- commentable-type=”Bill” defines the object model name, required when comments are created on the server
- src=”billCommentsPath” defines the server endpoint from where existing comments will be retrieved for
bill. This is defined in the current scope
- logged-in=”loggedIn()” the directive delegates the
loggedIn()function call to the relevant scope in the inheritance chain
To summarise, the following line adds a commenting system to any database backed object being represented on screen:
The AngularJS Directive
The Commentable Directive’s goals:
- responsible for rendering comments for the object called
billin this instance
- manage the comment’s state. In essence, a
billdoesn’t or shouldn’t care that a) it has comments nor b) what state each of these comments are in
- be completely isolated in the sense that scope property and function collisions are avoided for easier reusability
The Directive references the following template:
Crucially, the Commentable directive defines its own Controller which controls its templates in isolation to any Controllers on the page.
Further, the Directive also defines an inline Service object,
commentable, to which it delegates all persistence activity. This can be advantageous in the future if the Directive is to be injected with a
Summary and Conclusion
The more I use AngularJS Directives the more I feel like this.
Directives greatly aid in approaching UI development from a component/widget mindset, where components are defined in Directives and/or Directive hierarchies and are easily re-used due to their highly decoupled nature. AngularJS greatly aids this by introducing concepts such as Scope Isolation.
This approach has been in use for employed in desktop software development where UI elements are often defined as standalone components. We are starting to see a this approach in web development with multiple techniques, like Shadow Dom, converging on this same principle.
The opinions expressed here represent my own and may or may not have any basis in reality or truth. These opinions are completely my own and not those of my friends, colleagues, acquaintances, pets, employers, etc...