Introduction
Grid’s are used everywhere and one thing the Zend Framework really lacks is a useful view helper to quickly generate grids and their stores. I saw that Matthew Weier O’Phinney had a proposal for a dojox.grid.DataGrid view helper but no work had been done on it. I send him a tweet and learned that he had intended to finish it but is currently too busy to work on it so I wrote a few view helpers to follow his proposal. The dataStore view helper is fairly simplistic but the dataGrid view helper has quite a bit to it. I plan on refactoring the dataGrid view helper to add support for enhancedGrid and treeGrid but I wanted to share what I had done already. Both view helpers follow the use-cases as presented by Matthew in his dojox.grid.DataGrid view helper proposal.
The dataStore() helper
The dataStore() helper provides support for generating programmatic/declarative data stores and requires an id as well as a dojoType.
Usage
=$this->dataStore('store', 'dojox.data.QueryReadStore', array('url' => '/path/to/json'));?>
Code
dojo->requireModule($dojoType); // Programmatic if ($this->_useProgrammatic()) { if (!$this->_useProgrammaticNoScript()) { $this->dojo->addJavascript('var ' . $id . ";\n"); $js = $id . ' = ' . 'new ' . $dojoType . '(' . Zend_Json::encode($attribs) . ");"; $this->dojo->_addZendLoad("function(){{$js}}"); } return ''; } // Set extra attribs for declarative if (!array_key_exists('id', $attribs)) { $attribs['id'] = $id; } if (!array_key_exists('jsId', $attribs)) { $attribs['jsId'] = $id; } if (!array_key_exists('dojoType', $attribs)) { $attribs['dojoType'] = $dojoType; } return '_htmlAttribs($attribs) . ">\n"; } }
The dataGrid() helper
The dataGrid helper provides support for generating programmatic/declarative dataGrids. Currently, the only supported grid is dojox.grid.DataGrid but I’ll be adding EnhancedGrid and TreeGrid in the future.
Usage
dataStore('store', 'dojox.data.QueryReadStore', array('url' => '/admin/articles/grid.json')); $grid = $this->dataGrid('myGrid', array( 'selectionMode' => 'none', 'style' => 'height: 350px; width: 100%;', 'store' => 'store', 'fields' => array( array('field' => 'title', 'label' => 'Title', 'options' => array('formatter' => 'titleFormatter', 'width' => '60%')), array('field' => 'published', 'label' => 'Published', 'options' => array('formatter' => 'primaryFormatter', 'width' => '10%')), array('field' => 'updated_at', 'label' => 'Updated', 'options' => array('width' => '15%')), array('field' => 'created_at', 'label' => 'Created', 'options' => array('width' => '15%'))))); ?> scriptCaptureStart('dojo/method', 'onMouseOver', array('args' => 'row'));?> var g = dojo.byId("grid-menu-item-"+row.rowIndex); if (g) { dojo.toggleClass(g, "hide-text"); } scriptCaptureEnd();?> scriptCaptureStart('dojo/method', 'onMouseOut', array('args' => 'row'));?> var g = dojo.byId("grid-menu-item-"+row.rowIndex); if (g) { dojo.toggleClass(g, "hide-text"); } scriptCaptureEnd();?>
Code
render(); } /** * DataGrid view helper. * * @param string $id JavaScript id for the data store. * @param array $attribs Attributes for the data store. */ public function dataGrid($id = '', array $attribs = array()) { if (!$id) { throw new Zend_Exception('Invalid arguments: required jsId.'); } if (!array_key_exists('id', $attribs)) { $attribs['id'] = $id; } if (array_key_exists('fields', $attribs)) { foreach ($attribs['fields'] as $f) { $this->addField($f['field'], $f['label'], isset($f['options']) ? $f['options'] : array()); } unset($attribs['fields']); } $this->_attribs = $attribs; return $this; } /** * Static setter for theme. * @param string $theme Name of the current them. */ public static function setTheme($theme) { self::$_theme = $theme; } /** * Static getter for theme. * @return string */ public static function getTheme() { return self::$_theme; } /** * Static setting for script base. * @param string $scriptBase Path to the base script directory. */ public static function setScriptBase($scriptBase) { self::$_scriptBase = $scriptBase; } /** * Static getter for script base. * @return string */ public static function getScriptBase() { return self::$_scriptBase; } /** * Adds field data. * @param string $field Field name. * @param string $label Label of the field. * $param array $attribs Optional parameters for the field. */ public function addField($field, $label, array $attribs = array()) { $this->_fields[] = array( 'label' => $label, 'attribs' => array_merge(array('field' => $field), $attribs)); return $this; } /** * Adds script captures. * @param array $data */ public function addScriptCapture(array $script = array()) { if (!array_key_exists('data', $script)) { throw new Zend_Exception('Script data must include keys data and attribs'); } $this->_scriptCapture[] = $script; return $this; } /** * Begins script capturing. */ public function scriptCaptureStart($type, $event, array $attribs = array()) { if ($this->_captureLock) { throw new Zend_Exception('Cannot nest captures.'); } $this->_currentScript = array('type' => $type, 'event' => $event, 'attribs' => $attribs); $this->_captureLock = true; ob_start(); return; } /** * Ends script capturing. */ public function scriptCaptureEnd() { $data = ob_get_clean(); $this->_captureLock = false; $this->_currentScript['data'] = $data; $this->addScriptCapture($this->_currentScript); $this->_currentScript = array(); return true; } /** * Renders the grid based on programmatic setting. */ public function render() { $this->dojo->requireModule('dojox.grid.DataGrid'); // Setup the stylesheet base path if (null === self::getScriptBase()) { if ($this->dojo->useLocalPath()) { self::setScriptBase($this->dojo->getLocalPath()); } else { self::setScriptBase($this->dojo->getCdnBase() . $this->dojo->getCdnVersion()); } } $this->dojo->addStylesheet(self::getScriptBase() . '/dojox/grid/resources/Grid.css'); $this->dojo->addStylesheet( self::getScriptBase() . '/dojox/grid/resources/' . self::getTheme() . 'Grid.css'); // Programmatic if ($this->_useProgrammatic()) { if (!$this->_useProgrammaticNoScript()) { $this->_renderJavascript(); } return ''; } return $this->_renderDeclarative(); } /** * Renders a table for declarative grids. */ protected function _renderDeclarative() { if (!array_key_exists('jsId', $this->_attribs)) { $this->_attribs['jsId'] = $this->_attribs['id']; } $table = '
_htmlAttribs($f['attribs']) . '>' . $f['label'] . " | \n"; } $table .= "\t\t
---|
Final Thoughts
These view helpers are by no means complete but I will say that I’m currently using them in a production environment with no issues. I tested both declarative/programmatic usage and both functioned as expected. I also wanted to note that I’m by no means intending to step on Matthew’s toes but I know how busy he is with Zend Framework 2.0 (among other things). If anyone has any thoughts or use for this please comment below and, who knows, maybe I’ll make a proposal for it (or take over Matthew’s).
Tags: BSD licenses, JavaScript, Languages, php, Programming, Scripts, Source code, Zend Framework
This looks cool. But the helper should be universal enough to support jQuery as well.
@Ojjpo:
I disagree. jQuery is part of the extended Zend Framework and should have its own ZendX_jQuery_View_Helper.
note: I suppose the above classes should be called Zend_Dojo_View_Helper_DataStore and Zend_Dojo_View_Helper_DataGrid.
Your view helpers are much prettier than the grotesque crap I slopped together for my store and grid helpers, maybe I should use yours!
There is still quite a bit of work to do yet. Most notably, stylesheets and expanding for the new grids in Dojo 1.4.
Goog work!
Have you any unit tests for these classes?
It required for including to Zend Framework.
No, this is why I’ve renamed the library to SZend in order to differentiate it from the Zend core framework. My plan is to submit a proposal for these (and other) view helpers.
The updated source can be found at http://github.com/SpiffyJr/SZend
Spiffy – in order to use this then we need to create the SZend directory structure? Or should we just rename all SZend to Zend in the code? Thanks!
You can call them whatever you want as long as the helper path is setup properly.
Ahhh.. Initially I just dumped them into the regular zend path, so clearly I did that wrong
What should the path be?
If the prefix for the classes is SZend then you need to put the files in SZend/View/Helper. If you rename them all to Zend dropping them in Zend/View/Helper will work just fine.
For SZend, add the following to application.ini
———————————————————-
resources.view.helperPath.SZend_View_Helper = “SZend/View/Helper”
Awesomeness.
Well I can’t get the dialog or this to work
With the dialog I am using your example:
echo $this->dialog(‘myDialog’,…
Nothing happens, no errors or changes in display. Dojo is already working on the view I am testing this with. With the grid, I get an error when i create the grid, but don’t put any data in it and then echo it.
Fatal error: Call to undefined method Zend_Dojo_View_Helper_Dojo_Container::_addZendLoad() i
You should probably grab the latest code from http://www.github.com/spiffyjr/SZend.
yeah i did download the code from there last week thursday maybe? I think that is up to date.
is _addZendLoad() a valid function call? is there a better way to ask questions than spamming up your comments?
_addZendLoad() is a Zend Framework defined method. You can AIM me at SpiffyJr if you wish.
I sent you a msg on aim, but I commented out the code that uses programmatic, which then forces it to set up the rendering to use declarative. This works in displaying the grid headers at least.
Thanks for these great helpers. I found them before I started my own work.
I would like to move the DataGrid and DataStore to my library’s components. The idea is to create objects by an extending a Model (like with mappers or DbTable) or using a Zend_Config (configs/DataGrid/foo.ini).
Send me an email if you want me to keep you updated.
Cheers
Have you done any refactoring yet? I’m excited about the enhancedGrid features.
How about parsing an array to the ‘query’ parameter which will be automatically parsed to a json notation by the view helper?
dataGrid(‘myGrid’,
array(
‘query’ => array(‘Title’ => ‘*’)
));
?>
parsed:
query=”{ Title: ‘*’ }”
Another nice feature would be the translation lookup for the labels.
Instead of defining it inside the template
array(‘field’ => ‘Year’, ‘label’ => ‘Year’, ‘options’ => array(‘width’ => ’10%’))
you could write
array(‘field’ => ‘Year’, ‘label’ => ‘en_year_label_from_translation’, ‘options’ => array(‘width’ => ’10%’))
Thanks for you code SpiffyJr. It is much appreciated.
Thank you for the code. I am using this and have a question. When I move the mouse on the records, the color of the row doesn’t show up like how the grid would show by default. I just see a colored line. Any clue.
Thank you!
Hi,
Really nice helpers.
Integrated these into my project.
Some modifications i made for myself.
1. If you have 2 grids in one controller the second one gets all the fields added from the 1.
For removing this i empty the field list before adding new ones (Since no new DataGrid object is made)
if (array_key_exists(‘fields’, $attribs)) {
$this->_fields = array(); // Added
foreach ($attribs['fields'] as $f) {
$this->addField(
}
unset($attribs['fields']);
}
Maybe i just create the helpers the wrong way but worked for me
2. I added Cell edditing js to a method aswell to support cell edditing
Ty and all the best,
Ludri
Hi there! I just would like to give a huge thumbs up for the excellent info you have here on this article. I will be coming back to your website for more soon.