CRUD using jQuery and Codeigniter – II

Learning Objectives

In Part 2, you will learn:

  1. Displaying Ajax loader animation when an Ajax call is made.
  2. How to inject Update/Delete link in each record using jQuery.
  3. How to use jQueryUI dialog widget.
  4. How to send Ajax request to perform DELETE operation.
  5. How and when to use jQuery’s delegate method to bind events.

Previous Part of this Tutorial

Get Source Files

Ajax Loader Animation

In previous part I forgot to add Ajax loading animation. UI experts always recommend to show some kind of animation or progressbar whenever an ajax request is sent otherwise user won’t be able to know that some action is taking place behind the scene.

You can select different style of animations from www.ajaxload.info. I selected Indicator type “Indicator lite” and dark gray (#3A3A3A) background color and white foreground color as shown below:

Ajax loader animation settings

Create a new folder “images” in “crud” folder and save the generated gif animation in it. Now the path to image is crud/images/ajax-loader.gif

Now add following div just below opening tag of body in home view.

<div id="ajaxLoadAni">
    <img src="images/ajax-loader.gif" alt="Ajax Loading Animation" />
    <span>Loading...</span>
</div>

Then add CSS in css/styles.css to style loading animation and the text “Loading…” in span tags.

#ajaxLoadAni {
    background: #3A3A3A;
    color: #fff;
 
    /* we hide it because we only need to display it when ajax call is made */
    display: none;
 
    font-weight: bold;
    position: fixed;
    top: 0;
    left: 40%;
    padding: 8px;
    width: 106px;
    z-index: 9999;
}
 
#ajaxLoadAni span {
    float: right;
    margin: 2px 0 0 0;
}

Finally we need to modify crud/js/all.js file to display this animation whenever an Ajax request is sent and hide it when a reponse from server is received. To do so, add following line of code right before $.ajax function is called to perform READ operation.

//display ajax loader animation
$( '#ajaxLoadAni' ).fadeIn( 'slow' );

Above line of code simply displays the loader animation with fade effect. Now modify the success function of READ operation so that it hides the loader animation when a response from server is received.

1
2
3
4
5
6
success: function( response ) {
    $( '#readTemplate' ).render( response ).appendTo( "#records" );
 
    //hide ajax loader animation here...
    $( '#ajaxLoadAni' ).fadeOut( 'slow' );
}

Try reloading the home controller and you will see loader animation for few milli seconds.

Performing the DELETE operation

Modifying Model

To perform DELETE operation we first need to add a method in mUsers model which takes the id of record to be deleted as a parameter. Then execute the delete query using Codeigniter’s delete() active record method.

1
2
3
4
5
6
7
8
9
public function delete( $id ) {
    /*
    * Any non-digit character will be excluded after passing $id
    * from intval function. This is done for security reason.
    */
    $id = intval( $id );
 
    $this->db->delete( 'users', array( 'id' => $id ) );
} //end delete

Note the use of intval function on line 6. It’s PHP function and it converts a string into integer and any non-digit character will be ignored.

On line 8, we perform the delete operation using delete() active record method. First parameter is table name ‘users’ on which we want perform the delete operation. Second parameter is an array of name-value pair which tell the delete method to delete a record whose column “id” is equal to $id. Above delete operation generates following SQL:

DELETE FROM users WHERE id = $id

Of course $id will be replaced with passed value.

Modifying Controller

Now let’s add a new method in controller called delete.

1
2
3
4
5
6
7
8
9
public function delete( $id = null ) {
    if( is_null( $id ) ) {
            echo 'ERROR: Id not provided.';
            return;
    }
 
    $this->mUsers->delete( $id );
    echo 'Records deleted successfully';
}

This method is very simple. On line 2, we are checking is the id null. If yes then echo an error which will be sent as a response to the browser. Otherwise call delete method of mUsers model and echo success message.

Modifying View

Add following lines of code in home view after tabs div.

1
2
3
4
5
6
7
<!-- delete confirmation dialog box -->
<div id="delConfDialog" title="Confirm">
    <p>Are you sure?</p>
</div>
 
<!-- message dialog box -->
<div id="msgDialog"><p></p></div>

Above two divs are added for jQueryUI dialog boxes. On line 2 is the first div whose title is “Confirm”. The title of div will become the title of dialog box. Similarly the content of div will become the content of dialog box.

Second div is on line 7 which don’t have any title attribute and its content is only a blank paragraph tag. Actually I will add both title and content using jQuery.

Adding Update and Delete Links

To perform the delete operation on a specific record, we first need to add delete link in every table row as shown below:

Update Delete links

We need to modify jQuery template to add these links.

1
2
3
4
5
6
7
8
<script type="text/template" id="readTemplate">
    <tr>
        <td>${id}</td>
        <td>${name}</td>
        <td>${email}</td>
        <td><a class="updateBtn" href="${updateLink}">Update</a> | <a class="deleteBtn" href="${deleteLink}">Delete</a></td>
    </tr>
</script>

I added both Update and Delete links in the fourth td tag on line 6. Note the value of href of both links. These are template variables and its values will be added in success method of READ Ajax call.

First create three more variables in crud/javascript/all.js after the readUrl variable declaration.

var readUrl   = 'index.php/home/read',
    updateUrl = 'index.php/home/update',
    delUrl    = 'index.php/home/delete',
    delHref;

Nothing complex here. Just declaring variables containing URLs to update and delete Codeginiter Home controller methods. Of course I haven’t created update method yet. Update operation will be covered in Part 3.

Let’s modify the success method of READ Ajax call.

1
2
3
4
5
6
7
8
9
10
11
12
success: function( response ) {
 
    for( var i in response ) {
        response[ i ].updateLink = updateUrl + '/' + response[ i ].id;
        response[ i ].deleteLink = delUrl + '/' + response[ i ].id;
    }
 
    $( '#readTemplate' ).render( response ).appendTo( "#records" );
 
    //hide ajax loader animation here...
    $( '#ajaxLoadAni' ).fadeOut( 'slow' );
}

The only new thing in above code is the “for in” loop (which is similar to foreach loop in PHP). This loop iterates over each record returned from server and adding new data members updateLink and deleteLink.

Remember that the response returned from server is in json format and in this format there is an array of objects. Each record is actually an object containing data members for each column in database table.

On line 5 of above code, I am adding new data member “deleteLink” and assigning it delUrl (index.php/home/delete) and concatenating ‘/’ and id of current record. This id will be passed as a parameter to the “Home” controller method “delete”.

jQueryUI Dialog

Now I will convert the msgDialog and delConfDialog divs to jQueryUI dialog by calling dialog() method. For that write following code in all.js file after the $.ajax() call to READ.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$( '#msgDialog' ).dialog({
    autoOpen: false,
 
    buttons: {
        'Ok': function() {
            $( this ).dialog( 'close' );
        }
    }
});
 
 
$( '#delConfDialog' ).dialog({
    autoOpen: false,
 
    buttons: {
        'No': function() {
            $( this ).dialog( 'close' );
        },
 
        'Yes': function() {
            //display ajax loader animation here...
            $( '#ajaxLoadAni' ).fadeIn( 'slow' );
 
            $( this ).dialog( 'close' );
 
            //Ajax call to perform DELETE operation will go here
 
        } //end Yes
 
    } //end buttons
 
}); //end dialog

Line 1 – The msgDialog div is selected and dialog() method is called on it. An object is passed as a parameter to dialog(). Remember {} denotes object in javascript.

Line 2 – First data member of passed object is autoOpen which is set to false. It means that msgDialog must not open by default. We can open it by passing ‘open’ to dialog like this: $( ‘#msgDialog’ ).dialog( ‘open’ ).

Line 4 – Second data member is buttons whose value is another object. ‘buttons’ used to define what buttons will be displayed in dialog box and what function will they perform when clicked.

Line 5 – I defined only one button ‘Ok’ and it will close the dialog when clicked. It’s because on line 6, the function assigned to it contains statement to close the dialog.

Line 12 – I am calling dialog() on delConfDialog div and passing an object as a parameter to dialog() just like I did on line 1.

Line 15 – The buttons data member is defined and this time two buttons are defined ‘Yes’ and ‘No’.

Line 16 – ‘No’ button is assigned a function which contains a statement to close dialog.

Line 20 – ‘Yes’ button is assigned a function. This function is where we perform the Ajax call to delete the record.

Line 22 – Display ajax loader animation with fade effect.

Line 24 – Close dialog.

Line 26 – A comment is placed here. This is where I will call the $.ajax function to request delete operation.

The Ajax Call

Now I will perform the ajax call for delete operation. Replace the comment on line 26 of above code with following code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$.ajax({
    url: delHref,
 
    success: function( response ) {
        //hide ajax loader animation here...
        $( '#ajaxLoadAni' ).fadeOut( 'slow' );
 
        $( '#msgDialog > p' ).html( response );
        $( '#msgDialog' ).dialog( 'option', 'title', 'Success' ).dialog( 'open' );
 
        $( 'a[href=' + delHref + ']' ).parents( 'tr' )
        .fadeOut( 'slow', function() {
            $( this ).remove();
        });
 
    } //end success
});

Line 2 – The url where this ajax request will go is delHref. Remember it’s a var defined below delUrl = ‘index.php/home/delete’. I haven’t assigned any value to it. Don’t worry about it. I will assign a value to it in click event on Delete anchor (<a>) tag.

Line 4 – ‘success’ method is defined. Again reminding you that this method runs when a response from server is recevied.

Line 8 – Remember I wrote that the msgDialog’s title and content will be placed using jQuery. In this line we are simply replacing the content of p tag (in msgDialog div) to ‘response’ from server. The response from server will be plain text saying “Records deleted successfully” which I wrote in delete method of Home controller.

Line 9 – msgDialog is selected and dialog() method is called to change the title of dialog to Success. Then again dialog() method is called and ‘open’ is passed as parameter to open the dialog. Calling methods one after the other is called chaining.

Line 11 – Now we need to remove the row from table which is deleted. To find that row in a table we selected an anchor tag having an href value of delHref and then tr is searched in its parent tags. Then on line 12 that tr is hidden using fade effect. In fadeOut() method I passed two parameters. First is ‘slow’ which means animation will take place in slow speed. Second is the callback function which will run when the animation is completed. In this function (on line 13), we are calling remove() method on $( this ). Here this actually points to the tr tag and remove method actually removes an element from the DOM tree.

The Click Event

I wrote code that will perform the delete operation, and hide the row ‘tr’ which is deleted. But I haven’t defined when this operation will be performed? Of course delete operation must be performed when anchor tag having class ‘deleteBtn’ is clicked. So we need to bind a click handler on it. Write following code after dialog methods.

1
2
3
4
5
6
7
8
$( "#records" ).delegate( "a.deleteBtn", "click", function() {
    delHref = $( this ).attr( 'href' );
 
    $( '#delConfDialog' ).dialog( 'open' );
 
    return false;
 
}); //end delegate

You may be wondering why didn’t I used bind() method or simply click() method to bind click event. It’s because these methods don’t provide live binding. The problem with bind() method is that it don’t bind events to those elements which are added later dynamically after the DOM is loaded and script ran completely. bind() method bind events only once.

It may sound confusing so let’s try to understand it using an example. The complete code in all.js executes quickly than the time taken by READ ajax call to bring results from server. If we have used bind() method and the script in all.js runs completely, it means all the binding occurs before the records are fetched from server and displayed in the template we defined. So the bind() method wouldn’t have found the <a class=”deleteBtn”> links to whom it wants to bind the click event. Also bind() don’t supports live binding so when the records are displaying in template, the Delete link will not work. For that purpose I used jQuery 1.4’s delegate() method. Before version 1.4, live() method was used for this purpose.

Line 1 – Element having records id is selected (it’s the id of table) and delegate method is called on it. In delegate method, the first parameter tells that find anchor tag having class deleteBtn in #records table. Second parameter is “click”. It means the function passed as third parameter will run when click event occurs.

Line 2 – delHref is a variable which is used as the url value of ajax call to perform delete operation. So here we assign the href value of a.deleteBtn to it.

Line 4 – Open dialog having id ‘delConfDialog’ whose code is already defined above.

Line 6 – Finally false is returned to prevent the default behaviour of anchor tag (which is to load the page whose url is in href).

Now code in all.js must match code shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
$( function() {
    var readUrl   = 'index.php/home/read',
        updateUrl = 'index.php/home/update',
        delUrl    = 'index.php/home/delete',
        delHref;
 
    $( '#tabs' ).tabs({
        fx: { height: 'toggle', opacity: 'toggle' }
    });
 
    //display ajax loader animation
    $( '#ajaxLoadAni' ).fadeIn( 'slow' );
 
    $.ajax({
        url: readUrl,
        dataType: 'json',
        success: function( response ) {
 
            for( var i in response ) {
                response[ i ].updateLink = updateUrl + '/' + response[ i ].id;
                response[ i ].deleteLink = delUrl + '/' + response[ i ].id;
            }
 
            $( '#readTemplate' ).render( response ).appendTo( "#records" );
 
            //hide ajax loader animation here...
            $( '#ajaxLoadAni' ).fadeOut( 'slow' );
        }
    });
 
 
    $( '#msgDialog' ).dialog({
        autoOpen: false,
 
        buttons: {
            'Ok': function() {
                $( this ).dialog( 'close' );
            }
        }
    });
 
 
    $( '#delConfDialog' ).dialog({
        autoOpen: false,
 
        buttons: {
            'No': function() {
                $( this ).dialog( 'close' );
            },
 
            'Yes': function() {
                //display ajax loader animation here...
                $( '#ajaxLoadAni' ).fadeIn( 'slow' );
 
                $( this ).dialog( 'close' );
 
                $.ajax({
                    url: delHref,
 
                    success: function( response ) {
                        //hide ajax loader animation here...
                        $( '#ajaxLoadAni' ).fadeOut( 'slow' );
 
                        $( '#msgDialog > p' ).html( response );
                        $( '#msgDialog' ).dialog( 'option', 'title', 'Success' ).dialog( 'open' );
 
                        $( 'a[href=' + delHref + ']' ).parents( 'tr' )
                        .fadeOut( 'slow', function() {
                            $( this ).remove();
                        });
 
                    } //end success
                });
 
            } //end Yes
 
        } //end buttons
 
    }); //end dialog
 
 
    $( "#records" ).delegate( "a.deleteBtn", "click", function() {
        delHref = $( this ).attr( 'href' );
 
        $( '#delConfDialog' ).dialog( 'open' );
 
        return false;
 
    }); //end delegate
 
}); //end document ready

Review

In this part we learned how to display ajax loader animation. Then we learned how to display Update/Delete links and then we performed the delete operation using ajax call. We also learned about jQuery delegate method. In Part 3, I will show you how to perform Update operation.


comments powered by Disqus