The last piece of this OO JavaScript UI is the bit that handles the form for
adding jobs to the JobManager
.
This is not a Real World
Example...
In a real-world application scenario, this would probably be a lot
more complicated — The current popular approaches would probably have it
posting data to a web-service end-point that would return a
JSON result-set that
would be (or include) the validated job-data, possibly with some additional data
indicating the success or failure of the post-attempt. That job-data would then
be passed to the JobManager
's addJob
method, and the
various displays would update accordingly.
The POC-level approach here doesn't do
any of that, though there are a couple of places where that sort of connection to
a server-side process could be introduced. Me, I think I would drop it into
JobManager.addJob
, but that's a knee-jerk thought, and other places
might well be better.
At any rate, here's the last piece of the puzzle...
The JobAdderForm
class
JobAdderForm
attaches to the job-entry form in the main UI:
JobManager.addJob
, and submitting it. Its
implementation looks like this:
function JobAdderForm( wrapper, form )
{
/*
* Provides the functionality for adding a job to the JobManager
*
* wrapper ........... (element or string, required) The element
* (or the ID of the element) that wraps all
* of the UI.
*/
// Processing that needs to happen before definition, if any
if ( typeof wrapper == 'string' )
{ wrapper = document.getElementById( wrapper ); }
if ( typeof form == 'string' )
{ form = document.getElementById( form ); }
// Instance properties:
// + Private properties
var _cancelControl = null;
var _contactField = null;
var _createControl = null;
var _form = form;
var _nameField = null;
var _numberField = null;
var _startControl = null;
var _wrapper = wrapper;
var _jobManager = new JobManager();
// - Find the job-fields and -actions elements, and assign
// them as appropriate
allChildren = wrapper.getElementsByTagName( '*' );
for( ci=0; ci<allChildren.length; ci++ )
{
child = allChildren[ ci ];
// data-jobfield
jobField = child.getAttribute( 'data-jobfield' );
switch( jobField )
{
case 'contact':
_contactField = child;
break;
case 'name':
_nameField = child;
break;
case 'number':
_numberField = child;
break;
}
// data-jobaction
jobAction = child.getAttribute( 'data-jobaction' );
switch( jobAction )
{
case 'cancelJob':
_cancelControl = child;
break;
case 'createJob':
_createControl = child;
break;
case 'startJob':
_startControl = child;
break;
}
}
// TODO: This is where any validation that the REQUIRED fields
// and actions exist should be done.
// Initialize as needed based on incoming arguments
// Instance methods:
// + public methods
this.addJobToManager = function()
{
/*
* Collects the job-data from the form, generates a new
* data-object with all the job-data in it, and adds it
* to the JobManager.
*/
newJob = {
'contact':_contactField.value,
'name':_nameField.value,
'number':_numberField.value,
};
_jobManager.addJob( newJob );
this.clearForm();
}
this.clearForm = function()
{
/*
* Clears all of the fields in the job-adder form
*/
if ( _contactField )
{ _contactField.value = ''; }
if ( _nameField )
{ _nameField.value = ''; }
if ( _numberField )
{ _numberField.value = ''; }
}
this.hide = function()
{
/*
* Hides the form
*/
_form.style.display = 'none';
}
this.show = function()
{
/*
* Shows the form
*/
_form.style.display = 'block';
}
// Event-like methods of the instance
// Processing that needs to happen before returning the instance, if any
// - Attach events to controls
if ( _cancelControl )
{
_cancelControl.jobAdderForm = this;
_cancelControl.addEventListener( 'click', function( event )
{
this.jobAdderForm.clearForm();
this.jobAdderForm.hide();
}
);
}
if ( _createControl )
{
_createControl.jobAdderForm = this;
_createControl.addEventListener( 'click', function( event )
{
this.jobAdderForm.addJobToManager();
this.jobAdderForm.hide();
}
);
}
if ( _startControl )
{
_startControl.jobAdderForm = this;
_startControl.addEventListener( 'click', function( event )
{
this.jobAdderForm.show();
}
);
// If there is a start-control, it's responsible for showing
// the form, so hide the form...
this.hide();
}
// Return the instance
return this;
}
The interface:
- Properties
-
_cancelControl
[private]- The UI control (a button, link, or other element) that cancels a job-submission.
_contactField
[private]- The text-field that will contain the
contact
property-value of the new job. _createControl
[private]- The UI control (a button, link, or other element) that
submits
a job-submission. _form
[private]- The outermost element of the form that contains all of the
_*Field
elements and the_*Control
elements associated with the form. This probably won't include the_startControl
(since it should reside outside the_form
, lest it be hidden). _jobManager
[private]- The
JobManager
object that new job-submissions will be sent to. _nameField
[private]- The text-field that will contain the
name
property-value of the new job. _numberField
[private]- The text-field that will contain the
number
property-value of the new job. _startControl
[private]- The UI control (a button, link, or other element) that starts the job-submission process (by showing the form).
_wrapper
[private]- The element that contains the entire UI for the
application, including the
_form
and all of the_*Control
elements.
- Methods
-
addJobToManager()
- Gathers the values from the
_*Field
elements, builds aJob
-compatible data-structure from them, and sends that toJobManager.addJob
. clearForm()
- Clears the
value
s of all the_*Field
elements. hide()
- Hides the
_form
element. show()
- Shows the
_form
element.
JobAdderForm
has no public properties — the wrapping
UI element, the form and all of the fields and controls are auto-assimilated using a
process much like the one built out for JobListView
early in the
initialization process of the object's construction. While it wouldn't be too
difficult, I think, to allow items to be specified as part of a parameters
argument (as has been the pattern elsewhere), but my gut feeling was to auto-connect
everything involved. That's simpler from an integration standpoint (and it was less
code for me to write, to boot).
All of its method are pretty straightforward, too. The only potential caveat is
that as things stand right now, there is no checking performed with regards to whether
the various _*Field
fields are found and associated. Again, since this
is pretty much a POC-level effort on my part, I simply didn't want to take the time
to implement that level of initialization-validation.
Putting it All Together
With all of the markup-template in place (with all of the necessary
data-*
attributes), and the full object-library included, the only
thing remaining is to initialize the set of objects. That's a fairly small chunk
of code:
// Create some test jobs for inclusion into the initial view
testJobs = [
{ 'number':'OOK00001', 'name':'Job Number One', 'contact':'Joe Smith' },
{ 'number':'WOK00001', 'name':'Job Number Two', 'contact':'John Doe' },
{ 'number':'OOK00002', 'name':'The Third Job', 'contact':'Richard Roe' },
{ 'number':'WOK00002', 'name':'Fourth Is This Job', 'contact':'Pat Jones' },
{ 'number':'OOK00003', 'name':'The Fifth Job', 'contact':'Richard Roe' },
{ 'number':'WOK00003', 'name':'Sixth Is This Job', 'contact':'Pat Jones' },
];
// Create the main job-manager instance:
jobManager = new JobManager( testJobs );
// Initialize the main job-list view, with all the initial sort-settings
currentJobListParameters = {
'top':'currentJobList',
'listElement':'managedJobsList',
'sortField':'number',
'sortOrder':'up',
};
mainListView = new JobListView( currentJobListParameters );
// Create the job-contact-list view, with all the initial sort-settings
jobContactListParameters = {
'top':'jobContactList',
'listElement':'managedContactlist',
};
jobContactList = new JobContactList( jobContactListParameters );
// Create the job-name-list view, with all the initial sort-settings
jobNameListParameters = {
'top':'jobNameList',
'listElement':'managedNameList',
};
jobNameList = new JobNameList( jobNameListParameters );
// Set up the job-counter(s)
mainJobCounter = new JobCounter( 'jobCounter' );
// Set up the job-adder form
jobAdderForm = new JobAdderForm( 'ui-example', 'newJobForm' );
// After all the initialization is done, and all the objects' inter-relationships
// are established, start the job-update cycle across all of the displays:
jobManager.jobsChanged();
And here it is, live:
Is This an MVC...?
Eh, maybe.
If it isn't a Model-View-Controller, it certainly shares some of that pattern's structure. Consider:
JobManager
is Model-like- It directly manages the data of the application;
- It also directly manages all of the logic (such as there is) of the application, and could be modified to manage any rules around the data, if any were needed;
- The display-objects (
JobDisplayList
,JobContactList
,JobNameList
, andJobCounter
) are definitely Views - That's what they do: Displaying data from (or derived from)
the
JobManager
JobAdderForm
arguably comes closest. Any of the various sort-controls
in the JobDisplayList
are also controller-like, and at some level,
so is the entire page that the UI resides in.
If you want to play with the code, it's available for download. It runs well enough on my local environment as a local HTML page and JavaScript file (that's in the current version of Google Chrome, your mileage may vary from browser to browser):
No comments:
Post a Comment