Advertisement
PHP

Create a Contact Manager

by

As more and more applications are moving towards the hive, web developers and designers alike are required to learn more and more about how to design "desktopesque" projects. In this example, we will be building the front-end of a contact manager. We will use the extremely popular jQuery library for all of our JavaScript.


Stages

  • Section 1: Use jQuery to create a functional client side contact manager
  • Section 2:Use Codeigniter to make the project dynamic and interact with a database. (Create CRUD functionality Create-Read-Update-Delete)
  • Section 3:Speed up the application by using AJAX to dynamically update the contact manager application.

Objectives:

At the end of this tutorial, we want to accomplish the following:

  • Use jQuery to create a dynamic contact viewer
  • Be able to organize contacts into groups
  • Hide and show groups and contacts based on what is clicked

Basically, we want to create the client side of a contact manager. We will do server side in a later tutorial and use codeigniter. To get a better idea of the desired effect, you may just want to look at the demo.


Section 1


Getting Started

To start off here's the HTML and CSS to go along with the tutorial. Everything should be pretty self explanatory. Since this will be eventually churned out automatically by codeigniter when it's reading the database, it is not as human readable as you may have hoped, but it works. Also, if there is something you're confused about, it's a good chance that it will be answered in part two.

HTML:

 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Members</title> 
<link rel="stylesheet" type="text/css" href="assets/layout.css" media="screen" /> 
<link rel="stylesheet" type="text/css" href="assets/1.css" media="screen" /> 
<link rel="stylesheet" type="text/css" href="assets/ui-theme/ui.css" media="screen" /> 
<script type="text/javascript" src="assets/jq.js"></script> 
<script type="text/javascript" src="assets/ui.js"></script> 
<script type="text/javascript" src="assets/val.js"></script> 
<script type="text/javascript" src="assets/js.js"></script> 
</head> 
<body> 
<div class="roster module "> 
  <div class="module-title">Address Book<span class="loading"></span><span class="ui-state-default ui-corner-all add"><a class="ui-icon ui-icon-circle-plus frameopen" href="#" title="Add Member"></a></span></div> 
  <ul class="roster-groups"> 
    <li class="group"><span class="active">All Contacts</span></li> 
    <li class="group4"><span>Office</span></li> 
    <li class="group42"><span>Contacts</span></li> 
    <li class="group43"><span>Other</span></li> 
  </ul> 
  <!-- End roster-groups --> 
  <div class="member-column"> 
    <ul class="group-list group all-members"> 
      <li class="contact445"> 
        <input type="checkbox" class="selectbox"/> 
        <p>Connor Zwick<a class="edit frameopen" href="members/update_include/445" title="Edit this member"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/edit-trans.png" alt="edit" /></a> <a class="delete" href="members/delete/445" title="Click here to delete this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/delete-trans.png" alt="edit" /></a></p> 
      </li> 
      <li class="contact442"> 
        <input type="checkbox" class="selectbox"/> 
        <p>John Doe<a class="edit frameopen" href="members/update_include/442" title="Edit this member"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/edit-trans.png" alt="edit" /></a> <a class="delete" href="members/delete/442" title="Click here to delete this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/delete-trans.png" alt="edit" /></a></p> 
      </li> 
      <li class="contact444"> 
        <input type="checkbox" class="selectbox"/> 
        <p>Jane Doe<a class="edit frameopen" href="members/update_include/444" title="Edit this member"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/edit-trans.png" alt="edit" /></a> <a class="delete" href="members/delete/444" title="Click here to delete this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/delete-trans.png" alt="edit" /></a></p> 
      </li> 
    </ul> 
    <ul class="group-list group4"> 
      <li class="contact442"> 
        <input type="checkbox" /> 
        <p>John Doe<a class="edit frameopen" href="members/update_include/442" title="Click here to edit this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/edit-trans.png" alt="edit" /></a> <a class="delete" href="members/delete/442" title="Click here to delete this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/delete-trans.png" alt="edit" /></a></p> 
      </li> 
    </ul> 
    <ul class="group-list group42"> 
      <li class="contact445"> 
        <input type="checkbox" /> 
        <p>Connor Zwick<a class="edit frameopen" href="members/update_include/445" title="Click here to edit this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/edit-trans.png" alt="edit" /></a> <a class="delete" href="members/delete/445" title="Click here to delete this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/delete-trans.png" alt="edit" /></a></p> 
      </li> 
    </ul> 
    <ul class="group-list group43"> 
      <li class="contact444"> 
        <input type="checkbox" class="selectbox"/> 
        <p>Jane Doe<a class="edit frameopen" href="members/update_include/444" title="Edit this member"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/edit-trans.png" alt="edit" /></a> <a class="delete" href="members/delete/444" title="Click here to delete this task"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/assets/css/images/delete-trans.png" alt="edit" /></a></p> 
      </li> 
    </ul> 
  </div> 
  <!-- End member-column --> 
  <div class="contact-info"> 
    <ul> 
      <li class="showfirst"><br /> 
        <h1 class="sub-text">Click on a contact to view their information</h1> 
      </li> 
      <li class="contact445"> 
        <hr /><h2>Connor Zwick</h2> 
        <h3 class="sub-text">Designer</h3> 
        <br /> 
        <h3 class="sub-text"><a href="mailto:connor@email.com">connor@email.com</a></h3> 
        <br /> 
        <br /> 
        <p class="sub-text">364 Imaginery Ln.</p> 
        <br /> 
        <p class="sub-text">Someplace, TJ 74622</p> 
        <br /> 
        <br /> 
        <p class="sub-text"><b>Business: </b>234.555.7344</p> 
        <br /> 
        <p class="sub-text"><b>Cell Phone: </b>233.555.7754</p> 
        <br /> 
        <p class="sub-text"><b>Home Phone: </b>374.555.2635</p> 
        <br /> 
      </li> 
      <li class="contact442"> 
        <hr /><h2>John Doe</h2> 
        <h3 class="sub-text">Chief Name Specialist</h3> 
        <br /> 
        <h3 class="sub-text"><a href="mailto:jdoe@email.com">jdoe@email.com</a></h3> 
        <br /> 
        <br /> 
        <p class="sub-text">123 Special Lane</p> 
        <br /> 
        <p class="sub-text">City, State, ZIP</p> 
        <br /> 
        <br /> 
        <p class="sub-text"><b>Business: </b>325.356.2356</p> 
        <br /> 
        <p class="sub-text"><b>Cell Phone: </b>343.555.2363</p> 
        <br /> 
        <p class="sub-text"><b>Home Phone: </b>363.555.2953</p> 
        <br /> 
      </li> 
      <li class="contact444"> 
        <hr /><h2>Jane Doe</h2> 
        <h3 class="sub-text">CEO</h3> 
        <br /> 
        <h3 class="sub-text"><a href="mailto:janedoe@email.com">janedoe@email.com</a></h3> 
        <br /> 
        <br /> 
        <p class="sub-text">321 XYZ Dr.</p> 
        <br /> 
        <p class="sub-text">City, State, ZIP</p> 
        <br /> 
        <br /> 
        <p class="sub-text"><b>Business: </b>234.555.2463</p> 
        <br /> 
        <p class="sub-text"><b>Cell Phone: </b>234.555.2367</p> 
        <br /> 
        <p class="sub-text"><b>Home Phone: </b>398.555.2634</p> 
        <br /> 
      </li> 
    </ul> 
  </div> 
  <!-- End contact-info--> 
</div> 
<!-- End roster-module--> 
</body> 
</html>

Links:

As you can see we have several CSS and JS files that we're linking to. Here's what they're for.

  • layout.css is all of the CSS that has to do, surprisingly, with the layout of the project
  • 1.css contains any information with color styling. This is because when this is applied into a project, another feature could be the ability to switch themes of the application. Therefore, we want to split out structural CSS with our color CSS.
  • ui.css contains all of the theme-rolled styling from the jQuery UI site. We will use this in a later tutorial when we apply AJAX functionality to this project.
  • jq.js is our base jQuery file. We won't need to edit this for any reason.
  • ui.js is the jQuery UI file. Again, this is already compressed and will not need to be edited.
  • val.js is the form validator jQuery plugin. It is also compressed and we will not need to edit it.
  • js.js contains all of the custom js, that we will be writing.

CSS:

 
 
/* RESET */ 
/* ----------------------------------------- */ 
 
/* Global reset */ 
/* Based upon 'reset.css' in the Yahoo! User Interface Library: http://developer.yahoo.com/yui */ 
*, html, body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, label, fieldset, input, p, blockquote, th, td { margin:0; padding:0 } 
table { border-collapse:collapse; border-spacing:0 } 
fieldset, img { border:0 } 
address, caption, cite, code, dfn, em, strong, th, var { font-style:normal; font-weight:normal } 
ol, ul, li { list-style:none } 
caption, th { text-align:left } 
h1, h2, h3, h4, h5, h6 { font-size:100%; font-weight:normal } 
q:before, q:after { content:''} 
 
/* Global reset-RESET */ 
/* The below restores some sensible defaults */ 
strong { font-weight: bold } 
em { font-style: italic } 
a img { border:none } /* Gets rid of IE's blue borders */ 
html{ 
	height: 100%; 
} 
h1{ 
	font-size: 30px; 
} 
h2{ 
	font-size: 24px; 
	font-weight: bold; 
} 
h3{ 
	font-size: 14px; 
	font-weight: bold; 
} 
a{ 
	text-decoration: none; 
} 
a:hover{ 
	text-decoration: underline; 
} 
body{ 
	font-family: Arial, Helvetica, sans-serif; 
	text-align: center; 
	margin: 10px; 
} 
.module{<br />	position: relative;<br />	background: #f6f6f6;<br />	overflow: hidden;<br />	border: 1px solid #c1c1c1;<br />	margin-bottom: 10px;<br />	text-align: left;<br />	width: 900px;<br />	margin-left: auto;<br />	margin-right: auto;<br />} 
.roster{ 
	min-height: 300px; 
} 
.module .module-title{ 
	display: block; 
	width: 100%; 
	background: #f3f3f3; 
	border: 1px solid #c1c1c1; 
	padding: 5px 0px 5px 5px; 
	margin-left: -3px; 
	margin-top: -1px; 
	font-size: 24px; 
	font-weight: bold; 
	 
} 
.member-column{ 
	border-left: #c1c1c1 solid 1px; 
	border-right: #c1c1c1 solid 1px; 
	width: 180px; 
	height: 256px; 
	overflow-x: hidden; 
	overflow-y: auto; 
	position: absolute; 
	left: 150px; 
} 
.member-column li{ 
	position: relative; 
} 
.member-column li p a.delete{ 
	position: absolute;	 
	right: 5px; 
	top: 3px; 
	display: none; 
} 
.member-column li p a.edit{ 
	position: absolute;	 
	right: 25px; 
	top: 3px; 
	display: none; 
} 
.group-list{ 
	display: none; 
	margin-left: -1px; 
	margin-right: -1px; 
} 
li.all-members, ul.all-members{ 
	display: block; 
} 
.roster-groups{ 
	width: 152px; 
	display: inline-block; 
	float: left; 
	margin-left: -1px; 
	position: absolute; 
	overflow-x: hidden; 
	overflow-y: auto; 
} 
.roster-groups, .group-list, .contact-info{ 
	font-size: 12px; 
	color: #2e2e2e; 
	font-weight: bold; 
} 
.roster-groups li span{ 
	background: #f6f6f6; 
	border: 1px solid #c1c1c1; 
	width: 140px; 
	display: inline-block; 
	padding: 5px; 
	margin-top: -1px; 
} 
ul.group-list li p{ 
	display: inline; 
} 
.roster-groups span:hover, ul.group-list li:hover{ 
	background: #dcdcdc; 
	cursor: pointer; 
} 
.roster-groups span:active, .roster-groups span.active, ul.group-list li:active, ul.group-list li.active{ 
	background: #3180d0; 
} 
 
		ul.group-list li{ 
			background: #f6f6f6; 
			border: 1px solid #c1c1c1; 
			padding: 5px; 
			margin-top: -1px; 
		} 
			ul.group-list li input{ 
				display: inline; 
				margin-right: 5px; 
				position: relative; 
				top: 1px; 
			} 
.contact-info{ 
	display: inline; 
	height: 200px; 
	text-align: left; 
	position: absolute; 
	left: 340px; 
	width: 100%; 
} 
.contact-info *{ 
	display: inline; 
} 
.contact-info ul li{ 
	text-align: left; 
	display: none; 
} 
.contact-info ul li.showfirst{ 
	display: block; 
} 
.contact-info p{ 
	line-height: 20px; 
} 
.module-title{ 
	position: relative; 
} 
.module-title .add{ 
	position: absolute; 
	right: 15px; 
	top: 12px; 
} 
body{ 
background: url(images/pattern.jpg); 
} 
h1.company-title{ 
	color: #fff; 
} 
h2{ 
	color: #2e2e2e; 
} 
.sub-text{ 
	color: #b6b6b6; 
} 
a{ 
	color: #78bde0; 
} 
b{ 
	color: #2e2e2e; 
} 
.modalwrap{ 
	border: 2px solid #316d8f; 
} 
.module-title{ 
	color: #2e2e2e; 
}

Full Screencast



Background Image

In each of my tutorials, where possible, I like to feature a free web service of some kind that will assist you guys in future projects. In this tutorial, we used a BG Patterns to create a tiling background image. While a tiling background image might not fit the greatest into this sort of project, it still is a great service for some projects.

Picking An Image

First you pick one of many different icons for your base image. For this example, I thought the @ sign was the perfect choice.


Picking A Canvas

You can also specify a texture to overlay on the image. For this example, we think that it is best to just put the texture opacity down to zero, for our application's style. .


Picking Colors

As you can see, we chose a light blue and white for the background and foreground colors, respectively.


Result

Here's the result.



Starting with the jQuery

Now it's time to get started with the jQuery itself. Let's start by outlining everything we need to do for right now:

  • When a group in the first column is clicked, show all contacts in that group in the second column.
  • When a contact is clicked on in the second row, show contact info in third column/box.
  • When a contact item is being hovered on, show delete and edit icons. (Needed for future functionality)
  • Maintain an array that contains all selected contacts. (Needed for future functionality)

What we have so far

Notice that right now we have three self contained columns: ul.roster-groups, div.member-column, and div.contact-info. These columns are in order from left to right. Notice that within the roster-groups we have list items that each have a class with the group number. Notice also that in the .member-column we have unordered lists that correspond to the group classes. We will use this to link the members to the groups.


Sort out the groups

As mentioned above, the members are linked to a group, by the way that the classes are structured.

Here is how we will sort out the groups, I will explain it line by later afterwords:

 
$(document).ready(function(){ 
	 
 
	/*Show contacts from certain group*/ 
	$(".roster-groups li").click(function(){ 
		$(".roster-groups li span").removeClass("active"); 
		group = $(this).attr("class"); 
		group = ".member-column ul." + group; 
		$(".member-column ul").hide(); 
		$(this).children("span").addClass("active"); 
		$(group).fadeIn("slow"); 
	}); 
 
		 
 
});

Line by Line

  • First we do the standard on dom ready function that we always need for any jQuery code.
  • Then we initiate an on click listener that waits for a .roster-groups li to be clicked.
  • First we remove the active class from all group items. This will make sure that no other group is active.
  • Then we take the class name of what was clicked. This contains a specific group number.
  • We then add this to a pre-defined selector and put it in a variable called "group"
  • Next, we hide any member column that was showing.
  • Then we make the item that was clicked active
  • Finally, we show the specific group of members in the second column, with the group variable we created earlier.

Show Specific Contact Info

Next, we need to be able to show a specific contact's information.

As usual, I'll give you the code, and explain it line for line afterwards:

 
	/*Show specific contact info*/ 
	$("ul.group-list li").click(function(){ 
		$("ul.group-list li").removeClass("active"); 
		contact = $(this).attr("class"); 
		contact = "li." + contact; 
		$(".contact-info ul li").hide(); 
		$(this).addClass("active"); 
		editLink = $(this).children("p").children("a.edit").attr("href"); 
		$(".edit-member-dialog").attr("href",editLink).addClass("alive"); 
		$(contact).fadeIn("slow"); 
	});

Line by Line

  • First, we initiate an on click listener that waits for a ul.group-list li to be clicked.
  • First we remove the active class from all contact items. This will make sure that no other contact is active.
  • Then we take the class name of what was clicked. This contains a specific contact number.
  • We then add this to a pre-defined selector and put it in a variable called "contact"
  • Next, we hide any contact box that was showing.
  • Then we make the item that was clicked active
  • Next we get the edit link address, and add it to another element. Don't worry about these two lines for now. They will come into play in the AJAX tutorial.
  • Finally, we show the specific contact in the third column/box, with the contact variable we created earlier.

Hide & Show Edit & Delete Links

Although we won't cover server side in this tutorial, we will eventually add CRUD capabilities (Create-Read-Update-Delete) when we turn this into a codeigniter application. We will add the edit and delete links now. We want them to appear by any member when they are hovered over.

As usual, I'll give you the code, and explain it line for line afterwards:

 
	/*Hide and show delete & edit links*/ 
	$(".member-column li").hover(function(){ 
		$(".member-column li p a").hide(); 
		$(this).children('p').children('a').show(); 
	},function(){ 
		$(this).children('p').children('a').hide(); 
	});

Line by Line

  • First, we initiate an on hover listener that waits for a .member-column li to be clicked.
  • Then we make sure that no delete or edit links are already showing, by using a general selector.
  • Then we show the links for the element being hovered on only.
  • We then initiate a second on mouse out function.
  • We then hide the links

Maintain an Array of Selected Elements

In the second part of this tutorial, we will cover how to delete multiple contacts at once. We will use checkboxes by every contact to allow the user to select multiple contacts. However, we still need to use javascript to generate an array of the elements that are currently selected.

As usual, I'll give you the code, and explain it line for line afterwards:

 
//Maintain which are selected in Array 
	 
	$(".selectbox").change(function () { 
          checked = []; 
          var size = $(".selectbox:checked").size();  
          var i; 
          for (i=0;i<size;i++){ 
          	 
            checked[i] = $(".selectbox:checked:eq("+i+")").siblings('p').children("a.delete").attr("href");           
 
    	  }  
        }) 
        .trigger('change');

Line by Line

  • As you can see, we gave the checkboxes a class of .selectbox. We do this so that this code will not confuse these checkboxes with any other checkboxes that may be in the application.
  • We then create a new empty array called checked.
  • We then count the number of selected checkboxes and put the number in a variable called size.
  • We then create another variable, i, which will serve as a counting variable.
  • Next, we start a for function that starts i off at zero. It then runs through the function once and starts over. But the next time, it adds 1 to i. It continues to loops through until i is no longer smaller then the number of selected checkboxes.
  • We then have a trigger of change. This allows us to detect when there's a change in selected checkboxes and start the whole things over again.

The Whole Script

Now that we've gone line-by-line, here's the whole script so far:

 
$(document).ready(function(){ 
	 
 
	/*Show contacts from certain group*/ 
	$(".roster-groups li").click(function(){ 
		$(".roster-groups li span").removeClass("active"); 
		group = $(this).attr("class"); 
		group = ".member-column ul." + group; 
		$(".member-column ul").hide(); 
		$(this).children("span").addClass("active"); 
		$(group).fadeIn("slow"); 
	}); 
	/*Show specific contact info*/ 
	$("ul.group-list li").click(function(){ 
		$("ul.group-list li").removeClass("active"); 
		contact = $(this).attr("class"); 
		contact = "li." + contact; 
		$(".contact-info ul li").hide(); 
		$(this).addClass("active"); 
		editLink = $(this).children("p").children("a.edit").attr("href"); 
		$(".edit-member-dialog").attr("href",editLink).addClass("alive"); 
		$(contact).fadeIn("slow"); 
	}); 
	/*Hide and show delete & edit links*/ 
	$(".member-column li").hover(function(){ 
		$(".member-column li p a").hide(); 
		$(this).children('p').children('a').show(); 
	},function(){ 
		$(this).children('p').children('a').hide(); 
	}); 
	 
	 
 
	 
	//Maintain which are selected in Array 
	 
	$(".selectbox").change(function () { 
          checked = []; 
          var size = $(".selectbox:checked").size();  
          var i; 
          for (i=0;i<size;i++){ 
          	 
            checked[i] = $(".selectbox:checked:eq("+i+")").siblings('p').children("a.delete").attr("href");           
 
    	  }  
        }) 
        .trigger('change'); 
 
	 
 
		 
 
});

Wrapping Up

Congratulations, you now have a basic address book front-end! But it doesn't stop here, we still have to add the back end and eventually add AJAX!

Section 2


Are You New to Codeigniter and MVC?

Rather than reteaching what has already been taught many times before, this tutorial assumes that you have a basic knowledge of PHP and an MVC framework. That being said, if you find yourself confused over anything in this tutorial, be sure to check out these past tutorials (Thank you Dan Harper for gathering these up!):

Video Tutorial: Hello World! Introduction to CodeIgniter

This 10-minute video tutorial on the CodeIgniter by Derek Jones is a fantastic introduction to CodeIgniter, as Derek shows you the basics in writing CodeIgniter code and using the Controller and View.

Read More

Video Tutorial: Create a Blog in 20 Minutes

Continuing from the previous video above, watch as Derek creates a basic blog from scratch in just 20-minutes! Everything is explained in great detail, and this is a must watch for anyone wanting to start out in CodeIgniter.

Read More

Nettuts+: Everything You Need to Get Started With CodeIgniter

CodeIgniter is a web application framework for PHP. It enables developers to build web applications faster, and it offers many helpful code libraries and helpers which speed up tedious tasks in PHP. CodeIgniter is based on a modular design; meaning that you can implement specific libraries at your discretion - which adds to the speed of the framework. This tutorial will attempt to show you the basics of setting up the framework, including how to build a basic hello world application that uses the MVC approach.

Read More

Nettuts: Creating a File Hosting Site with CodeIgniter

I have seen a few introductory tutorials for Codeigniter, and was hoping to show you something a little more advanced. This tutorial will show you how to build a powerful web application for hosting images, using the flexibility of Codeigniter. This tutorial should teach you about the MVC coding philosophy, integral to producing serviceable applications.

Read More

Book: Professional CodeIgniter

In my opinion, this is the gem of CodeIgniter books. Written by Thomas Myer, this book starts right at the beginning, and works through real-world scenarios until you've built a feature-filled e-commerce store and shopping cart for your imaginary client.

Read More

CodeIgniter User Guide

CodeIgniter holds pride in itself for itself heavily-documented User Guide covering every aspect of every function CodeIgniter contains. Stuck or need ideas? A quick look through here will set you back on track.

Read More


Objectives:

At the end of this tutorial, we want to accomplish the following:

  • Use codeigniter to hook the application up with a database
  • CRUD functions (Create-Read-Update-Delete)

Basically, we want to transform the front end application, that we currently have, into a functional application that can store, retrieve, and delete records for us.



Getting Started

Let's start off by creating the database that will be the base of the project data. First, we need to think about everything we want to store. What information about the contact do we need?

Contact Information (users2 table):

  • User ID (Primary Key)
  • Name
  • Email
  • Job Title
  • Address Line 1
  • Address Line 2
  • Home Phone
  • Cell Phone
  • Business Phone
  • Group

Additionally, we'll need to create a second table to store the groups.

Groups (groups2 table)

  • Group ID (Primary Key)
  • Group Name

That looks good. Feel free to add whatever info your project might require or subtract any unnecessary fields as well.

 
CREATE TABLE IF NOT EXISTS `users2` ( 
  `userID` int(25) NOT NULL AUTO_INCREMENT, 
  `name` varchar(56) NOT NULL, 
  `email` varchar(255) NOT NULL,   
  `title` varchar(255) NOT NULL, 
  `addressLine1` varchar(128) NOT NULL, 
  `addressLine2` varchar(128) NOT NULL, 
  `homePhone` varchar(28) NOT NULL, 
  `cellPhone` varchar(28) NOT NULL, 
  `businessPhone` varchar(255) NOT NULL, 
  `group` varchar(28) NOT NULL, 
  PRIMARY KEY (`UserID`) 
); 
 
CREATE TABLE IF NOT EXISTS `groups2` ( 
  `groupID` int(11) NOT NULL AUTO_INCREMENT, 
  `Group` varchar(255) NOT NULL, 
  PRIMARY KEY (`groupID`) 
);  
 
 
INSERT INTO `groups2` (`groupID`, `Group`) VALUES 
(1, 'Office'), 
(2, 'Contacts'), 
(3, 'Other');

NOTE: If you don't have any experience with databases, you might be wondering how to add a table and do the previous step. If this is the case, you have to look at your server set up. Is it local? Is it remote? What OS are you using? The first place I'd look is in your server software documentation. You'll probably be using some sort of mySQL. If that is the case, you might also want to check out their site's documentation.

Also, note that we used users2 and groups2. This is in order to avoid conflict with some session and user management libraries that use CI. Some of these libraries use users and groups as database tables. By appending 2 onto our table names, we can avoid this conflict from the start.


Get & Configure Codeigniter

The first step that we have to do is to go grab codeigniter. You can download codeigniter directly from their site.

After you download the zip file, you will need to extract all of the framework files. Although configuring codeigniter is a very simple process, we don't need to reinvent the wheel here. If you don't already know how to set CI up, be sure to watch Jeffrey Way's first installation of the amazing Codeigniter from scratch series. You can watch it here.

What do we need to do?

Before we go any further, we need to ask ourselves what we all have to accomplish so that we can plan it out before we start writing code. This will allow us to organize our code better. Let's start by listing everything:

  • Create all needed database integration through the members model.
  • Build the CRUD capability in the members controller.
  • Integrate the dynamic content within the CI views.

A look at the finished product

We want something like this when we're finished.

Autoload Libraries & Helpers

Before we go any further, let's autoload some libraries and helpers for Codeigniter. CI libraries and helpers help us write less code. To autoload things, open up config/autoload.php and replace this:

 
 
$autoload['libraries'] = array(); 
 
/*There are some comments in between here */ 
 
$autoload['helper'] = array();

With this:

 
 
$autoload['libraries'] = array('database','session','form_validation','validation'); 
 
/*There are some comments in between here */ 
$autoload['helper'] = array('url', 'form', 'html');

Creating the Model

As you know (since if you didn't already you watched the introduction CI from scratch first video), codeigniter follows the MVC pattern (Model-View-Controller). Each part is equally important. For this tutorial, we'll create the model first, since we've already created the database. First we need to create a file called members_model.php in the models folder.

After you have created the file, we'll fill in some default things first. These are the standard basic requirements for a model:

<?php 
 class Members_model extends Model { 
}

The only part that changes above it the Members_model part. This is the capitalized version of the model file name.

Next, we need to define four functions for different ways we need to access certain information from the database:

 
<?php 
 
	class Members_model extends Model { 
		 
		function getAll() { 
			$q = $this->db->get('users2'); 
				 
			if($q->num_rows() > 0) { 
				foreach ($q->result() as $row) { 
					$data[] = $row; 
				} 
				return $data; 
			} 
		} 
		function getGroups() { 
			$q = $this->db->get('groups2'); 
				 
			if($q->num_rows() > 0) { 
				foreach ($q->result() as $row) { 
					$data[] = $row; 
				} 
				return $data; 
			} 
		} 
		function getID() { 
  			$id = $this->uri->segment(3); 
  			$this->db->where('UserID', $id); 
			$q = $this->db->get('users2'); 
			 
			if($q->num_rows() > 0) { 
				foreach ($q->result() as $row) { 
					$data[] = $row; 
				} 
				return $data; 
			} 
  		} 
  		function update() {  
		  $id = $this->uri->segment(3);  
		  $data = array( 
	               'Username'    =>  $this->form_validation->set_value('Username'),   
			       'Password'   =>  $this->form_validation->set_value('Password'),   
			       'Email' =>  $this->form_validation->set_value('Email'),  
			       'Name' =>  $this->form_validation->set_value('Name'), 
			       'Title'    =>  $this->form_validation->set_value('Title'),   
			       'AddressLine1'   =>  $this->form_validation->set_value('AddressLine1'),   
			       'AddressLine2' =>  $this->form_validation->set_value('AddressLine2'),  
			       'HomePhone' =>  $this->form_validation->set_value('HomePhone'), 
			       'CellPhone'    =>  $this->form_validation->set_value('CellPhone'),   
			       'BusinessPhone'   =>  $this->form_validation->set_value('BusinessPhone'),   
			       'Group' =>  $this->form_validation->set_value('Group') 
	            ); 
			 
		  $this->db->where('UserID', $id); 
		  $this->db->update('users2', $data);  
		   
		} 
	}

getALL(): This function will do exactly what it suggests; it will grab everything from the users2 table. It then returns what it gets in the $data variable.

getGroups(): This function is similar to getALL, except it grabs all of the groups that are in the database group.

getID(): This function will come into play when we need to view the information for a specific user. We will generate a URL that has the user ID as the third segment. Then this function will read that segment, get the user's ID, and grab all of that users information from that specific row.

update(): This function will allow us to edit and update the database. It will also read the third segment of the URL to get the ID of the user it is supposed to update. It then gets all of the data from the form_validation library, which you'll see more about in the controller. It then targets the specific row using the user's ID, and updates the row with the data.


Creating the Controller

Now that we can interact with the database, we need to create all of the logic necessary for this application to function. Since we will add AJAX capabilities after this, some of this might not work properly yet. (i.e. it might not redirect to the right page, etc.)

Default Controller Class

First, we need to create the controller file. We'll create a new file in the application/controllers/ folder and name the file members.php.

Inside the file, we'll type the code that will make it a controller:

 
<?php  
 
class Members extends Controller { 
	 
		function Members() { 
			parent::Controller(); 
		} 
}

Next, we need to create a class for every page that we will need. First, we need to decide what pages we need:

  • index(): This page will be the default page and will display the address book in it's default state.
  • add(): This page will allow you to add a new contact. By the finished product, this page will only be a backup page. This is because we will be using AJAX to load a simpler add page instead.
  • add_include(): This will be the page that will replace add() when we introduce AJAX. It will not include the header or the footer parts of the page. We will then load this page in an iframe javascript popup window.
  • insert(): This won't be an actual page, but it will allow us to add data to the database. This really should be in a model if we were to comply 100% to the MVC architecture. However, for such a simple line in such a simple project, it doesn't make much sense to write more lines to achieve the same thing. Especially when we're also trying to stay along the KISS method (Keep it simple stupid or Keep it short and simple).
  • insertgroup(): This is pretty much the same things as insert(). The only difference is that it will add groups to the group database.
  • update_include(): This is very similar to update. We created this for the same reason that we created add_include().
  • update(): This is where we will edit contact.
  • delete(): This class will not surprisingly allow us to delete entries. Again, this should probably be in the model. But look above for the reasons we didn't put it there.
  • deletegroup(): This class will do the same thing as delete(), but will handle groups.

Now that we know what we have to code, let's do it:

 
<?php  
 
class Members extends Controller { 
	 
		function Members() { 
			parent::Controller(); 
		} 
		 
		function index() { 
			$data['title'] = "Members"; 
			$this->load->model('members_model'); 
			$data['members'] = $this->members_model->getAll(); 
			$data['groups'] = $this->members_model->getGroups(); 
			$this->load->view('header', $data); 
			$this->load->view('members/sidebar', $data); 
			$this->load->view('members/widget', $data); 
			$this->load->view('footer', $data); 
		} 
		 
		function add() { 
			$data['title'] = "Add Members"; 
			$this->load->model('members_model'); 
			$data['members'] = $this->members_model->getAll(); 
			$data['groups'] = $this->members_model->getGroups(); 
			$this->load->view('header', $data); 
			$this->load->view('members/add', $data); 
			$this->load->view('footer', $data); 
		} 
		function add_include(){ 
			$data['title'] = "Add Members"; 
			$this->load->model('members_model'); 
			$data['members'] = $this->members_model->getAll(); 
			$data['groups'] = $this->members_model->getGroups(); 
			$this->load->view('head-links', $data); 
			$this->load->view('members/add'); 
		}		 
		function insert() { 
			$this->db->insert('users2', $_POST); 
			$this->session->set_flashdata('success', 'Member successfully added. Would you like to <a href="../members">view members</a>?'); 
			redirect('members/add_include'); 
		} 
		function insertgroup() { 
			$this->db->insert('groups2', $_POST); 
			$this->session->set_flashdata('success', 'Group successfully added. Would you like to <a href="../members">view members</a>?'); 
			redirect('members/'); 
		} 
		function update_include() {  
		  $this->form_validation->set_rules('Username', 'User Name', 'required|max_length[255]|htmlspecialchars');   
		  $this->form_validation->set_rules('Password', 'Password', 'required|max_length[255]|htmlspecialchars');  
		  $this->form_validation->set_rules('Email', 'Email', 'valid_email|required|htmlspecialchars');  
		  $this->form_validation->set_rules('Name', 'Name', 'required|max_length[255]|htmlspecialchars'); 
		  $this->form_validation->set_rules('Title', 'Title', 'max_length[255]|htmlspecialchars');   
		  $this->form_validation->set_rules('AddressLine1', 'Address Line 1', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('AddressLine2', 'Address Line 2', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('HomePhone', 'Home Phone', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('CellPhone', 'Cell Phone', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('BusinessPhone', 'Business Phone', 'htmlspecialchars');   
		  $this->form_validation->set_rules('Group', 'Group', 'htmlspecialchars');   
		     
		  if ($this->form_validation->run() == FALSE) {   
		  	$data['title'] = "Editing Member"; 
			$this->load->model('members_model'); 
			$data['groups'] = $this->members_model->getGroups(); 
			$data['members'] = $this->members_model->getID();  
			$this->load->view('head-links', $data); 
			$this->load->view('members/edit'); 
		  }   
		  else {   
		  	$id = $this->uri->segment(3); 
		    $this->load->model('members_model');	 
			$this->members_model->update();  
			$this->session->set_flashdata('success', 'Member information updated successfully');  
  			redirect('members/update_include/'. $id);   
		  }   
			 
		} 
		function update() {  
		  $this->form_validation->set_rules('Username', 'User Name', 'required|max_length[255]|htmlspecialchars');   
		  $this->form_validation->set_rules('Password', 'Password', 'required|max_length[255]|htmlspecialchars');  
		  $this->form_validation->set_rules('Email', 'Email', 'valid_email|required|htmlspecialchars');  
		  $this->form_validation->set_rules('Name', 'Name', 'required|max_length[255]|htmlspecialchars'); 
		  $this->form_validation->set_rules('Title', 'Title', 'max_length[255]|htmlspecialchars');   
		  $this->form_validation->set_rules('AddressLine1', 'Address Line 1', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('AddressLine2', 'Address Line 2', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('HomePhone', 'Home Phone', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('CellPhone', 'Cell Phone', 'htmlspecialchars'); 
		  $this->form_validation->set_rules('BusinessPhone', 'Business Phone', 'htmlspecialchars');   
		  $this->form_validation->set_rules('Group', 'Group', 'htmlspecialchars');   
		     
		  if ($this->form_validation->run() == FALSE) {   
		  	$data['title'] = "Editing Member"; 
			$this->load->model('members_model'); 
			$data['groups'] = $this->members_model->getGroups(); 
			$data['members'] = $this->members_model->getID();  
			$this->load->view('header', $data); 
			$this->load->view('members/edit', $data); 
			$this->load->view('footer', $data); 
		  }   
		  else {   
		    $this->load->model('members_model');	 
			$this->members_model->update();  
			$this->session->set_flashdata('success', 'Member information updated successfully');  
  			redirect('members/');   
		  }   
			 
		} 
		function delete() { 
			$id = $this->uri->segment(3); 
			$this->db->where('UserID', $id); 
			$this->db->delete('users2'); 
			$this->session->set_flashdata('caution', 'User has been deleted.');   
		    redirect('members/');   
		} 
		function deletegroup() { 
			$groupid = $this->uri->segment(3); 
			$this->db->where('groupID', $groupid); 
			$this->db->delete('groups2'); 
			$this->db->last_query(); 
			$this->session->set_flashdata('caution', 'Group has been deleted.');   
		    redirect('members/'.$id);   
		} 
		 
	 
		 
}

Now that we have the code down, let's cover what we all did. As you look through the above code closer, you will notice that most of it is repetitive. That being said, let's cover all of the principles by class. If I already explained the principle while explaining another class, I'll skip it:

  • index():
    • First we passed some data to the view. In this case, we passed Members as the title of the page. As you will see, we will use this to title our page.
    • Next, we load the members model class so that we can access the functions inside of it. These functions are the ones we created earlier that will allow us to access information from the database. We then load the getAll() and getGroups() functions. We assign the information we receive from the database to data variables.
    • After that, we load the views. In a real world project, this is where we would create the views. But since this is a tutorial, we already know what we're going to call our views. Notice that we also pass the data as the second parameter.
  • insert():
    • First off, notice that in this class, we don't load any views.
    • Next, we insert the data received into our database. We receive the data from a global POST variable sent from a form in the view.
    • Next, we set a flashdata text. This is a codeigniter feature, that basically will send a message variable to the next thing loaded. It is basically a really short term variable. There is two steps to flashdata. First, you have to set the flashdata (which we just did). Then you have to display the falshdata in the view. We will do that in the view later.
    • Finally, we redirect the user back to the members controller. They then load the index class by default.
  • update_include():
    • With this class (and the normal update class), we start out with a bunch of validation rules. These will help ensure that we're not putting anything dangerous into our databases. These lines are pretty self-explanatory.
    • After the rules, we set up an if else statement to check if the validation passed. If it didn't, the class reloads. If it passes, it updates the database. This server side is only a backup protection. We will also add front end validation using jQuery later.

Creating the Views

As we've already established, the view is the part of the application that contains all front-end associated code. (Client Side) Basically, we're going to use everything we created in part 1 on this tutorial. As you noticed, in some classes, we loaded views. However, usually we loaded three views: header, footer, and members/edit. This method of compartmentalizing views is one strategy of organizing a project. In this case, we split the footer and header content, from the ever changing middle content. This way we can have the constant parts of the page editable in one place. Since the views contain many components necessary for the AJAX step, it doesn't make sense to cover it twice. Rather, we will cover it as the first section in the last part of this tutorial.

Section 3


Objectives:

At the end of this tutorial, we want to accomplish the following:

  • Create a fully functional Contact Manager that never needs to total page reload to function.

Basically, we want to transform the dynamic application we have right now, into a sleek AJAX one.


Creating the Views

As we've already established, the view is the part of the application that contains all front-end associated code. (Client Side) Basically, we're going to use everything we created in part 1 on this tutorial. As you noticed, in some classes, we loaded views. However, usually we loaded three views: header, footer, and members/edit. This method of compartmentalizing views is one strategy of organizing a project. In this case, we split the footer and header content, from the ever changing middle content. This way we can have the constant parts of the page editable in one place. So let's create the views, one by one:

Header.php

I'll show you the code and explain afterwards:

<?php $this->load->view('head-links',$title); ?><div class="dialogframe" title="Dialog Window"><span class="loadingshown"></span><iframe src="members/add_include" width="100%" height="100%">Sorry...an error occurred. Please try again.</iframe></div><div class="confirm" title="Are you sure?">Are you sure you want to delete this?</div><div class="confirmmultiple" title="Are you sure?">Are you sure you want to delete these?</div>	<div class="wrap">		<div class="company-brand">        	            <h1 class="company-title">Address Book</h1>		</div><!-- End company-brand -->        	<ul class="top-nav main-nav">            	<li><a href="<?=site_url();?>" title="Click here to go Home">Home</a></li>                <li><a href="<?=site_url();?>members" title="Click here to view Members" class="current">Members</a></li>            </ul><!-- End top-nav -->            <div class="page">            	<?php			if ($this->session->flashdata('success')) {   		 		echo '<div class="ui-widget notification"><div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;"><p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>' . $this->session->flashdata('success') . '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>';  			} else if ($this->session->flashdata('caution')) {   		 		echo '<div class="notification ui-widget"><div class="ui-state-error ui-corner-all" style="padding: 0 .7em;"><p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span> ' . $this->session->flashdata('caution') . '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>';  			} ?>
  • First, you'll notice that we load another view in the first line of the header view. While you could load this view from the controller (and it would be more MVC), for this project's requirements, it's a lot easier to just include this in the view. That's because when we add AJAX, we don't always want to load those CSS and JS links every time.
  • Next, you'll notice that we have a div with a class of dialogframe. This will serve as the dialog box where we will load new content, when we apply the AJAX.
  • After some basic HTML, you'll notice we use site_url(). This is a nice feature of CI that allows us to write in the url of the site, in a nonstatic fashion for added portability.
  • Lastly, you see another PHP segment. Remember where we set the flashdata in the controller? Here is the second part of that. Here, we listen for any set flashdata, and if there is any, we display it.

Head-links.php

This is the page links that we loaded from inside the header view:

 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title><?php if($title!==null)echo $title; ?></title> 
<link rel="stylesheet" type="text/css" href="<?=site_url('assets/css/layout.css'); ?>" media="screen"> 
<link rel="stylesheet" type="text/css" href="<?=site_url('assets/css/1.css'); ?>" media="screen"> 
<link rel="stylesheet" type="text/css" href="<?=site_url('assets/css/ui-theme/ui.css'); ?>" media="screen"> 
<script type="text/javascript" src="<?=site_url('assets/js/jq.js'); ?>"></script> 
<script type="text/javascript" src="<?=site_url('assets/js/ui.js'); ?>"></script> 
<script type="text/javascript" src="<?=site_url('assets/js/val.js'); ?>"></script> 
<script type="text/javascript" src="<?=site_url('assets/js/js.js'); ?>"></script> 
<script src="<?=site_url('assets/js/cal.js'); ?>" type="text/javascript" charset="utf-8"></script> 
<link rel="stylesheet" href="<?=site_url('assets/css/cal.css'); ?>" type="text/css" charset="utf-8"> 
 
</head> 
 
<body onload="init();">
  • First, you'll notice that we check if there is a title for the page. If there is, we display is. This prevents an error being our title if in some weird case a title is never set.
  • Next, we set several stylesheets. Notice how we use the site_url function to link to the stylesheets. Again, this gives us more flexibility and portability than if we just hard coded the links in.
  • We then do the same with the JS files. Notice that we are loading a ui.js and a ui.css file. You might have guessed by now that this means that we are going to use jQuery's UI library.
  • The last point of interest for this view is the javascript function we load when the body is loaded: init(). You will learn more about this in a later step.

Footer.php

This just closes all open tags form the header. Nothing complicated here:

                 
                 
<div class="clear"></div> 
             
</div><!-- End page -->	 
</div><!-- End page-wrap -->	 
</body> 
</html>

Members/widget.php

This is where all the magic happens. Most of this is just necessary HTML/CSS. You've already seen most of this from part 1 of the tutorial. However, you'll notice instead of the jargon data returned, we now have cleaner dynamic PHP instead. Look at the code, and I'll explain it afterwards.

              
             < div class="widget"> 
            	 < div class="roster widget-content module leftcolumn large"> 
            		 < div class="module-title">Employees < span class="loading"> < /span> < span class="ui-state-default ui-corner-all add"> < a class="ui-icon ui-icon-circle-plus frameopen" href=" < ?=site_url('members/add_include');?>" title="Add Member"> < /a> < /span> < /div> 
                     < ul class="roster-groups"> 
                    	 < li class="group"> < span class="active">All Employees < /span> < /li>   
                    	 < ?php foreach($groups as $row) : ?> 
                         < li class="group < ?= $row->groupID ?>"> < span> < ?= $row->Group ?> < /span> < /li>   
                         < ?php endforeach; ?>                     
                     < /ul> < !-- End roster-groups --> 
                     < div class="member-column"> 
                         < ul class="group-list group all-members"> 
                        	 < ?php if($members > 0):?> 
                        		 < ?php foreach($members as $row) : ?> 
                                     < li class="contact < ?= $row->UserID ?>"> < input type="checkbox" class="selectbox"/> < p> < ?= $row->Name ?> < a class="edit frameopen" href=" < ?=site_url('members/update_include/');?>/ < ?=$row->UserID?>" title="Edit this member"> < img src=" < ?=site_url('assets/css/images/edit-trans.png');?>" alt="edit"> < /a> 
         < a class="delete" href=" < ?=site_url('members/delete/');?>/ < ?=$row->UserID?>" title="Click here to delete this task"> < img src=" < ?=site_url('assets/css/images/delete-trans.png');?>" alt="edit" > < /a> < /p> < /li> 
                                 < ?php endforeach; ?> 
                              < ?php endif; ?> 
                         < /ul> 
                         < ?php foreach($groups as $row) : ?> 
                         < ul class="group-list group < ?= $row->groupID ?>"> 
                                 < ?php $currentGroup = $row->Group ?> 
                                 < ?php foreach($members as $row) : ?> 
                                	 < ?php $currentContactGroup = $row->Group ?> 
	                                 < ?php if($currentGroup == $currentContactGroup):?> 
	                                     < li class="contact < ?= $row->UserID ?>"> < input type="checkbox" /> < p> < ?= $row->Name ?> < a class="edit frameopen" href="members/update_include/ < ?=$row->UserID?>" title="Click here to edit this task"> < img src=" < ?=site_url('assets/css/images/edit-trans.png');?>" alt="edit"> < /a> 
         < a class="delete" href="members/delete/ < ?=$row->UserID?>" title="Click here to delete this task"> < img src=" < ?=site_url('assets/css/images/delete-trans.png');?>" alt="edit"> < /a> < /p> < /li> 
                                     < ?php endif; ?> 
                                 < ?php endforeach; ?> 
                         < /ul> 
                         < ?php endforeach; ?>      
                      < /div> < !-- End member-column --> 
                     < div class="contact-info"> 
                       
                       < ?php if($members > 0):?> 
                      	 < ul> 
                      	 < li class="showfirst"> < br /> < h1 class="sub-text">Click on a contact to view their information < /h1> < /li> 
                      
					   < ?php foreach($members as $row) : ?> 
                    	   	 < li class="contact < ?= $row->UserID ?>">                          
                                 < h2> < ?php echo $row->Name; ?> < /h2> 
                                 < h3 class="sub-text"> < ?php echo $row->Title; ?> < /h3> < br /> 
                                 < h3 class="sub-text"> < a href="mailto: < ?php echo $row->Email; ?>"> < ?php echo $row->Email; ?> < /a> < /h3> < br /> < br /> 
                                 < p class="sub-text"> < ?php echo $row->AddressLine1; ?> < /p> < br /> 
                                 < p class="sub-text"> < ?php echo $row->AddressLine2; ?> < /p> < br /> < br /> 
                                 < p class="sub-text"> < b>Business:  < /b> < ?php echo $row->BusinessPhone; ?> < /p> < br /> 
                                 < p class="sub-text"> < b>Cell Phone:  < /b> < ?php echo $row->CellPhone; ?> < /p> < br /> 
                                 < p class="sub-text"> < b>Home Phone:  < /b> < ?php echo $row->HomePhone; ?> < /p> < br /> 
                        	 < /li> 
                         
					 < ?php endforeach; ?> < /ul> 
					  < ?php else:?> 
					 	 < li class="showfirst"> < br /> < h1 class="sub-text">It appears there are no contacts yet. Would you like to  < a class="add" href="members/add" title="Click here to add a contact">add some? < /a> < /h1> < /li> 
					  < ?php endif; ?> 
                    < /div> < !-- End contact-info--> 
				 < /div> < !-- End roster-module--> 
			 < /div> < !-- End widget -->

Foreach Statements/Loops

You'll notice we used several foreach statements. Hopefully you already know what a foreach statement is. But in case you don't, here is a quick rundown. A foreach is a very useful loop. Here is the basic syntax:

 
 < ?php foreach($groups as $row) : ?> 
      < li class="group < ?= $row->groupID ?>"> < span> < ?= $row->Group ?> < /span> < /li>   
 < ?php endforeach; ?>

We start by specifying the set of data we want to use. In this case, we are using the $groups variable that was passed from the controller. This particular variable was set directly from a Model class called getGroups(). (The getGroups class just grabbed all of the rows from the database.) So, we assign this variable to a new variable. In this case, we call it $row. This second variable is where we put all of the data from one row of the variable, so it makes sense to call it row. Then, within the foreach statement, we can reference a particular row, by this variable name. As you can see, we reference parts of the row within the loop. (The groupID, and the Group) The foreach will run through the loop once, for the first row. Then it will start over with the second row. It will continue to repeat itself until it runs out of rows

If you'd like to learn more about foreach statements, check out php.net's docs.

Members/add.php

This is where we will facilitate the ability to create new records in the database. Basically, we just want to create fields for all database fields and give the user the ability to save the data they enter into the database. We already setup the model and controller part, so we don't need much more:

 
<?php 
			if ($this->session->flashdata('success')) {   
 		 		echo '<p class="success">' . $this->session->flashdata('success') . '</p>';   
			} ?> 
			<div class="module fullwidth"> 
            		<h2 class="module-title">Add Employee</h2> 
            		<div class="employee-add"> 
            		<?=validation_errors();?> 
            		 
            		<?php $attributes = array('class' => 'validate'); ?> 
            		<?=form_open('members/insert', $attributes)?> 
            		 
            		<p>Name:</p><input type="text" name="Name" class="required"> 
            		<p>Username:</p><input type="text" name="Username"  class="required"> 
            		<p>Password:</p><input type="password" name="Password"  class="required password" id="password1"> 
            		<p>Email:</p><input type="text" name="Email"  class="required email"> 
            		<p>Title:</p><input type="text" name="Title"> 
            		<p>Address Line 1:</p><input type="text" name="AddressLine1"> 
            		<p>Address Line 2:</p><input type="text" name="AddressLine2"> 
            		<p>Home Phone:</p><input type="text" name="HomePhone"> 
            		<p>Cell Phone:</p><input type="text" name="CellPhone"> 
            		<p>Business Phone:</p><input type="text" name="BusinessPhone"><br /> 
            		<select name="group"> 
						<?php foreach($groups as $row) : ?> 
	    					<option value="<?=$row->Group?>"><?= $row->Group ?></option> 
	                    <?php endforeach; ?>      
					</select>  
            		<button type="submit" class="save"></button> 
        		</div><!-- End employee-add--> 
            		</form> 
			</div><!-- End roster-module-->

There's not much to explain except for:

  • In the beginning we listen for any flashdata variables we might have sent to the view.
  • Then we check for any validation errors that occurred in the back end. This really should never happen, but is just one final fail-safe before the data, that could potentially be bad, reaches the database.
  • Next we use some codeiginiter shorthand to open up a form. The first argument we pass is the where the form should direct the data. Next, we pass a variable as the second argument. Notice, that we declared the variable on the previous line. This is basically how we add attributes to the form field when using the codeigniter shorthand. In this case, all we did is add a class of validate to the form. We'll use this later when we had jQuery validation to the form.
  • Then we have a bunch of regular old inputs like a typical form.
  • Next, we have another foreach statement! In this one, we display every group in the database as an option on the dropdown selection element.

Members/edit.php

This view is pretty similar to add.php. The only major difference is that we are already populating the fields with the pre-existing data that we are editing from the database.

 
		<?php 
			if ($this->session->flashdata('success')) {   
 		 		echo '<p class="success">' . $this->session->flashdata('success') . '</p>';   
			} ?> 
				<?php 
			if ($this->session->flashdata('success')) {   
 		 		echo '<div class="ui-widget notification"><div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;"><p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>' . $this->session->flashdata('success') . '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>';   
			} else if ($this->session->flashdata('caution')) {   
 		 		echo '<div class="notification ui-widget"><div class="ui-state-error ui-corner-all" style="padding: 0 .7em;"><p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span> ' . $this->session->flashdata('caution') . '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>';   
			} ?> 
			<div class="module fullwidth"> 
            		<h2 class="module-title">Edit Employee</h2> 
            		 
            	<?php if($members > 0):?> 
            	  <div class="employee-add"> 
            		<?php foreach($members as $row) : ?> 
            		<?=validation_errors();?> 
            		<?php $attributes = array('class' => 'validate'); ?> 
            		<?=form_open('members/update_include/'.$row->UserID , $attributes)?> 
            		 
            		<p>Name:</p><input type="text" name="Name" value="<?= $row->Name ?>" class="required"> 
            		<p>Username:</p><input type="text" name="Username" value="<?= $row->Username ?>" class="required"> 
            		<p>Password:</p><input type="password" name="Password" value="<?= $row->Password ?>" class="required password"> 
            		<p>Email:</p><input type="text" name="Email" value="<?= $row->Email ?>" class="required email"> 
            		<p>Title:</p><input type="text" name="Title" value="<?= $row->Title ?>"> 
            		<p>Address Line 1:</p><input type="text" name="AddressLine1" value="<?= $row->AddressLine1 ?>"> 
            		<p>Address Line 2:</p><input type="text" name="AddressLine2" value="<?= $row->AddressLine2 ?>"> 
            		<p>Home Phone:</p><input type="text" name="HomePhone" value="<?= $row->HomePhone ?>"> 
            		<p>Cell Phone:</p><input type="text" name="CellPhone" value="<?= $row->CellPhone ?>"> 
            		<p>Business Phone:</p><input type="text" name="BusinessPhone" value="<?= $row->BusinessPhone ?>"><br /> 
            		<select name="Group" value="<?= $row->Group ?>"> 
            			<option value="<?= $row->Group ?>" selected="selected"><?= $row->Group ?></option> 
						<?php foreach($groups as $row) : ?> 
	    					<option value="<?=$row->Group?>"><?= $row->Group ?></option> 
	                    <?php endforeach; ?>      
					</select>  
					 
					 
            		<button type="submit" class="edit"></button> 
        		</div><!-- End employee-add--> 
            		</form> 
            		 <?php endforeach; ?> 
            	<?php else: ?> 
            		<p>Sorry, but it appears this person does not exist in our records. Please try again.</p> 
            	<?php endif; ?> 
			</div><!-- End module--> 
			 
			 
            		 
			</div><!-- End roster-module-->

There's not much to explain except for:

  • In the beginning we listen for any flashdata variables we might have sent to the view. However, this time we also check to see if the flashdata is a message of success or of an error. Then we display a bunch of other HTML, which will be explained later in the javascript section.
  • Next, we check if there are records by seeing if the variable's entries are greater than 0.
  • We then use a foreach statement to fill in the data with the row of data we retrieved from the database. Remember that we are only retrieving one record because we are retrieving all records with the id that is in the url we are visiting.
  • The rest was covered in add.php.

Members/sidebar.php

This is what we stick on the side of every page. It will contain other useful features for the application such as giving the user the ability to :

  • Add Groups
  • Add Members
  • Edit Groups (Delete basically)
  • Edit selected member
  • Delete all checked members
 
		<div class="rightcolumn"> 
<div class="white-module small"> 
	<!-- Check if site has a logo, if so then do this line --> 
	<a href="<?=site_url();?>" title="Click to Return Home" class="logo"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/<?=site_url('assets/css'); ?>/images/logo.jpg" alt="Logo" /></a> 
</div><!-- End module --> 
 
<div class="rightcolumn small"> 
	<ul class="side-nav"> 
			<li><a href="#" class="ui-corner-all expandable">Add Group<span class="nav-icons ui-state-default"><span class="nav-icons ui-icon ui-icon-triangle-2-n-s"></span></span></a> 
				<div class="module rightcolumn small expand-content"> 
            		<div class="group-add"> 
            		<?=validation_errors();?> 
            		<?=form_open('members/insertgroup')?> 
            		 
            		<p>Group:</p><input type="text" name="Group"> 
            		<p><button type="submit" class="save"></button></p> 
            		</form> 
            		</div> 
        		</div><!-- End group-add--> 
			</li>				 
			<li><a href="members/add_include" class="frameopen ui-corner-all notexpandable dialog-link" title="Add Member"><span class="nav-icons ui-state-default"><span class="nav-icons ui-icon ui-icon-circle-plus"></span></span>Add Member</a> 
			</li> 
			<li><a href="#" class="ui-corner-all expandable"><span class="nav-icons ui-state-default"><span class="nav-icons ui-icon ui-icon-triangle-2-n-s"></span></span>Edit Groups</a> 
				<div class="module rightcolumn small expand-content"> 
            		<div class="group-edit"> 
            		<?php if($groups > 0):?> 
            			<?php foreach($groups as $row) : ?> 
	    					<div class="row"><a class="deletegroup" href="<?=site_url('members/deletegroup/');?>/<?=$row->groupID?>" title="Click here to delete this group"><img src="http://tutsplus.s3.amazonaws.com/tutspremium/web-development/039_contact_manager/images/<?=site_url('assets/css/images/delete-trans.png');?>" alt="delete"></a><?=$row->Group?></div> 
	                    <?php endforeach; ?> 
	                <?php else: ?> 
	                	<p>It appears that there are no groups yet. Please add some</p> 
	                <?php endif; ?> 
            				 
            			 
            		</div><!-- End group-edit --> 
            	</div><!-- End module--> 
			</li> 
			<li><a href="#" class="ui-corner-all notexpandable edit-member-dialog frameopen edit" title="Edit the selected member">Edit This Member<span class="nav-icons ui-state-default"><span class="nav-icons ui-icon ui-icon-circle-plus"></span></span></a> 
			</li> 
			<li><a href="#" class="ui-corner-all notexpandable delete-selected-dialog dialog-link edit" title="Delete Selected Items">Delete Selected<span class="nav-icons ui-state-default"><span class="nav-icons ui-icon ui-icon-circle-plus"></span></span></a> 
			</li> 
	</ul> 
</div><!-- End module --> 
</div>

By now, we've covered all concepts that we used here, so it should be pretty self-explanatory.


jQuery UI

We already have created the necessary javascript to make this app functional. We have also created the codeigniter functionality. Therefore, with a little tweaking, this app could be finished here. However, we're going to improve on it further by adding AJAX. We'll be taking advantage of the jQuery and the jQuery UI library to do this. To start off, grab a copy of jQuery UI and add it to the application directory.

Step 1: Go to http://jqueryui.com

One of the neat features of the jQuery UI library is that you can build your own version of features to suit your needs. For this app, we need the Core, all of the interactions (nice to have but not necessary), and the dialog and datepicker (the datepicker would be nice to have in the future so you might as well include it).

Step 2: Select your build from what you need

After you have selected what you need, you can proceed to download the build. For this app, we kept the theme to UI lightness (default).

Step 3: Download it.

While we're grabbing jquery files, we'll also need the jQuery validation plugin. You can get that from bassistance.de.

Step 4: Download jQuery validation plugin.


Adding JS Notifications

Now that we have jQuery UI, we can use some of it's prebuilt functionality. One thing that we can use is the notification feature, which is extremely useful in applications like this where you have to communicate to the user what's happening.

An example of what jQuery UI's notifications look like.

Remember how we already have flashdata in the application? We can use this flashdata to display these nicer looking UI notifications instead. Here's the basic syntax:

<div class="ui-widget notification"><div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;"><p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>Notification text<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>

You might have noticed we did this in above example when we had flashdata. This is why we did that.

Now that we're going to add JS validation though, we'll need to create a similar thing with javascript. We'll do this by declaring four variables (Beginning and End of Error and Success) at the beginning of the javascript:

 
successA = '<div class="ui-widget notification"><div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;"><p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>'; 
successB = '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>'; 
	 
errorA = '<div class="ui-widget notification"><div class="ui-state-error ui-corner-all" style="padding: 0 .7em;"><p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>'; 
errorB = '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>';
Adding the Close Functionality

We added a little bit to the notification however. We want the user to be able to close the notification if they like, so we added an 'x'. Now we need to make it work with a little jQuery:

$("span.close").click(function(){	$(this).parent().parent().parent().fadeOut().remove();});

All this is saying is that when .close is clicked, fade out it's great grandparent (You know...parent of a parent of a parent :) ) and then remove it from the DOM.

We'll use this in a little bit.


Validate

Thankfully, validating the forms is made super easy because of the great jQuery validation plugin that we downloaded earlier. We already designated the forms that we want to validate by adding the class of .validate. Now we just add one line to the jQuery:

$(".validate").validate();

Dialogue Boxes

Now we'll use another great feature of jQuery UI: the dialogue box.

An example of what jQuery UI's dialogue box can look like.

We'll use the dialogue box so that we can add, edit, and delete entries without leaving the page. Here is how we do it:

Delete Confirmation

 
$("a.delete").click(function(){ 
      $(".confirm").dialog('open'); 
      deleteLink = $(this).attr("href");  
      return false;	 
            
  }); 
  $('.confirm').dialog({ 
                  autoOpen: false, 
                  width: 400, 
                  height: 300, 
                  minWidth: 400, 
                  maxWidth: 900, 
                  minHeight: 300, 
                  maxHeight: 600, 
                  dialogClass: 'confirmdelete', 
                  buttons: { 
                      "Delete Forever": function() {  
                          if(deleteLink !== null){ 
                              $.post(deleteLink); 	 
                          } 
                          $(this).dialog("close"); 
                          deleteLink = ""; 
                          reloadWidget(); 
                      },  
                      "Cancel": function() {  
                          $(".confirm").dialog("close");  
                      }  
                  }, 
                  modal: true 
 
  });
  • First, we have to set up a click listener. This will wait for an element with the class of "delete" to be clicked.
  • Once one is clicked, it will open up the dialogue box.
  • We then take the link that the delete anchor would have pointed to and put it in a variable for later.
  • Next, we disable the link from going anywhere.
  • Next, the dialogue box is opened up.
  • We specifiy some self-explanatory values.
  • We also add a class to the dialogue box called "confirmdelete". This allows us to style the top bar of the box red, instead of yellow.
  • Next, we specify two buttons that the user can click, called "Delete Forever" and "Cancel".
  • If the delete forever button is clicked, we run the function that is inside.
    • First we check if there really is a link value to go to.
    • If there is, we point a POST request there. By doing this, we tell the controller that we can to delete this specific row in the database.
    • We then close the dialogue box.
    • We reset the deleteLink variable.
    • Then we reference a function that we'll be using later.
  • If cancel is clicked, we simple close the dialogue box.
  • The last line (modal:true) just tells UI to darken the background when the dialogue box is opened.

An example of what our jQuery UI's dialogue box will look like.

Delete--Confirm Multiple

 
$(".selectbox").change(function () { 
          checked = []; 
          var size = $(".selectbox:checked").size();  
          var i; 
          for (i=0;i<size;i++){ 
          	 
            checked[i] = $(".selectbox:checked:eq("+i+")").siblings('p').children("a.delete").attr("href");           
 
    	  }  
        }) 
        .trigger('change');

The above code will monitor for any changes to which items are checked. Every time something is checked or unchecked, it will retrigger this function. First, we declare a new array, called checked. This will store all data we need. Next, we figure out how many boxes are checked, and store that number in a variable called size. We then create a counting variable called i. Next, we start a for loop, which goes through until it has run as many times as there is checked checkboxes. This works because we set i at zero, but then every time we run through the loop we add 1 to i. We keep repeating this, until i is no longer smaller than the size variable. Every time the loop runs, we add an entry into the array with the corresponding anchor href for that specific check box's row of data.

 
$('.delete-selected-dialog').click(function(){					if(checked[0] == null){				alert("Please select at least one entry first. Thanks!");			}else{			dialogTitle = $(this).attr("title");			$(".confirmmultiple").dialog('open').dialog( 'option' , 'title' , dialogTitle );			return false;		}	}); 
$('.confirmmultiple').dialog({ 
                autoOpen: false, 
                width: 400, 
                height: 300, 
                minWidth: 400, 
                maxWidth: 900, 
                minHeight: 300, 
                maxHeight: 600, 
                dialogClass: 'confirmdelete', 
                buttons: { 
                    "Delete Forever": function() { 							 
                        if(checked[0] !== null){ 
                            $.each(checked, function() { 
                              $.post(this); 
                            }); 
                        } 
                         
                        $(this).dialog("close"); 
                        deleteLink = ""; 
                        reloadWidget(); 
                    },  
                    "Cancel": function() {  
                        $(".confirm").dialog("close");  
                    }  
                }, 
                modal: true 
 
});

This is very similar to the previous example. However this dialogue box will be used when we are deleting multiple entries at once. The only major difference (besides the click trigger) is the function that is called when the button "Delete Forever" is clicked.

  • First, we check if any of the check boxes are checked.
    • If not, we throw up an alert saying so
    • If we have stuff checked, we grab the title of the element clicked and put it in a variable
    • Then we open up the dialogue box with the title from the variable
    • We then do a return false, so that we don't actually visit the link.
  • Then we open up the dialogue box. This is very similar to the previous example. Again, we'll only cover the major difference: The "Delete Forever" button.
    • We check again to make sure that things are checked.
    • Then we use a special loop called an each loop. This will go through the list and post each link.

One More Dialogue Boxes

We need to create one more dialogue box. This will be for all instances where we are adding or editing members. We'll load in an iframe that loads the needed page (whether it be a new member or editing a pre-existing one). This is a much more robust solution than using AJAX to load it in. We're going to use one dialogue box for both cases, so we need it to be flexible as well:

 
$('.frameopen').click(function(){ 
        if($(this).attr("href") == "#"){ 
            alert("Please select an entry to edit first."); 
        }else{ 
        dialogTitle = $(this).attr("title"); 
        frameLink = $(this).attr("href"); 
        $(".dialogframe iframe").attr("src", frameLink); 
        $(".dialogframe").dialog('open').dialog( 'option' , 'title' , dialogTitle ); 
        return false; 
    } 
}); 
$('.dialogframe').dialog({ 
                autoOpen: false, 
                width: 800, 
                height: 550, 
                minWidth: 400, 
                maxWidth: 900, 
                minHeight: 300, 
                maxHeight: 600, 
                close: function() { reloadWidget(); }, 
                buttons: { 
                    "Save": function() {   
                        if($(".ui-dialog-title:contains('Add')").length > 0){save();}else if($(".ui-dialog-title:contains('Edit')").length > 0){edit();} 
                         
                        reloadWidget(); 
                    }, 
                    "Close": function() {  
                        $(this).dialog("close");  
                        reloadWidget(); 
                    } 
                }, 
                modal: true 
 
});
  • When a .frameopen link is clicked, it will:

    • Check that there is a link value
    • Grab the title of the link
    • Grab the link's href
    • Set the iframe src value for the dialogue box that will be opened.
    • Open the dialogue box that has an iframe in it, with the title of the dialogue box being the title from the link
    • Return False
  • In the "Save" button:
    • We check whether the dialogue title contains the word 'edit' or the word 'add'. That determines what function we perform.

Save() & Edit()

So far, we've been referencing two functions in particular, but haven't written them yet: save and edit. Let's do that now:

Save()

For the save function we need to accomplish the following:

  • Find the action URL for the form we are saving
  • Put all of the form data into a serialized array
  • Make sure everything is valid
  • Post the data
  • Display Notification

It should look something like this:

 
function save(){ 
		 
    var action = $("iframe").contents().find("form").attr("action"); 
    var fields = $("iframe").contents().find("input, select").serializeArray(); 
    var valid = $("iframe").contents().find(".validate").valid(); 
    if(valid == true){ 
         
        $.post(action, fields, function(data) { 
            $("iframe").contents().find('input').val(""); 
        }); 
        $("iframe").contents().find('.notification').slideUp().remove(); 
        $("iframe").contents().find('body').prepend(successA + "Member Successfully Edited" + successB); 
        reloadWidget(); 
    }else{ 
        $("iframe").contents().find('.notification').slideUp().remove(); 
        $("iframe").contents().find('body').prepend(errorA + "Oops! There appears to be some errors." + errorB); 
        js(); 
    } 
     
     
     
    return false; 
}

Edit()

For the edit function we need to accomplish the following (pretty similar):

  • Find the action URL for the form we are editing
  • Put all of the form data into a serialized array
  • Make sure everything is valid
  • Post the data
  • Display Notification

It should look something like this:

 
function edit(){ 
     
    var action = $("iframe").contents().find("form").attr("action"); 
    var fields = $("iframe").contents().find("input, select").serializeArray(); 
    var valid = $("iframe").contents().find(".validate").valid(); 
    if(valid == true){ 
         
        $.post(action, fields, function(data) { 
             
        }); 
        $("iframe").contents().find('.notification').slideUp().remove(); 
        $("iframe").contents().find('body').prepend(successA + "Member Successfully Edited" + successB); 
        reloadWidget(); 
    }else{ 
        $("iframe").contents().find('.notification').slideUp().remove(); 
        $("iframe").contents().find('body').prepend(errorA + "Please review your entry for errors. Thanks!" + errorB); 
        js(); 
    } 
     
     
     
    return false; 
}

Notice that we are using the notification variables.


Sidebar Toggle

For some of the sidebar items, they require extra content that is toggled on click. We'll do that with this simple code:

 
$(".side-nav li a, .expand").click(function(){ 
		$(this).siblings(".expand-content").toggle(); 
	});

Reload Widget

After we have changed the data in the database, we need to reload the widget on the page. That is why you saw the reloadWidget function in many of these segments of code. All we need is a simple function that will reload the widget whenever we need it to be.

 
function reloadWidget(link){ 
    $(".loading").fadeIn(); 
    $(".widget .widget-content").prepend('<div class="screen"></div>'); 
    $(".screen").fadeIn(); 
    $(".widget").load(location.href + "# .widget .widget-content",function(){ 
        $(".screen").fadeOut(); 
        js(); 
    });		 
    $(".loading").fadeOut(); 
}
  • First, we fade in the .loading element. This is just one of those gif loaders.
  • Next, we add a little code called ".screen" which will dim out the widget, while we reload it. This will be a second visual clue to the user that the current code is old.
  • Next, we load the widget and replace the current widget, with the new widget's content.
  • After, we fade the ".screen" and the ".loading" back into the invisible state.
  • We also run another function called js(). That will be explained next.

Js()

All of this code has worked nicely so far, except for one thing: it might work once, but after that it appears to stop working. This is a complicated issue with jQuery, but simple put: jQuery waits for the DOM (Document Object Model, basically everything on the page) to be loaded. Then it runs through the code, based on what was on the page. It doesn't recheck or rescan the DOM for changes. This means that any changes we have made to the DOM are unaffected by the jQuery. So for example, our new widget that we just reloaded might appear to be fine at first glance. But it will basically be like javascript is disabled on only that section of the page, since it is new content that wasn't there when the DOM was originally loaded.

So how do we fix this? Well there are two main ways in jQuery. We'll be covering the more basic way: which is sort of a way to rebind. We're going to rebind the data to the DOM. So, we wrap everything inside of the first jQuery DOM ready function inside another function, called js. This function can be recalled anywhere, anytime. So for instance, after we load in some new content, we just reload the js function, and the javascript will be essentially refreshed.

Since this function wraps everything, I'm going to wait to paste it until we have our final code (almost!).


Iframe & jQuery Issues

There's one other issue. jQuery has an issue traversing through iframes. That means that if you have a page that has an iframe in it, and you have a selector that selects all p elements, it will not select the ones in the iframe. This is an issue, because sometimes we need to traverse into the iframe, from the normal page. We can solve this, but we need to select elements in a different way. Long story short, we need to add several more segments of code, to cover traversing from inside and traversing from outside of the iframe:

Notification Close From Inside Iframe

This code will give us the same functionality as the above notification close segment, but will work for notifications inside an iframe too.

 
$("iframe").contents().find("span.close").click(function(){ 
    $(this).parent().parent().parent().fadeOut().remove(); 
}); 
<b> 
</b>

Button Save & Edit

This code will allow us to save and edit forms from inside the iframe as well as the dialogue save/edit button.

 
$("button.save").click(function(){ 
    var action = $(this).parent("form").attr("action"); 
    var fields = $("input, select").serializeArray(); 
    var valid = $(".validate").valid(); 
    if(valid == true){ 
        $.post(action, fields, function(data) { 
            $('input').val(""); 
        }); 
        $('.notification').slideUp().remove(); 
        $('body').prepend(successA + "Member Successfully Added" + successB); 
        reloadWidget(); 
        js(); 
    }else{ 
        $('.notification').slideUp().remove(); 
        $('body').prepend(errorA + "Please review your entry for errors. Thanks!" + errorB); 
        js(); 
    } 
     
     
    return false; 
}); 
$("button.edit").click(function(){ 
    var action = $(this).parent("form").attr("action");  
    var fields = $("input, select").serializeArray();  
    var valid = $(".validate").valid();  
    if(valid == true){ 
        $.post(action, fields, function(data) { 
             
        }); 
        $('.notification').slideUp().remove(); 
        $('body').prepend(successA + "Member Successfully Edited" + successB); 
        reloadWidget(); 
        js(); 
    }else{ 
        $('.notification').slideUp().remove(); 
        $('body').prepend(errorA + "Please review your entry for errors. Thanks!" + errorB); 
        js(); 
    } 
     
    return false; 
});

The Final jQuery File

It's been a long jQuery journey, but we have finally made it. Here is our final 300 + line jQuery file:

 
$(document).ready(function(){ 
	 
js();	 
	 
function js(){ 
	successA = '<div class="ui-widget notification"><div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;"><p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>'; 
	successB = '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>'; 
	 
	errorA = '<div class="ui-widget notification"><div class="ui-state-error ui-corner-all" style="padding: 0 .7em;"><p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>'; 
	errorB = '<span class="ui-icon close ui-icon-closethick" unselectable="on" style="-moz-user-select: none;">close</span></p></div></div>'; 
	 
 
	 
	$(".roster-groups li").click(function(){ 
		$(".roster-groups li span").removeClass("active"); 
		group = $(this).attr("class"); 
		group = ".member-column ul." + group; 
		$(".member-column ul").hide(); 
		$(this).children("span").addClass("active"); 
		$(group).fadeIn("slow"); 
	}); 
	$("ul.group-list li").click(function(){ 
		$("ul.group-list li").removeClass("active"); 
		contact = $(this).attr("class"); 
		contact = "li." + contact; 
		$(".contact-info ul li").hide(); 
		$(this).addClass("active"); 
		editLink = $(this).children("p").children("a.edit").attr("href"); 
		$(".edit-member-dialog").attr("href",editLink).addClass("alive"); 
		$(contact).fadeIn("slow"); 
	}); 
	$(".member-column li").hover(function(){ 
		$(".member-column li p a").hide(); 
		$(this).children('p').children('a').show(); 
	},function(){ 
		$(this).children('p').children('a').hide(); 
	}); 
	 
	 
	 
	function reloadWidget(link){ 
		$(".loading").fadeIn(); 
		$(".widget .widget-content").prepend('<div class="screen"></div>'); 
		$(".screen").fadeIn(); 
		$(".widget").load(location.href + "# .widget .widget-content",function(){ 
			$(".screen").fadeOut(); 
			js(); 
		});		 
		$(".loading").fadeOut(); 
	} 
	$("a.delete").click(function(){ 
		$(".confirm").dialog('open'); 
		deleteLink = $(this).attr("href");  
		return false;	 
		  	  
	}); 
	$('.confirm').dialog({ 
					autoOpen: false, 
					width: 400, 
					height: 300, 
					minWidth: 400, 
					maxWidth: 900, 
					minHeight: 300, 
					maxHeight: 600, 
					dialogClass: 'confirmdelete', 
					buttons: { 
						"Delete Forever": function() {  
							if(deleteLink !== null){ 
								$.post(deleteLink); 	 
							} 
							$(this).dialog("close"); 
							deleteLink = ""; 
							reloadWidget(); 
						},  
						"Cancel": function() {  
							$(".confirm").dialog("close");  
						}  
					}, 
					modal: true 
 
	}); 
	$('.confirmmultiple').dialog({ 
					autoOpen: false, 
					width: 400, 
					height: 300, 
					minWidth: 400, 
					maxWidth: 900, 
					minHeight: 300, 
					maxHeight: 600, 
					dialogClass: 'confirmdelete', 
					buttons: { 
						"Delete Forever": function() { 							 
							if(checked[0] !== null){ 
								$.each(checked, function() { 
							      $.post(this); 
							    }); 
							} 
							 
							$(this).dialog("close"); 
							deleteLink = ""; 
							reloadWidget(); 
						},  
						"Cancel": function() {  
							$(".confirm").dialog("close");  
						}  
					}, 
					modal: true 
 
	}); 
	 
	 
	$(".side-nav li a, .expand").click(function(){ 
		$(this).siblings(".expand-content").toggle(); 
	}); 
	 
	$("iframe").contents().find("span.close").click(function(){ 
		$(this).parent().parent().parent().fadeOut().remove(); 
	}); 
	$("span.close").click(function(){ 
		$(this).parent().parent().parent().fadeOut().remove(); 
	}); 
	 
	$('.frameopen').click(function(){ 
			if($(this).attr("href") == "#"){ 
				alert("Please select an entry to edit first."); 
			}else{ 
			dialogTitle = $(this).attr("title"); 
			frameLink = $(this).attr("href"); 
			$(".dialogframe iframe").attr("src", frameLink); 
			$(".dialogframe").dialog('open').dialog( 'option' , 'title' , dialogTitle ); 
			return false; 
		} 
	}); 
	 
	//Maintain which are selected in Array 
	 
	$(".selectbox").change(function () { 
			//something = $(this+':selected').serializeArray(); 
          checked = []; 
          var size = $(".selectbox:checked").size();  
          var i; 
          for (i=0;i<size;i++){ 
          	 
            checked[i] = $(".selectbox:checked:eq("+i+")").siblings('p').children("a.delete").attr("href");           
 
    	  }  
        }) 
        .trigger('change'); 
 
	 
	$('.delete-selected-dialog').click(function(){		 
			if(checked[0] == null){ 
				alert("Please select at least one entry first. Thanks!"); 
			}else{ 
			dialogTitle = $(this).attr("title"); 
			$(".confirmmultiple").dialog('open').dialog( 'option' , 'title' , dialogTitle ); 
			return false; 
		} 
	}); 
	 
	 
		 
	function save(){ 
		 
		var action = $("iframe").contents().find("form").attr("action"); 
		var fields = $("iframe").contents().find("input, select").serializeArray(); 
		var valid = $("iframe").contents().find(".validate").valid(); 
		if(valid == true){ 
			 
			$.post(action, fields, function(data) { 
				$("iframe").contents().find('input').val(""); 
			}); 
			$("iframe").contents().find('.notification').slideUp().remove(); 
			$("iframe").contents().find('body').prepend(successA + "Member Successfully Edited" + successB); 
			reloadWidget(); 
		}else{ 
			$("iframe").contents().find('.notification').slideUp().remove(); 
			$("iframe").contents().find('body').prepend(errorA + "Oops! There appears to be some errors." + errorB); 
			js(); 
		} 
		 
         
         
		return false; 
	} 
	function edit(){ 
		 
		var action = $("iframe").contents().find("form").attr("action"); 
		var fields = $("iframe").contents().find("input, select").serializeArray(); 
		var valid = $("iframe").contents().find(".validate").valid(); 
		if(valid == true){ 
			 
			$.post(action, fields, function(data) { 
				 
			}); 
			$("iframe").contents().find('.notification').slideUp().remove(); 
			$("iframe").contents().find('body').prepend(successA + "Member Successfully Edited" + successB); 
			reloadWidget(); 
		}else{ 
			$("iframe").contents().find('.notification').slideUp().remove(); 
			$("iframe").contents().find('body').prepend(errorA + "Please review your entry for errors. Thanks!" + errorB); 
			js(); 
		} 
		 
         
         
		return false; 
	} 
	 
	$(".validate").validate(); 
	 
	$("button.save").click(function(){ 
		var action = $(this).parent("form").attr("action"); 
		var fields = $("input, select").serializeArray(); 
		var valid = $(".validate").valid(); 
		if(valid == true){ 
			$.post(action, fields, function(data) { 
				$('input').val(""); 
			}); 
			$('.notification').slideUp().remove(); 
	   		$('body').prepend(successA + "Member Successfully Added" + successB); 
	   		reloadWidget(); 
	   		js(); 
		}else{ 
			$('.notification').slideUp().remove(); 
			$('body').prepend(errorA + "Please review your entry for errors. Thanks!" + errorB); 
			js(); 
		} 
	     
	     
		return false; 
	}); 
	$("button.edit").click(function(){ 
		var action = $(this).parent("form").attr("action");  
		var fields = $("input, select").serializeArray();  
		var valid = $(".validate").valid();  
		if(valid == true){ 
			$.post(action, fields, function(data) { 
				 
			}); 
			$('.notification').slideUp().remove(); 
	   		$('body').prepend(successA + "Member Successfully Edited" + successB); 
	   		reloadWidget(); 
	   		js(); 
		}else{ 
			$('.notification').slideUp().remove(); 
			$('body').prepend(errorA + "Please review your entry for errors. Thanks!" + errorB); 
			js(); 
		} 
	     
		return false; 
	}); 
	$('.dialogframe').dialog({ 
					autoOpen: false, 
					width: 800, 
					height: 550, 
					minWidth: 400, 
					maxWidth: 900, 
					minHeight: 300, 
					maxHeight: 600, 
					close: function() { reloadWidget(); }, 
					buttons: { 
						"Save": function() {   
							if($(".ui-dialog-title:contains('Add')").length > 0){save();}else if($(".ui-dialog-title:contains('Edit')").length > 0){edit();} 
							 
							reloadWidget(); 
						}, 
						"Close": function() {  
							$(this).dialog("close");  
							reloadWidget(); 
						} 
					}, 
					modal: true 
 
	}); 
	 
	 
	$('.dialog').dialog({ 
					autoOpen: false, 
					width: 600, 
					height: 400, 
					minWidth: 400, 
					maxWidth: 900, 
					minHeight: 300, 
					maxHeight: 600, 
					buttons: { 
						"Ok": function() {  
							$(this).dialog("close");  
							reloadWidget(); 
						},  
						"Cancel": function() {  
							$(this).dialog("close");  
						}  
					}, 
					modal: true 
 
	}); 
	// Dialog Link 
		$('.dialog-link').click(function(){ 
			var dialogID = $(this).attr("id"); 
			dialogID = "." + dialogID; 
			$(dialogID).dialog('open'); 
			return false; 
		}); 
 
		// Datepicker 
		$(".datepicker").datepicker({showOn: 'button', buttonImage: 'http://lbg.test/suite/application/css/images/calendar.png', buttonImageOnly: true}); 
		 
		 
	$('#dialog_link, .ui-state-default').hover( 
		function() { $(this).addClass('ui-state-hover'); },  
		function() { $(this).removeClass('ui-state-hover'); } 
	); 
				 
} 
 
	 
});

That's a Wrap

Wow...you made it (and so did I!). We covered a ton of concepts and new material, and created a realistic application using the power of the codeigniter and jQuery framework. I hope you learned a lot and enjoyed the tutorial!

Related Posts
  • Web Design
    HTML/CSS
    Build a Top Bar Off-Canvas Navigation With Foundation 5Foundation menu
    Today, we are going to combine ZURB's Foundation 5 Off-Canvas feature with our top bar navigation. The result will be a nice custom navigation for desktop users and a sleek off -canvas menu for tablet and mobile users. Read More…
  • Web Design
    Case Studies
    How They Did It: Typekit's New HomepageTypekit retina
    Typekit recently redesigned their homepage with some new services in mind. When Typekit joined Adobe, they set out to bring us a new way to handle fonts on the web. Not only did they create a fairly simple way to embed fonts on the web, but they have now officially launched a desktop sync option, which allows Creative Cloud subscribers to sync fonts to their computer directly from Typekit. This has been in a beta form for a while now, and provides a much easier route to local fonts than finding them elsewhere!Read More…
  • Web Design
    UX
    Walk Users Through Your Website With Bootstrap TourTour retina
    When you have a web application which requires some getting used to from your users, a walkthrough of the interface is in order. Creating a walkthrough directly on top of the interface makes things very clear, so that's what we're going to build, using Bootstrap Tour.Read More…
  • Code
    PHP
    Creating a Photo Tag Wall With Twilio Picture Messaging & PHPProcedural to oop php retina preview
    Twilio's recently announced Picture Messaging has vastly opened up what we can do with text messaging, now we can attach photos to our text messages and have them get used in different ways. In our case, we are going to build a Photo Tag Wall, which will contain photos linked to tags that will be displayed on a website.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme From Static HTML: Creating Template FilesCreating wordpress theme from html 400
    In the first part of this series, I showed you how to prepare your HTML and CSS files for WordPress, ensuring the structure would work, the code was valid and that the correct classes were being used. In this tutorial you'll learn how to take your index.html file and split it up into a set of template files for use by WordPress.Read More…
  • Code
    Theme Development
    How to Integrate Bootstrap Navbar Into WordPress ThemeBootstrapd 400
    Have you ever wanted to speed-up the process of theme development? I assume the answer is "yes" and you already know about Bootstrap and use it in mock-ups for development. This raises the question: "How can you integrate Bootstrap components into a WordPress theme?"Read More…