Xataface  2.0alpha2
Xataface Application Framework
 All Data Structures Namespaces Files Functions Variables Groups Pages
Creating Custom Widgets
Contents of this Section:
  1. Step 1: Create a Widget Handler
  2. Step 2: Register the Widget Handler
  3. Step 3: Use The Widget For a Field
  4. Step 4: Add Javascripts and CSS Files
  5. Step 5: Creating a Javascript Bootstrap For the Widget
  6. Step 6: Registering the Javascript Bootstrap
  7. Step 7: Trying Out Our Widget

The previous sections have been laying the foundation for this section, in what will be our first "useful" module. Xataface comes with a set of built-in widgets that are quite useful, but once in a while, you will likely find yourself in need of a widget that doesn't yet exist. The internet is full of jQuery plugins that can easily be converted into Xataface widgets with very little effort.

Some Javascript plugins that have already been adapted into Xataface widgets include:

The Strategy

The general strategy for creating a widget is:

  1. Add CSS classes and HTML attributes to a simple text input so that we can access all necessary information from javascript
  2. Transform text inputs matching the given attributes or CSS classes into more complex widgets using Javascript.

For this tutorial we will be creating a Color Picker widget based on the eyecon colorpicker.

Step 1: Create a Widget Handler

Xataface forms are based on HTML_QuickForm for its base set of widgets, but it uses a Widget Handler as a wrapper around these widgets as a means of configuring them. These wrappers are known as Widget handlers and they are managed by the Dataface_FormTool class.

A widget handler has to be able to do at least three things:

  1. Build a widget (returning an HTML_QuickForm_element object.)
  2. Retrieve a value stored in the widget.
  3. Store a value in the widget.

In most cases you can get by with only defining a single method (buildWidget()) in the widget handler and let Xataface just use its default handler to load and store the values in the widget.

Creating the Widget Handler Script

There is no set place where the widget handler needs to go. Let's just add it to the root directory of our module, and call it colorpicker_widget.php. Add the following content to this file:

class Dataface_FormTool_colorpicker {
// Builds a widget for the given record
function buildWidget($record, &$field, $form, $formFieldName, $new=false){
// Obtain an HTML_QuickForm factory to creat the basic elements
// Create a basic text field
$el = $factory->addElement('text', $formFieldName, $widget['label']);
return $el;
}
}
See Also
WidgetHandler::buildWidget() for details of the parameters of this method and how they should be used.

The above widget handler doesn't do anything fancy yet. It simply creates a plain text field. After we have our widget working as a plain text field, we'll go back and transform it into a color picker.

Attention
I chose to base this widget on the 'text' widget because text widgets are easy to work with. If your widget will be storing multiple lines of data, you should use a textarea widget as a base instead to prevent data from being clipped inadvertently. In our case we are only storing colors though so a text widget makes sense as a base to build upon.

Step 2: Register the Widget Handler

Before we can use our new widget, we need to register the widget handler with the form tool. We'll make use of the Dataface_FormTool::init event that is fired when the form tool is first created. This allows us to avoid loading the FormTool into memory unnecessarily. We'll do all of this in our module class's constructor as this is guraranteed to be executed at the beginning of every request that the module is enabled for.

class modules_hello_world {
// The constructor for the module class. Executed at the beginning
// of each request
public function __construct(){
if ( !class_exists('Dataface_FormTool') ){
// If the formtool is not loaded then we don't
// want to load it here... we'll just register
// the _registerWidget() method to run as soon
// as the FormTool is loaded.
$app->registerEventListener(
'Dataface_FormTool::init',
array($this, '_registerWidget')
);
} else {
// If the formTool is already loaded, then we'll
// register the widget directly
$this->_registerWidget(Dataface_FormTool::getInstance());
}
}
// Function to register our widget with the form tool.
public function _registerWidget(Dataface_FormTool $formTool){
'colorpicker',
dirname(__FILE__).'/colorpicker_widget.php',
'Dataface_FormTool_colorpicker'
);
}
}

Step 3: Use The Widget For a Field

Now that we have registered our widget, we should be able to use the widget. Let's set up a simple table in the database for this purpose:

create table color_profile (
profile_id int(11) not null auto_increment primary key,
profile_name varchar(32),
foreground_color varchar(32),
background_color varchar(32)
)

And we'll create a fields.ini file for this table in the myapp directory:

myapp/tables/color_profile/fields.ini

We will use our new colorpicker widget as the widget for both the foreground_color and background_color fields:

[foreground_color]
widget:type=colorpicker
[background_color]
widget:type=colorpicker
Try Out Our Widgets

Now that all of the pieces are in place, let's load up a new record form for the color_profile table and see what it looks like.

Note
You can load the new record form for the color_profile table by pointing your web browser to your application's index.php page with the GET parameters -table=color_profile and -action=new

If everything is working correctly, you should see regular text fields for the foreground_color and background_color fields. This may be slightly disappointing, but this is what we want. We'll convert these into more interactive color-picker widgets in the next few steps. At this point we just want to be sure that our custom widget handler is working.

Attention
If you do not see a form like the one above but instead see an error message, you'll need to go back through your code to make sure that there are no typos... Get this working before moving onto the next steps.

Step 4: Add Javascripts and CSS Files

Before we can convert our text fields into color pickers, we need to make sure that the javascripts, CSS files, and images for the colorpicker widget that we are planning to use are accessible to our project. Specifically we need to add these files to our project in such a way that the Dataface_JavascriptTool and Dataface_CSSTool can work with them.

Downloading The Color Picker Files

We start by downloading the Color Picker source files from the EyeCon website. After unzipping the ZIP file we should have the following directory structure:

So we have 3 types of content to contend with:

  1. CSS Files
  2. Images
  3. Javascript Files
Attention
If possible it is important to keep all images in the same place relative to the CSS files or some of the path dependencies may be broken at runtime. Alternatively you can go through the CSS files to alter the paths to all referenced images to mark the new location.
Adding the Javascript Files To Our Module

The first thing we'll do is to add the Javascript files to our module. In order to keep things separated, we'll place them inside our namespace inside the js directory. A good location might be

js/xataface/modules/hello_world/colorpicker
Attention
We will copy all of the javascript files except jquery.js because we have our own version of jQuery with the Xataface distribution that we'll be using instead.

Once we have copied all of the javascript files we should have the following directory structure inside our module:

Adding the CSS Files and Images To Our Module

Similarly, we'll add the CSS files in a namespaced directory structure so that we can reference them uniquely. We'll place both the css and images directories inside the

css/xataface/modules/hello_world/colorpicker

directory.

When we're done copying these files, the directory structure of our module will look like:

Step 5: Creating a Javascript Bootstrap For the Widget

Now that we've added all of the necessary javascript and CSS files for the colorpicker widget, we need to create our own thin bootstrap code that converts our text fields into color pickers.

Before we can do this, however, we need to add a CSS class or attribute to our text fields so that we are able to identify the colorpicker widgets in Javascript. We modify our Widget handler (colorpicker_widget.php) in order to accommodate this:

class Dataface_FormTool_colorpicker {
// Builds a widget for the given record
function buildWidget($record, &$field, $form, $formFieldName, $new=false){
// Obtain an HTML_QuickForm factory to creat the basic elements
$atts = array(
'class' => 'xf-colorpicker'
);
// Create a basic text field
$el = $factory->addElement('text', $formFieldName, $widget['label'], $atts);
return $el;
}
}

All we've done here is create an $atts array where we specify a class attribute that is to be added to all color picker fields. We then add this as the 4th parameter of the $factory->addElement() call (a call to HTML_QuickForm::addElement).

In short, all this does is add the css class xf-colorpicker to all instances of our colorpicker widget. This will allow our Javascript bootstrap code that we're about to write to identify which fields are colorpicker fields, and hence which ones should be "decorated".

We'll place our Javascript bootstrap code into a new file. It's location doesn't matter as long as it is located somewhere inside the module's js directory. We'll place it at

js/xataface/modules/hello_world/colorpicker_widget.js

Place the following code inside this file:

//require <jquery.packed.js>
//require <xataface/modules/hello_world/colorpicker/colorpicker.js>
//require-css <xataface/modules/hello_world/colorpicker/css/colorpicker.css>
(function(){
var $ = jQuery;
// We wrap our code in registerXatafaceDecorator so that it is executed
// whenever new content is added to the page. This is important if you
// want your widget to work in compound widgets like the grid widget.
registerXatafaceDecorator(function(node){
// Find all elements with the xf-colorpicker CSS class
// and convert them into a ColorPicker()
$('input.xf-colorpicker', node).ColorPicker({
onSubmit: function(hsb, hex, rgb, el) {
$(el).val(hex);
$(el).ColorPickerHide();
},
onBeforeShow: function () {
$(this).ColorPickerSetColor(this.value);
}
});
});
})();

There isn't much in ths bootstrap file, but there's a lot going on.

  1. The first three lines declare the other javascript and CSS files that should be included. This is where we include jQuery and the ColorPicker javascript and CSS files that we just installed in the last step.
  2. We wrap the whole thing inside an anonymous Javascript function to ensure that we don't pollute the global namespace with our code.
  3. We wrap our "decoration" code inside the registerXatafaceDecorator function so that it will be executed for any part of the page that is added even after page load. This is important if you want your widget to work properly inside compound widgets like the grid widget.
  4. We apply the ColorPicker() jQuery plugin to all elements with the xf-colorpicker CSS class.

Step 6: Registering the Javascript Bootstrap

Now that we have created the Javascript bootstrap we need a way to ensure that it will be run whenever a form is rendered. Essentially, this just requires us to import the colorpickier_widget.js using Dataface_JavascriptTool::import(), but we would prefer to do this in the most efficient way possible, such that the file is only imported if the colorpicker is going to be rendered. And taking an additional step back, we also need to ensure that our module's js and css directories are registered with the application when necessary. Otherwise our import won't work.

In the previous section we used Dataface_JavascriptTool::addPath() method to register our js directory. However we only did this inside the hello_world action. This code is only executed for that one action and won't be executed for the edit form or other Xataface actions that may require the colorpicker widget to be rendered. A simple solution would be to perform this registration inside our module's constructor, but then we may be adding unnecessary overhead to our application since most of the application has no need for our scripts.

To succinctly rephrase, our problem is:

  1. We want our Javascript files to be available to the Dataface_JavascriptTool when they are needed and only when they are needed. (I.e. we don't want to load them if they are not needed as it adds to the overhead of the application).

Properties of the ideal solution to this problem are outlined in the wording of the problem itself. Namely, the ideal solution should:

  1. Make the CSS and Javascript files available to any part of the application that requires them.
  2. Not load the CSS or Javascript files nor register the css or Javascript directories with Dataface_JavascriptTool unless they are needed.

To achieve this goal we will encapsulate the code that registers these files into our module's class as a method, and use a flag to ensure that they are loaded at most once.

hello_world.php:

<?php
class modules_hello_world {
// ... Other contents of this class hidden here ......
private $jsLoaded = false;
// Function to register the module's javascript and css
// files
function loadJs(){
if ( !$this->jsLoaded ){
$this->jsLoaded = true;
$jsTool->addPath(
dirname(__FILE__).'/js',
DATAFACE_SITE_URL.'/modules/hello_world/js'
);
$cssTool->addPath(
dirname(__FILE__).'/css',
DATAFACE_SITE_URL.'/modules/hello_world/css'
);
}
}
}

Having this functionality encapsulated in a single method of our module makes it a simple matter to register the module's javascript and css assets on demand.

Note
You can always access the module file using Dataface_ModuleTool::loadModule() e.g.
$mod = Dataface_ModuleTool::getInstance()->loadModule('modules_hello_world');
$mod->loadJs();

Now to complete our solution, let's add load our bootstrap javascript code from our widget handler (hello_world_widget.js):

class Dataface_FormTool_colorpicker {
// Builds a widget for the given record
function buildWidget($record, &$field, $form, $formFieldName, $new=false){
// Obtain an HTML_QuickForm factory to creat the basic elements
$atts = array(
'class' => 'xf-colorpicker'
);
// Create a basic text field
$el = $factory->addElement('text', $formFieldName, $widget['label'], $atts);
// Load our bootstrap colorpicker javascript code so that it runs
// on page load.
$mod = Dataface_ModuleTool::getInstance()->loadModule('modules_hello_world');
$mod->loadJs();
'xataface/modules/hello_world/colorpicker_widget.js'
);
return $el;
}
}

All we've done here is to call our loadJs() method on the hello_world module class to load our javascript and css directories. Then we imported the bootstrap code so that it will run on page load. Notice that we do all this inside the buildWidget() method of our widget handler. This ensures that it will only run the bootstrap code if a colorpicker widget has been built (and thus likely will exist on the final page output).

Step 7: Trying Out Our Widget

Now if you reload the new record form for the color_profile table and click on one of the colorpicker fields, you should see something like:

If you don't see anything like this, there see module_developer_guide_widget_troublueshooting

Troubleshooting

If you run into problems along the way in this tutorial it really helps to have a good Javascript development tool so that you can easily check things like attributes and classes on HTML elements on the page. In addition you will want to keep an eye on the Javascript error log to see if any errors are reported. Especially in a tutorial such as this, with multiple parts, there are many places where you can go wrong. If a file is miss-named you may not see an error message - but the file will just not be picked up at all.

If you get really stuck, the best thing to do is seek help in the forum.

Download This Module

You can download the source for this module here.