JavaScript is an object-oriented language, though most of the development
with it that I've seen doesn't take advantage of that. Granted, the syntax and
language-structure of JavaScript didn't lend itself well to real
OO coding-styles
until ECMAScript 6 came out with an official(?) class
definition syntax, along with extends
capabilities. There's still no interface or abstract class declaration,
though, as far as I've been able to tell. The underlying
ability to create classes, extend them, make them (kinda) abstract, and to
generate interface-like behavior has been available in the language for a long time,
though, even if the methods to achieve those ends were odd, cryptic, or could be implemented
in several different ways.
I look forward to the new class
and extends
being
standard in all the active browsers on the market. It's looking good for all the
modern browsers, at least on the desktop, but support on mobile and older
desktop browsers is not as good. With around 20% of the market share still showing
Internet Explorer 8, 9, 10 and 11 (as of the end of 2016 at
netmarketshare.com),
I'm not going to hold out high hopes for adoption of the new JavaScript capabilities for
a while yet...
But, since it is an object-oriented language, that means that there is at least some possibility for writing JavaScript code that follows good, solid OO-design principles — Things like design patterns, for example...
A Basic Form/UI and Requirements
So, indulge me in a little conscientous role-playing. Say there's a need for a UI and functionality to manage a list of jobs in a web-application, and a somewhat functional POC UI has to be built out.
In this scenario, a user is on some sort of dashboard page, and can see a list of current jobs that looks something like this:
The bold headings will (eventually) be clickable to toggle a sort of the listed items by that column's values, and theXbuttons will allow the user to remove a job from the list.
They can also enter data for a new job using a form that looks something like this:
Once the fields are populated and the user creates the job, it should be inserted into the job-list, paying attention to the current sort-order so as not to just be added to the end of the list. TheCurrent Jobsheader's job-count should also update when a job is added or removed.
There is also some talk about showing a distinct list of job-names and a separate list of job-contacts elsewhere on the dashboard (it's still on the same page, however):
Finally, for reasons unspecified (something, something, UI design, something
):
- The
Create a New Job
form is hidden until theAdd A New Job
button is clicked; - When the
Create a New Job
form is made visible, theAdd A New Job
button is hidden or disabled; - When the
OK
orCancel
buttons are clicked, theAdd A New Job
button is shown or enabled; and - When the
OK
orCancel
buttons are clicked, theCreate a New Job
form is hidden and all of its fields are cleared;
All of these processes would normally involve some sort of server connection, probably an AJAX/XHR call, that returned some data-set to be added to the active jobs, but the basic mechanism can be established without that server-side connection, as can the removal mechanism, so for the POC, all that needs to happen is being able to demonstrate the job-addition, -removal and -counting functionality that the UI needs.
There are a lot of moving parts here, so I'm going to spend this post mostly just delving into the design of the UI components. Getting into the weeds will follow starting with my next post. There may be several — as I'm writing this, I don't really know how much code this is going to entail. I'm also going not going to worry about member-scopes for this write-up. That is, I'm going to stick to public members across the board until or unless I can think of a good reason to try and make things private, and I can come up with a way to implement it — I know it can be done with one object-construction pattern, but I'm not sure if it can with the pattern that I'm currently planning to use...
What That's Going To Look Like
After some analysis, and deciding on the architecture (yes, it really is architecture, even if it is simple and in JavaScript), I can accomplish the POC with seven classes:
Job
to represent individual job-items;JobManager
to keep track of job-data, handle addition and removal of jobs from the data-set, and act as an originator of updates in an Observer relationship with various display-entities;JobListView
to manage the display of thebig job list
portion of the UI;JobContactList
to manage the display of job contact-names;JobNameList
to manage the display of job-names;JobCounter
to manage the display of job-counters; andJobAdderForm
to provide the functionality for adding a job to theJobManager
.
eye-candyof showing and hiding things when various buttons are clicked. That can be handled with some simple event-setup, so I'm going to set those aside for now.
I'm going to write the code for these classes in plain-old-JavaScript, with an eye towards making sure it'll run on older browsers. Converting the code into something that uses jQuery (or any of the other multitude of JavaScript frameworks should not be difficult for those so inclined. For now, though, I'm going to keep it as free-standing as I can, so noting but standard, local JS implementation.
I will, however, try to make the code reasonably smart — allowing it to do at least some automatic attachment to elements in the page markup.
The Job
class
A Job
is a dumb data object
— It's really just a
data-structure, with no methods or logic behind it other than a constructor. That
constructor, at least for a POC-level effort, doesn't need to do anything more than
set instance properties from their corresponding values in the parameters, so I won't
go into the class' interface in any detail. If this were to be implemented in a system
to be deployed, I'd want to add some type- and value-checking to those property-setting
processes, and some error-handling as well, in all likelihood, but that's more effort
than I'm going to put in here and now. Here's the class-diagram for a Job
:
Job
from time to time, so I added a toString
method to it after the initial
class-diagram had been drawn up. It's nothing too fancy, returning an XML-like tag
along the lines of
<Job number="[number]" name="[name]" contact="[contact]" />but it did prove invaluable when I found that I needed to be able to see what was happening while debugging some methods in other classes.
The JobManager
class
First up, I'm going to define the class that will be used to keep track of
all of the jobs in the UI. That class, JobManager
is mostly a
data-repository, but one that participates in an Observer
pattern relationship with any number of display-oriented objects. As a
class-diagram, it looks like this (at first blush — as development goes
on, I may want/need to add or remove members):
- Properties
-
_displays
[private]- The collection of display-related objects that will be notified
when the instance's
jobsChanged
method is executed. _jobs
[private]- The collection of
Job
objects that the instance is keeping track of.
- Methods
-
.ctor( jobs )
- The object constructor/initializer. Returns a new instance if one does not already exist, otherwise returns the previously-created instance.
addDisplay( display )
- Adds a display-related object to the instance's
displays
collection addJob( job, updateNow )
- Adds a
Job
object to the instance'sjobs
collection. compareJobs( job1, job2 )
- Compares two
Job
(or equivalent) objects, returningtrue
if they have the same field-values,false
otherwise getSortedJobs( sortKey, sortOrder )
- Gets a copy of the main job-list (because each
different display may need a different sort-order!), sorted by
the
sortField
specified, in the order specified bysortOrder
. jobsChanged()
- Calls the
onJobsChanged
methods of all the display-related objects in the instance'sdisplays
. removeDisplay( display )
- Removes the specified display-related object from the instance's
displays
collection, which means that it will no longer get updates fromjobsChanged
. removeJob( job, updateNow )
- Removes the specified
Job
object from the instance'sjobs
collection
JobManager
should probably be a Singleton:
The underlying rationale for this is that there should (probably) be only one job-data
set that's being tracked and manipulated. The .ctor
(constructor) method
is where I'm planning to manage that need.
The JobListView
class
JobListView
is a display-control mechanism that is part of an
Observer realtionship with the JobManager
on the page. When a job is
added to or removed from the JobManager
, it will update the displayed
list of Job
s, keeping the specified sort-order for items in the display.
It also provides a process for attaching sort-control functionality to arbitrary
elements on the page so that, when clicked, the display will be re-sorted according
to the sort-criteria relevant to the sort-control.
- Properties
-
_jobManager
[private]- The
JobManager
where the display's jobs can be found and manipulated. _listElement
[private]- The display-element that all of the list-members will be shown in.
_listItemTemplate
[static]- The HTML template to be used in generating a list-member's display in the list.
sortControls
- An array that keeps track of all the sort-controls for the display.
sortField
- The name of the
Job
property that is used in the sort-process for the display. sortOrder
- The
sort-direction
that is used in the sort-process for the display. _top
[private]- The display-element that wraps all of the UI elements for the display; the display's root tag, as it were.
- Methods
-
.ctor( parameters )
- The object constructor/initializer. The
parameters
argument is an object that is expected to contain the following fields:listElement
- The element that will contain the list-items, which will be cleared of all child nodes and re-populated as needed/arbitrarily.
sortField
- The name of the Job data-field to be used for sorting list-members in the display.
sortOrder
- The sort-order to be used for sorting list-members in the display.
top
- The element that wraps all of the UI managed by this object.
clearDisplay()
- Removes all child element from the list-view.
displayItems()
- Populates the listElement with zero-to-many job-displays using the jobManager's jobs and the current sort-field and -order.
getJobDisplay( job )
- Generates and returns a sequence of nodes based on the class-level HTML template, populated with the specified job's data, ready to be appended to the list-display element.
registerSortControl( element, field )
- Registers a sort-control element with the instance.
setSortCriteria( field, order )
- Sets the display's sort criteria (which field, and in what order), and triggers a display-update with those criteria.
setListItemTemplate( markup )
[static]- Sets the class-level display-list member HTML template to use to display item data
- Event-like Methods
-
onJobsChanged()
- Clears the current list-display of all members, and re-creates it
with the current data. Called by
JobManager.jobsChanged
as part of their Observer-pattern relationship.
The JobContactList
class
JobContactList
is a display-control object (like
JobListView
) that displays a sorted list of all job-contacts across
all the jobs in the JobManager
.
- Properties
-
_jobManager
[private]- The
JobManager
where the display's jobs can be found. _listElement
[private]- The display-element that all of the list-members will be shown in.
_listItemTemplate
[static]- The HTML template to be used in generating a list-member's display in the list.
sortField
- Description
sortOrder
- Description
- Methods
-
.ctor( parameters )
- The object constructor/initializer.
listElement
- The element that will contain the list-items, which will be cleared of all child nodes and re-populated as needed/arbitrarily.
top
- The element that wraps all of the UI managed by this object.
clearDisplay()
- Removes all child element from the list-view.
displayItems()
- Populates the listElement with zero-to-many job-displays using the jobManager's jobs and the current sort-field and -order.
getJobDisplay( job )
- Generates and returns a sequence of nodes based on the class-level HTML template, populated with the specified job's data, ready to be appended to the list-display element.
- Event-like Methods
-
onJobsChanged()
- Clears the current list-display of all members, and re-creates it
with the current data. Called by
JobManager.jobsChanged
as part of their Observer-pattern relationship.
The JobNameList
class
Apart from the fact that it focuses on displaying the name
values of
all the Job
s in the JobManager
, JobNameList
is pretty much identical to JobContactList
.
- Properties
-
_jobManager
[private]- The
JobManager
where the display's jobs can be found. _listElement
[private]- The display-element that all of the list-members will be shown in.
_listItemTemplate
[static]- The HTML template to be used in generating a list-member's display in the list.
sortField
- Description
sortOrder
- Description
- Methods
-
.ctor( parameters )
- The object constructor/initializer.
listElement
- The element that will contain the list-items, which will be cleared of all child nodes and re-populated as needed/arbitrarily.
top
- The element that wraps all of the UI managed by this object.
clearDisplay()
- Removes all child element from the list-view.
displayItems()
- Populates the listElement with zero-to-many job-displays using the jobManager's jobs and the current sort-field and -order.
getJobDisplay( job )
- Generates and returns a sequence of nodes based on the class-level HTML template, populated with the specified job's data, ready to be appended to the list-display element.
- Event-like Methods
-
onJobsChanged()
- Clears the current list-display of all members, and re-creates it
with the current data. Called by
JobManager.jobsChanged
as part of their Observer-pattern relationship.
The JobCounter
class
Displays a count of current Job
s in the JobManager
that
updates as jobs are added or removed.
- Properties
-
_element
[private]- The document element whose content will be updated as jobs are added
to or removed from the
JobManager
. _jobManager
[private]- The
JobManager
where the display's jobs can be found.
- Methods
-
.ctor( element )
- The object constructor/initializer.
- Event-like Methods
-
onJobsChanged()
- Updates the
_element
witht he current count of jobs. Called byJobManager.jobsChanged
as part of their Observer-pattern relationship.
The JobAdderForm
class
JobAdderForm
defines a class that keeps track of all of the
form-fields and action-elements (buttons) involved in the UI, connects those
elements with other elements and objects (like the JobManager
) to
allow the form
in the UI to add jobs to the JobManager
. It
also proves some of the eye-candy
noted in the initial POC requirements
(showing and hiding the job-adding form). I wasn't going to bother with that
initially, but it was simple to implement, and it just made sense to wire the
show/hide functionality up to the UI elements rather than assuming that it
would be handled later. It could always be changed...
- Properties
-
_cancelControl
[private]- The element (a button) that the user clicks to cancel a job-creation.
_contactField
[private]- The form-field that is the source for a new
Job
'scontact
value. _createControl
[private]- The element (a button) that the user clicks to submit a job-creation.
_form
[private]- The outermost element that contains all of the
_*Field
elements of the form. Also the chunk of the UI that will be shown or hidden by various actions (buttons). _jobManager
[private]- The
JobManager
that new jobs will be added to when they are created. _nameField
[private]- The form-field that is the source for a new
Job
'sname
value. _numberField
[private]- The form-field that is the source for a new
Job
'snumber
value. _startControl
[private]- The element (a button) that the user clicks sow the job-creation form.
_wrapper
[private]- The element that wraps all of the UI —
including the
_form
and all_*Control
and_*Field
elements
- Methods
-
.ctor( wrapper, form )
- The object constructor/initializer.
addJobToManager()
- Builds the data-structure for a new job, then hands it off to the
JobManager
to be added. clearForm()
- Clears the
value
s of all of the_*Field
elements hide()
[scope]- Hides the
_form
element show()
[scope]- Shows the
_form
element
Next time, I'll start digging in to the actual implementation of these classes, and show how I'm integrating them into a process that allows the page mark-up to control what the UI looks like while keeping that separate from the logic embedded in these classes.
No comments:
Post a Comment