Code Corner 5 The Nextmatch Widget - Nilpferdschaf/Egroupware-Doku GitHub Wiki
<< Prev.: Using Hooks and Accessing the Sidebox
The Nextmatch Widget
The Nextmatch widget is a part of eTemplate that will come in very handy whenever you want to display a list of items to the user. Nextmatch will make the list searchable and sortable and also makes it possible to select entries and perform actions on them. Using all these features requires minimal work from you, so you should definitely know how to use it.
Setup through the eTemplate editor
The Nextmatch widget is an eTemplate widget just like all the other UI elements. If you want to use one, you need add it to the UI via the eTemplate editor. Open the helloworld.db_controls template file and add a new widget after the debug text area. Set the Type of the widget to Nextmatch, its Name to "nm" and fill in the Options field with "helloworld.db_controls.rows". The result should look like the screenshot below.

Double click on the pink rectangle that appears right below the Nextmatch header when you hover over it. There will be an error "Error: Template not found". The widget looks for a template helloworld.db_controls.rows as specified by the Options field. Create this template to continue.
This template will contain the body of the Nextmatch widget. The body will basically be an HTML table with a row of table headers and multiple rows of table data. It consists of a grid with two rows, where the first row is made up of the table headers and the second row contains the items that will be displayed inside the table. Start creating the body by opening the widget editor and adding a new Nextmatch Sort Header. Set the Name to fname and the Label to "First name"

Now add more headers to the template. Their Name s and Label s are:
- sname, Second Name
- position, Position
- dep_name, Department
The Label texts are just what will be shown on the header, while the Name tells eTemplate where to put the data. When you are done adding the table headers, it should look like this:

The second row will be entirely made up of labels, because the table will display nothing but strings. It is important to set their _Name_s correctly. These are all instances of the following scheme: "${row}[key]". When eTemplate parses this string and creates the table rows, it will automatically replace "${row}" with an identifier for the data point that will be displayed in that row. The data points will later be given to the widget via arrays of $key => $value pairs and the $values of the different data points will be shown under the corresponding $key column. Keeping all this in mind, set the Name option of the labels in the second row to these values:
- ${row}[fname]
- ${row}[sname]
- ${row}[position]
- ${row}[dep_name]

When you export the final result, it should look like the screenshot below.

If you open the helloworld.db_controls template again, you will see the rows template inside the Nextmatch widget.

Initialisation, Sorting and Filtering
At this point the Nextmatch widget will not actually show up as part of the user interface when you open up your app. You need to properly initialize it from your codebase. A typical initialization function looks like this:
class.helloworld_ui.inc.php
function init_nextmatch(&$content)
{
$nm_settings = array(
'get_rows' => 'helloworld.helloworld_so.get_employees_nm',
'filter_label' => '',
'filter_help' => '',
'no_filter' => True,
'no_filter2' => True,
'no_cat' => True,
'lettersearch' => False,
'searchletter' => '',
'start' => 0,
'order' => 'sname',
'sort' => 'ASC',
'col_filter' => array(),
'row_id' => 'emp_id',
);
if (is_array ($content ['nm']))
{
$content ['nm'] = array_merge ($content ['nm'], $nm_settings);
}
else
{
$content ['nm'] = $nm_settings;
}
}
Add this function to helloworld_ui. There are all sorts of parameters being set here and there are many more. To find out what they all do, take a look at $egw_installation/etemplate/inc/class.etemplate_widget_nextmatch.inc.php. The most important one for now is get_rows. With this you can set a callback function that Nextmatch uses to fetch the data that will be displayed. Implementing this function is quite easy.
class.helloworld_so.inc.php
function get_employees_nm($query, &$rows, &$readonlys)
{
$rows = $this->get_employees ();
return count ($rows);
}
As suggested by the get_rows callback string, "helloworld.helloworld_so.get_employees_nm", this function needs to be part of helloworld_so. The return value should be the number of data points and the $rows parameter is supposed to contain all the data once the function has run through. $query has all kinds of information about the widget itself, like all the initialization you just set, but also information about the string that was inserted into the search field that is part of the widget. Finally, $readonlys makes it so you can not perform actions on some data rows.
With the current setup, Nextmatch will display only a minimal interface, containing nothing but a search field apart from the actual data. The init_nextmatch() functions needs to be called from the db_controls() function before the call to etemplate->exec().
class.helloworld_ui.inc.php
function db_controls($content = null)
{
// Do other stuff
$this->init_nextmatch ($content);
$tmpl = new Etemplate ('helloworld.db_controls');
$tmpl->exec ('helloworld.helloworld_ui.db_controls', $content, $selection_options);
}
You can also remove the code for dumping all the data to the debug text area. That is now the purpose of the Nextmatch widget. The minimal amount of setup is done now and you can reopen your app.

If you play around with the widget, you will notice that sorting and searching does not work yet. You need to tell the widget manually how to do this. Use the following code in helloworld_so to enable these features.
class.helloworld_so.inc.php
function get_employees_nm($query, &$rows, &$readonlys)
{
$rows = $this->get_employees ();
if ($query ['search'])
{
$rows = array_filter ($rows, $this->get_string_filter ($query ['search']));
}
usort ($rows, $this->get_sort ($query ['order']));
if ($query ['sort'] == 'DESC') $rows = array_reverse ($rows);
return count ($rows);
}
function get_string_filter($string)
{
$string = strtolower ($string);
return function ($cmp_arr) use ($string)
{
foreach ($cmp_arr as $entry)
{
if (strpos (strtolower ($entry), $string) !== false)
{
return true;
}
}
return false;
};
}
function get_sort($key)
{
return function ($a, $b) use ($key)
{
return strcmp (strtolower ($a [$key]), strtolower ($b [$key]));
};
}
The widget is now fully functional.

Performing Actions
Nextmatch also provides a convenient way for your users to select entries and perform actions on them. All it takes is a simple change in the initiation.
class.helloworld_ui.inc.php
function init_nextmatch(&$content)
{
$nm_settings = array(
// Set other options
);
$nm_settings['actions'] = array(
'select' => array(
'caption' => 'Select',
'default' => true,
'group' => 1,
'allowOnMultiple' => false,
'nm_action' => 'submit'
),
'delete' => array(
'caption' => 'Delete',
'default' => false,
'group' => 1,
'allowOnMultiple' => false,
'nm_action' => 'submit'
)
);
if (is_array ($content ['nm']))
{
$content ['nm'] = array_merge ($content ['nm'], $nm_settings);
}
else
{
$content ['nm'] = $nm_settings;
}
}
All this does is set $nm_settings['actions'] to an array that encodes the actions that should be displayed to the user. The array contains $key => $value pairs, where $key is an identifier for the action and $value an array containing its parameters. The parameters that are set by this array are also described in class.etemplate_widget_nextmatch.inc.php.
Right clicking on an entry will now reveal a context menu, where you can choose to select or to delete it. Both options will not do anything at the moment. The functionality behind them needs to be added first. The chosen nm_action for both actions in this case is submit. This means that once an action is clicked by a user, the callback that was given for the call to etemplate->exec() will be executed. The callback in this case is helloworld_ui->db_control().
class.hellworld_ui.inc.php
function db_controls($content = null)
{
$departments = $this->bo->get_department_names ();
if (is_array ($content))
{
if($content['nm']['action'] == 'delete')
{
$this->bo->delete_employee_by_id($content['nm']['selected'][0]);
}
if($content['nm']['action'] == 'select')
{
$data = $this->bo->read_employee($content['nm']['selected'][0]);
$content['select'] = $data;
$content['change'] = $data;
}
// Handle button presses
}
// Other Code
}
As you can see, eTemplate tells us which action was selected by writing its identifier to $content['nm']['action']. The selected rows are written to $content['nm']['selected']. The rows are identified by the field that is defined by the row_id parameter for the Nextmatch widget, which in this case is set to "emp_id". $content['nm']['selected'] will therefore be a list of emp_ids.
The functions helloworld_bo->delete_employee_by_id() and helloworld_bo->read_employee() only pass the function call through to the stack object.
class.helloworld_bo.inc.php
function read_employee($emp_id)
{
return $this->so->read_employee($emp_id);
}
function delete_employee_by_id($emp_id)
{
return $this->so->delete_employee_by_id($emp_id);
}
And finally, the implementation of the functions on the stack object.
class.helloworld_so.inc.php
function delete_employee_by_id($emp_id)
{
return $this->db->delete (
'employees',
array(
'emp_id' => $emp_id
)
);
}
function read_employee($emp_id)
{
$sel_result = $this->db->select (
'departments,
employees',
array(
'fname',
'sname',
'departments.dep_id as dep_id',
'position'
),
array(
'emp_id' => $emp_id
),
__LINE__,
__FILE__,
false,
'ORDER BY sname desc',
false,
0,
"WHERE employees.dep_id = departments.dep_id"
);
foreach ($sel_result as $record)
{
return $record;
}
}
With the implementation ready to go, it is time to test wether everything works. Just right click on any entry and select one of the actions.

Conclusion
The Nextmatch widget is an incredibly powerful tool for displaying lists of data to the user. You are now able to utilize this power in your own projects and display data in a nice-looking, user-friendly way. The next task is to polish the user interface even more with the power of images and CSS.
Download
You can download the result of this section here.