« back

Using single ModalBox in CakePHP to access a controllers CRUD actions

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'),
        '&nbsp;'
      ), 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.

Tags: tutorial cakephp modalbox

19 Comments

  1. Feb 26th 2010, 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.

  2. Feb 26th 2010, 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.

  3. Apr 4th 2010, 17:58 by abdulhakim Haliru

    Could this not have been done with Jquery ? pls lets have the Jquery version...nice work.

  4. Apr 18th 2010, 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

  5. Apr 20th 2010, 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

  6. Apr 30th 2010, 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

  7. Jun 25th 2010, 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

  8. Jun 25th 2010, 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.

  9. Jan 19th 2011, 17:42 by Alex

    Nice code, however I am struggling to do something that should be simple.
    When I submit the form and everything validates, I want to close the ModalBox and refresh the calling page. If I use the afterHide parameter, it will refresh the page no matter whether the form was submitted, the cancel link was pressed or the box was closed.

    Can you help?

  10. Jan 19th 2011, 18:43 by PaulGardner

    @Alex: If you echo the following at any time it will close the modal and refresh the parent page.

    <script type="text/javascript">
    Modalbox.hide({
    afterHide: function(element, value) {
    window.location.reload(true);
    }
    });
    </script>

    It's probably not the most elegant solution, but as I said way back in comment #3, I did not actually go on to use this technique and as such I have not worked with ModalBox apart from when I put this demo together which was a spin off from some work on a project where I did away with modals altogether.

    Not long after writing this post I switched javascript frameworks entirely, moving from Prototype/Scriptaculous to jQuery and have recently started using jQuery UI, which I find so much easier to use.

    If you've not invested too much time in Prototype and ModalBox it may be worth checking out jQuery UI's Modal behaviour:
    http://jqueryui.com/demos/dialog/#modal-form

  11. Jan 19th 2011, 19:09 by Alex

    Thanks, I am using JQuery quite a lot actually. However I never managed to load a controller within the dialog, all I could do was to define the form with javascript which is not ideal.
    Perhaps I need to look into it a bit more :)

  12. Jan 19th 2011, 19:20 by PaulGardner

    If you strip back what is happening in the above demo, it comprises of a few Ajax calls to update the modal's content and as jQuery loves Ajax it should not be to hard to do something similar.

    Beats having to load a whole separate framework just so you can use ModalBox. If you do manage it write a blog post and send me over a link :o) Or you could submit a guest blog post here ;)

    Paul.

  13. Jan 19th 2011, 21:53 by Alex

    jQuery definitely does not like me... I got my Dialog to open and loading the proper view that contains a form with an ajax submit. But this damn button does not do anything!

    And curious thing is that the ModalBox will strip out all the SCRIPT tags before the rendering occurs, so your solution is not good :)

  14. Feb 18th 2011, 15:40 by Mark V

    Hi, i need help on this. please help me.

    INSIDE the modal box i have a search box which i will input name of employees, and the div which is inside the modal box should be filled... so i want that div to be autofilled as i type characters in the search box.....so i have an "onkeypress" event in the inputfield(searchbox)..... i put the code onkeypress="alert('BANG!')",,,, when i am in the modal box, the onkeypress event will not work, but when i do not use the modal box (i go to the url -> '/employeesr/searchemp/') the onkeypress event will work and popsup the alert box.... pls help me i am a total newbie

    Thank you everyone

  15. Aug 3rd 2011, 13:54 by Alex

    Good job man. I'm a little bit confused about the models, what tables do you have in your database. I see person and and $people variables. Kinda confusing.

  16. Aug 3rd 2011, 16:17 by PaulGardner

    Alex,

    No need to worry about this as CakePHP does it all for you. It already has a default list of rules for inflecting between singular and plural, and the inflection for people -> Person is already in there.

    - table: people
    - model: person.php (class: Person)
    - controller: people_controller.php (class: PeopleController)

    HTH, Paul.

  17. Nov 8th 2011, 09:49 by asdzxc

    This is a great idea, but just a little info for anyone who would have view pages with links that leads to a different controller form (eg: users/add, posts/add) dont use this, because you cannot control the forms redirection of serialized data since the referred url would always be the main page. In the example that Paul made he uses a PeopleController to create and add users in the index page, lets say you have an add Item on that index page, where if clicked would open a form to create a new item for the user. Of course in the submit button, we will put the onlick event and specify the URL in the Modalbox which should send the datas in the controllers action in our case in the ItemControllers Add action or function. Modalbox('items/add', {params: Form.serialize('ItemAddForm'), method: 'post'}]) is the correct syntax we would put for that, but here comes the problem, the 'items/add' in the Modalbox method will just always append the url from the reffering url, so in our case the final url output where the datas of the ItemAdd form will be sent is CakeApp.com/people/items/add:params

    wherein you should be able to distinguish what im saying, so just stick to the nerdnotes.org idea where in the ajax /form helper would let you redirect your datas properly.

    hope this helps -C.Edward

  18. Nov 9th 2011, 07:53 by asdzxc

    A follow up to my last comment(#17)

    i forgot to mention about the settings in your cakephp route, if you changed something in there like renaming the controller/:params (eg 'people/add into peoplespage/add)
    all that i stated in my last comment(#17) are true.

    thanks! -C.Edward

  19. Nov 11th 2011, 08:19 by PaulGardner

    Thanks for your comments asdzxc,

    I plan to do an update to this post at some point as I developed this way back with Cake 1.1.

    When I do, I will test this in Cake 1.3 and 2.0.X as well as developing a jQuery (UI) version of the tutorial.

    I will of course try and address the issues you raise too.

    Thanks,

    Paul.

Leave a comment

Why not leave a comment for the author and others to read?

Get In Touch

Keep In Touch

Twitter Updates

    follow us on Twitter