developing and hosting web sites and applications for the Third Sector and SMEs since 2003
Posted Dec 9th 2009, 21:14 by PaulGardner
Are you a developer using CakePHP in the North East of England? If so, get in touch if you would be interested in being part of a group of developers who meet up once or twice a month to bounce ideas off one another.
I have been meaning to write this one up since the start of 2009, but have never gotten around to it. Now is as good a time as any so here goes and I hope it is of use to someone.
At the start of the year I was developing my first CakePHP application which used modal windows to update multiple telphone numbers, postal addresses and online addresses etc. My weapon of choice for creating modals was ModalBox and the following article helped me greatly in trying to integrate the two.
However, I wasn't too keen on the closeModalbox() javascript method used and I wanted to make my modalbox a bit more flexible. This is what I came up with.
In the above demo I have switched to a simple list of people, but the way it works is the same as I had in my application.
The unordered list is updated via an ajax call on page load. To edit the contents of the list click on 'Manage: Open Modal' and the modal will open. You can now add, edit and delete people till your hearts content and when you are happy with the list of people, close the modal box which then updates the content of the unordered list.
Still interested? Well you might want to have a go at recreating the above so here is a step by step guide. Apologies in advance as this is my first attempt at an article/tutorial of this nature so there may be some mistakes :o)
1. Download ModalBox, unzip and copy ...
modalbox.js to /app/webroot/js
modalbox.css to /app/webroot/css
2. If you aren't already using prototype and scriptaculous copy ..
lib/prototype.js to /app/webroot/js
lib/scriptaculous.js to /app/webroot/js
lib/effects.js to /app/webroot/js
3. Add the following within the <head> section of /app/views/layouts/default.ctp
echo $javascript->link('prototype');
echo $javascript->link('scriptaculous.js?load=effects');
echo $javascript->link('modalbox');
echo $html->css('modalbox');
4. Add RequestHandler component to /app/app_controller.ctp
This automagically tells cake to use the ajax layout if the request is indeed an ajax request.
5. Add Ajax helper to /app/app_controller.ctp
Used to populate the initial unordered list, could equally call your own Ajax.Updater() function.
6. Create your main view
<h1>List of people</h1>
<!-- Link to open ModalBox -->
<p><?php echo $html->link(
'Manage: Open Modal',
array('controller'=>'people', 'action'=>'index'),
array('title'=>'Manage People', 'onclick'=>'Modalbox.show(this.href, {
title: this.title, width: 700, overlayClose: false,
afterHide: function(element, value) {new Ajax.Updater(\'PeopleContainer\',\'/people/display\')}}); return false;')); ?></p>
<!-- Container for list of people -->
<div id='PeopleContainer' class='clearfix'>
<script type="text/javascript">
<?php echo $ajax->remoteFunction(array(
'url'=>array('controller'=>'people', 'action'=>'display'),
'update'=>'PeopleContainer',
'indicator'=>'PeopleIndicator'
)); ?>
</script>
</div>
<!-- Indicator whilst list of people is updating -->
<div id="PeopleIndicator" style="display:none; text-align:center;">
<p><?php echo $html->image("spinner.gif", array("alt"=>"Content loading")); ?></p>
<p>loading</p>
</div>
For the link I use the standard $html->link method but add an 'onclick' attribute to return the a link false and trigger my javascript call. The javascript calls modalbox.show() specifying the title, width, overlayClose attributes but special attention needs to be paid to the afterHide attribute, as it's this that calls Ajax.updater() once we have finished managing our list of people.
The rest of the view creates a container for the list of people and uses the Ajax helper to call in the unordered list and the indicator is created and hidden by using style="display:none;" so prototype can automatically shot it whilst making ajax calls.
7. Create the controller
<?php
class PeopleController extends AppController {
function beforeFilter() {
parent::beforeFilter();
}
function demo() {
$this->layout = 'demo';
}
function index() {
$this->paginate = array(
'limit' => '10',
'order' => array(
'Person.last_name' => 'ASC',
'Person.first_name' => 'ASC'
)
);
$people = $this->paginate('Person');
$this->set('people', $people);
}
function display() {
$people = $this->Person->find('all', array(
'order' => array(
'Person.last_name' => 'ASC',
'Person.first_name' => 'ASC'
),
'limit' => 10
));
$this->set('people', $people);
}
function add() {
if (!empty($this->data)) {
if ($this->Person->save($this->data)) {
$this->redirect(array('controller'=>'people', 'action'=>'index'));
} else {
$this->set('form_error', 'Person not added, correct errors and resubmit');
}
}
}
function edit($id = null) {
if (!empty($this->data)) {
if ($this->Person->save($this->data)) {
$this->redirect(array('controller'=>'people', 'action'=>'index'));
} else {
$this->set('ajax_message', 'Person not added, correct errors and resubmit');
}
} else {
$this->data = $this->Person->findById($id);
}
}
function delete($id) {
if ($this->Person->del($id)) {
$this->set('ajax_message', 'Person deleted');
} else {
$this->set('ajax_message', 'Person not deleted');
}
$this->redirect(array('controller'=>'people', 'action'=>'index'));
}
}
?>
There is nothing special about this controller, it is pretty much as it would be if it had been baked. There are no special conditions for ajax requests as the RequestHandler component deals with that.
8. Create Index View
<?php if(empty($people)): ?>
<p>None found.</p>
<?php else: ?>
<?php $paginator->options(array('update' => 'MB_content', 'indicator' => 'MB_loading')); ?>
<?php echo $html->para(null, $paginator->counter(array('format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true)))); ?>
<table class='index'>
<thead>
<?php echo $html->tableHeaders(array(
$paginator->sort('id'),
$paginator->sort('Last Name', 'Person.last_name'),
$paginator->sort('First Name', 'Person.first_name'),
' '
), null, null); ?>
</thead>
<tbody>
<?php
foreach($people as $person):
echo $html->tableCells(array(
$person['Person']['id'],
$person['Person']['last_name'],
$person['Person']['first_name'],
$html->link(
'Edit',
array('action' => 'edit', $person['Person']['id']),
array('title'=>'Edit', 'onclick'=>'Modalbox.show(this.href, {method: \'get\'}); return false;')).' '.
$html->link(
'Delete',
array('action' => 'delete', $person['Person']['id']),
array('title'=>'Delete', 'onclick'=>'Modalbox.show(this.href, {method: \'get\'}); return false;'))
), null, null);
endforeach; ?>
</tbody>
</table>
<div class="paging">
<?php echo $paginator->prev('<< '.__('previous', true), array(), null, array('class'=>'disabled'));?>
| <?php echo $paginator->numbers();?>
<?php echo $paginator->next(__('next', true).' >>', array(), null, array('class'=>'disabled'));?>
</div>
<?php endif; ?>
<p><?php echo $html->link(__('Add Person', true), array('action'=>'add'), array('title'=>'New Person', 'onclick'=>'Modalbox.show(this.href, {method: \'get\'}); return false;')); ?></p>
Things of not with this view are that I have added options to paginate so it updates the modalbox box rather than reloading the page, which means you can paginate through records and reorder columns within the modal, and that the Edit, Delete and Add links are all created using $html->link with the 'onclick' attribute .. so nothing too technical.
9. Create The Forms
Add:
<div class="form">
<?php
echo $form->create('Person');
echo $form->input('Person.first_name');
echo $form->input('Person.last_name');
echo "<p>".$form->submit('Submit', array('div'=>false, 'onclick'=>"Modalbox.show('/people/add', {params: Form.serialize('PersonAddForm'), method: 'post'}); return false;"));
echo " or ".$html->link(__('Cancel', true), array('action'=>'index'), array('title'=>'List People', 'onclick'=>'Modalbox.show(this.href, {method: \'get\'}); return false;'));
echo $form->end();
?>
</div>
Edit:
<div class="form">
<?php
echo $form->create('Person');
echo $form->input('Person.id');
echo $form->input('Person.first_name');
echo $form->input('Person.last_name');
echo "<p>".$form->submit('Submit', array('div'=>false, 'onclick'=>"Modalbox.show('/people/edit', {params: Form.serialize('PersonEditForm'), method: 'post'}); return false;"));
echo " or ".$html->link(__('Cancel', true), array('action'=>'index'), array('title'=>'List People', 'onclick'=>'Modalbox.show(this.href, {method: \'get\'}); return false;'));
echo $form->end();
?>
</div>
No need to use ajax forms, as we are once again using 'onclick' to call Modal.show() which serialises the form fields and posts them to the add/edit action.
And that is that, you can now add, edit and delete people till you're happy before closing the modal which will use the afterHide attribute of ModalBox to reload the pages unordered list.
I hope someone finds this of use and there are not too many mistakes!
Paul.
9 Comments
Jan 11th, 16:24 by aniltc
great article....
Feb 26th, 13:32 by Rahul
I have been using this tool and Its a nice tool but I have some questions.
1. I have a page that is bit lengthy, so is there any way i can scroll the page down.
2. I am unable to focus to next textbox when i press the tab button.
3. I have introduced javascript validations that are not working with this modal when the page opens in modalbox window.
any help would be highly appreciable.
Feb 26th, 14:15 by PaulGardner
@Rahul: The remit of my article is quite simplistic and I have not tried to address the issues you are having. To be honest I am not using this technique in any of my live sites.
However a quick Google search brought me to http://code.google.com/p/modalbox/issues/list where you can then search for your issues.
Regards,
Paul G.
Apr 4th, 17:58 by abdulhakim Haliru
Could this not have been done with Jquery ? pls lets have the Jquery version...nice work.
Apr 18th, 15:42 by Gagan
hi there---
Im trying to use modal box page to display some information pertaining to three different catagories into 3 TABS, but after juggling with so many tabs I'm not able to integrate it.
when I use mootools tabs, they appear properly on the html page otherwise, but the tabs don't work when that html is loaded in the modal box.
Im sure some lib calls are mismatching, but I'm very new for all this so if someone can pls help me with the code, i'll be really really thankful
Apr 20th, 07:50 by praveen kalal
sir
i implement this code in my task but it firstly render the index code then after it work with modalbox. so please tell me where i have to place the main view code.
thanks in advance
Apr 30th, 11:44 by PaulGardner
@abdulhakim: I have recently changed from using Prototype to jQuery, but I'm afraid ModalBox is built on Prototype so you would have to look for a jQuery dependant library to do this. Since writing this tutorial I have not used this technique so not had the need to code a jQuery equivalent
@Gagan: I have never used Mootools so have no idea how easily it will integrate with ModalBox
@praveen: My action/view naming follows cake's conventions, so I place the .ctp files for demo, index, add and edit actions in the /app/views/people folder
Jun 25th, 22:27 by m16u31
hi!! sorry but my english is not good,,,, but please can you give us the project's complete source code ,cuz im lost and i canīt do anything,i nedd to do a project
thxnks
Jun 25th, 22:47 by PaulGardner
@m16u31: What is it you're having problems? I have already provided all of the custom code required to import the modalbox javascript library and create all index and form views so not sure what else you're after that is related to this tutorial.
Leave a comment
Why not leave a comment for the author and others to read?