hocuspokus

Adding/Deleting Rows In TableKit Tables

October 11, 2007

This post has now been updated. Please head over to the new post for something better...

Following on from my previous post on how to integrate the excellent TableKit into your Catalyst webapp (to make your data tables dynamically editable), here’s how i’ve gone about adding ajax inserts and deletes so that you can add and remove data rows in your tables.

You’ll need the code from the previous post to follow along.

We’re going to be using prototype to do the background ajax calls, as this is already in our app as a requirement of TableKit. Now let’s get on with doctoring our template. First we add in a delete button for each row of our table. Open up /root/src/users/list.tt and change the code of our table to look like the following (the changes come on lines 9, 17, 20 and 26-28):

<table id=“users” class=“sortable resizable editable”>
  <thead>
    <tr>
      <th id=“id” class=“sortfirstasc noedit”>Id</th>
      <th id=“firstname”>First Name</th>
      <th id=“lastname”>Last Name</th>
      <th class=“noedit nocol”></th>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <th>Id</th>
      <th>First Name</th>
      <th>Last Name</th>
      <th class=“nocol”></th>
    </tr>
  </tfoot>
  <tbody id=“user_body”>
  [% FOREACH user IN users -%]
    <tr id=‘[% user.id %]’>
      <td>[% user.id %]</td>
      <td>[% user.firstname %]</td>
      <td>[% user.lastname %]</td>
      <td class=“nocol”>
        <a class=“delete” href=“#” onclick=“userDelete([% user.id %]); return false”>delete</a>
      </td>
    </tr>
  [% END -%]
  </tbody>
</table>

The above code adds a ‘delete’ link into each row of the table. This link is to run a short javascript function (called userDelete) that we shall add now at the bottom of the template (within the original javascript block):

userDelete = function( user_id ) {
  var answer = confirm(‘Are you sure you want to delete this user?’);
  if (answer) {
    new Ajax.Updater( ‘user_body’,
      ‘[% c.uri_for(’/users/_delete_user/‘) %]?user_id=’ + user_id,
      { asynchronous: 1, }
    );
    return false;
  }
}

This function basically asks the user to confirm that they want to delete the user entry, the sets up an Ajax.Updater that passes the user_id to be deleted to a new function in our controller (that we shall write next) which deletes it, and then passes the new table body back to be updated on screen. Let’s now create a new function _delete_user (and a helper function __return_all_users) in our user controller /lib/MyApp/Controller/Users.pm:

=head _delete_user

Ajax method to delete users from the users table

=cut

sub delete_user : Local { my ($self, $c) = @;

# Look-up our user entry my $user = $c->model(‘MyAppDB::Users’)->find({ id => $c->req->params->{user_id} })->delete();

my $output = __return_all_users( $self, $c, ‘null’ );

unless ( $output ) { $output = ‘<tr><td colspan=“4” class=“nocol”>All Users Deleted</td></tr>’; }

$c->res->body( $output ); }

=head2 __return_all_users

Private method for the Users ajax interaction. Returns the inner body of a html table.

=cut

sub _return_all_users : Private { my ( $self, $c, $new_user_id ) = @;

my @users = $c->model(‘MyAppDB::Users’)->all( { order_by => ‘id ASC’ } );

my $html; for ( my $i = 0 ; $i < scalar(@users) ; $i++ ) { my $class;

<span class="k">if</span> <span class="p">(</span> <span class="nv">$new_user_id</span> <span class="o">&amp;&amp;</span> <span class="nv">$users</span><span class="p">[</span><span class="nv">$i</span><span class="p">]</span><span class="o">-&gt;</span><span class="nv">id</span> <span class="o">==</span> <span class="nv">$new_user_id</span> <span class="p">)</span> <span class="p">{</span>
  <span class="nv">$class</span> <span class="o">=</span> <span class="s">'new'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span> <span class="nv">$i</span> <span class="nv">%</span> <span class="nv">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">)</span> <span class="p">{</span> <span class="nv">$class</span> <span class="o">=</span> <span class="s">'rowodd'</span><span class="p">;</span> <span class="p">}</span>
  <span class="k">else</span> <span class="p">{</span> <span class="nv">$class</span> <span class="o">=</span> <span class="s">'roweven'</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>

<span class="nv">$html</span> <span class="o">.=</span> <span class="s">"&lt;tr class=\""</span> <span class="o">.</span> <span class="nv">$class</span> <span class="o">.</span> <span class="s">"\" id=\""</span> <span class="o">.</span> <span class="nv">$users</span><span class="p">[</span><span class="nv">$i</span><span class="p">]</span><span class="o">-&gt;</span><span class="nv">id</span> <span class="o">.</span> <span class="s">"\"&gt;
  &lt;td&gt;"</span> <span class="o">.</span> <span class="nv">$users</span><span class="p">[</span><span class="nv">$i</span><span class="p">]</span><span class="o">-&gt;</span><span class="nv">id</span> <span class="o">.</span> <span class="s">"&lt;/td&gt;
  &lt;td&gt;"</span> <span class="o">.</span> <span class="nv">$users</span><span class="p">[</span><span class="nv">$i</span><span class="p">]</span><span class="o">-&gt;</span><span class="nv">firstname</span> <span class="o">.</span> <span class="s">"&lt;/td&gt;
  &lt;td&gt;"</span> <span class="o">.</span> <span class="nv">$users</span><span class="p">[</span><span class="nv">$i</span><span class="p">]</span><span class="o">-&gt;</span><span class="nv">lastname</span> <span class="o">.</span> <span class="s">"&lt;/td&gt;
  &lt;td class=\"nocol\"&gt;
    &lt;a class=\"delete\" href=\"#\" onclick=\" userDelete("</span>
    <span class="o">.</span> <span class="nv">$users</span><span class="p">[</span><span class="nv">$i</span><span class="p">]</span><span class="o">-&gt;</span><span class="nv">id</span> <span class="o">.</span> <span class="s">"); return false\"&gt;delete&lt;/a&gt;
  &lt;/td&gt;
  &lt;/tr&gt;"</span><span class="p">;</span>

}

return ( $html ); }

The _delete_user function, takes the user_id that we sent to it, deletes the entry from the database, then calls the __return_all_users function to rebuild the table body. This table body is then used to update the table back on the web page.

That’s the delete functionality sorted, now on with the row adding… Back in the template, add we add another Ajax.Updater link below the table:

<br />
<a class=“add” href=“#” onclick=“
  new Ajax.Updater( ‘user_body’,
    ‘[% c.uri_for(’/users/_add_user') %]‘,
    { asynchronous: 1, }
  ); return false ”>add a user</a>

And add another new method into our controller to add a new user into the database and table:

=head _add_user

Ajax method to add users to the users table

=cut

sub add_user : Local { my ($self, $c) = @;

# create a new user… my $new_user = $c->model(‘MyAppDB::Users’)->create( { id => $c->req->params->{user_id}, firstname => ‘[First Name]’, lastname => ‘[Last Name]’ } );

my $output = __return_all_users( $self, $c, $new_user->id );

$c->res->body( $output ); }

This function, like the delete function, does its business on the database, then rebuilds the table body and sends it back to the web page.

The final icing on the cake, is a little addition to our CSS to make this all look a little nicer. Open up /root/css/myapp.css and add the following to the bottom of the file:

.nocol, th.nocol {
  background-color: #fff;
  border: none;
}

tr.new { background-color: #ff9697; }

a { outline: none; }

a.add, a.delete { background: url(‘/images/add.png’) left center no-repeat; padding: 2px 0 2px 20px; }

a.delete { background: url(‘/images/delete.png’) left center no-repeat; left: 0; }

tr a.delete { font-size: 0.8em; }

And there you go. Now you can edit, add and delete users to your database and table without having to reload the page.

add-remove-tablekit.png

However, there is one small bug that needs to be squashed to make this perfect… TableKit caches the tables on page load - if you have a go at re-sorting one of the tables after deleting or adding a row, this causes the table to essentially double in size as the original cached data is reloaded onto the screen! :( I now need to figure out a way to clean out the cache for the current table and re-initalise TableKit. Any suggestions would be more than welcome! :)

The files used in the examples can be downloaded below.

Attached Files:

MyApp - v2

Comments