pre.code
Any good Content Management System (CMS) will have five key functionalities.
The ability to:
In this training module, we will implement these functionalities.
First, let’s create content that is needed for our application by creating the HTML framework and then adding the static view components.
Let’s first create a blank framework and tweak it for mobile devices if necessary.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>e-Directory</title>
</head>
<body>
</body>
</html>
Now that we have the basic HTML framework established, let’s add some static view components to it.
<body>
<div>
<h1>EMPLOYEE DIRECTORY<button>☀ ADD EMPLOYEE</button></h1>
<table border="1">
<tr>
<th width="20">#</th>
<th width="30"> </th>
<th width="185">FULL NAME</th>
<th width="135">DEPARTMENT</th>
<th width="170">EDIT / DELETE</th>
</tr>
<tr>
<td>{{$index+1}}</td>
<td><img alt="" height="40" width="40"
src="images/{{employee.image}}"></td>
<td>{{employee.fullName}}</td>
<td>{{employee.department | uppercase}}</td>
<td>
<button>✎ EDIT</button>
<button>☒ DELETE</button>
</td>
</tr>
</table>
</div>
</body>
CODE EXPLANATION:
- This is standard HTML code with some placeholders ({{...}}) for dynamic content that will be used later.
- The pipe symbol and the word uppercase ( | uppercase) is an AngualarJS filter that will force all
characters to uppercase even if they are written in lower or mixed case (e.g., qa becomes QA).
@charset "utf-8"; body {
font-family: Arial;
}
/* Panel styles -------------------------------------------*/
.panel {
width: 600px;
background-color: antiquewhite;
margin: 10px;
padding: 20px;
-webkit-box-shadow: 5px 5px 5px gray;
box-shadow: 5px 5px 5px gray;
border-radius: 10px;
}
.panel h1 {margin-top: 5px;}
/* Table styles ---------------------------------*/
table, th, td {
border: 1px solid gray;
border-collapse: collapse;
padding: 0px 5px;
font-family: Arial;
font-weight: bold;
}
table th {background-color: black;color: white;}
table tr: nth-child(odd){background-color: lightblue;}
table tr: nth-child(even){background-color: white;} /* Thumbnail image ----------------------------- */ .thumbnail_image{float: left; margin-right: 20px;
box-shadow: 5px 5px 5px gray; border: 1px solid black;
height: 125px; width: 125px;
} /* Other styles -------------------------------*/
h1 button {float: right;}
.row_image {padding: 0px;}
img{padding: 0; margin: 0; border: 0;} form h2 {border: 1px solid black;
display: inline;
padding: 4px;
border-radius: 10px;
background-color: white;
}
form {
width: 555px;
height: 200px;
background-color:lightblue;
padding:20px;
}
<title>e-Directory</title>
<link href="e-Directory.css" rel="stylesheet" type="text/css">
<body>
<div class="panel">
<h1>EMPLOYEE DIRECTORY<button>☀ ADD EMPLOYEE</button></h1>
<table border="1">
<tr>
<th width="20">#</th>
<th width="30"> </th>
<th width="185">FULL NAME</th>
<th width="135">DEPARTMENT</th>
<th width="170">EDIT / DELETE</th>
</tr>
<tr>
<td style="text-align:center">{{$index+1}}</td>
<td class="row_image"><img alt="" height="40" width="40"
src="images/{{employee.image}}"></td>
<td>{{employee.fullName}}</td>
<td>{{employee.department | uppercase}}</td>
<td>
<button>✎ EDIT</button>
<button>☒ DELETE</button>
</td>
</tr>
</table>
</div>
</body>
Now that we have the framework and the view components in place, let's add two JavaScript files - one local and one remote.
Add the following two <script> tags above the closing </body> tag:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="e-Directory.js"></script> </body>
Now that we have connected the JavaScript files that we need, let’s add some dynamic content.
We will start first by:
// JavaScript Document var app=angular.module("eDirectoryApp", []);
</head>
<body data-ng-app="eDirectoryApp">
CODE EXPLANATION:
- The data-ng- prefix is used instead of the ng- prefix to make the application compatible to HTML5.
It will also be used throughout the rest of this app.
- We have just associated the <body> tag with the AngularJS framework making the <body> tag and all of
its content an application. How easy was that!!!
Now that we have created an app, let’s associate it with a controller to “control” our app. We want to add the following functionality to our controller:
If a user click:
Write the following highlighted code below the app variable
WHY: To assign a controller to the app and create a user array to the current $scope.
var app = angular.module("eDirectoryApp", []);
app.controller("eDirectoryCtrl", function ($scope) {
"use strict";
$scope.employeesJSON = []; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
CODE EXPLANATION:
- The $scope.employeesJSON is an array that will be populated with default users in the next few steps.
- The "use strict" directive is used to define that JavaScript code should be executed in "strict mode."
For example, you cannot use undeclared variables.
Populate the blank array with some default data.
Add the following highlighted data in between the square braces ([...]) of the employee array.var app = angular.module("eDirectoryApp", []);
app.controller("eDirectoryCtrl", function ($scope) {
"use strict";
$scope.employeesJSON = [{
id: 0,
fullName: "Ann Ricoh",
department: "HR",
image: "ann_ricoh.jpg"
}, {
id: 1,
fullName: "Bob Anderson",
department: "IT",
image: "bob_anderson.jpg"
}, {
id: 2,
fullName: "Carol Green",
department: "QA",
image: "carol_green.jpg"
}, {
id: 3,
fullName: "Debra Samson",
department: "Admin",
image: "debra_samson.jpg"
}]; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
Now that we have associated the controller with the app within the SCRIPT code, we now need to associate the SCRIPT code within the HTML code.
<body data-ng-app="eDirectoryApp" data-ng-controller="eDirectoryCtrl">
<table border="1">
<tr>
<th width="20">#</th>
<th width="30"> </th>
<th width="185">FULL NAME</th>
<th width="135">DEPARTMENT</th>
<th width="170">EDIT / DELETE</th>
</tr>
<tr data-ng-repeat="employee in employeesJSON">
<td style="text-align:center">{{$index+1}}</td>
<td class="row_image"><img alt="" height="40" width="40"
src="images/{{employee.image}}"></td>
<td>{{employee.fullName}}</td>
<td>{{employee.department | uppercase}}</td>
<td>
<button>✎ EDIT</button>
<button>☒ DELETE</button>
</td>
</tr>
</table>
A form will be created inside of the <div class="panel> tag.
<h1>EMPLOYEE DIRECTORY<button>☀ ADD EMPLOYEE</button></h1>
<form name="addEditForm" style="padding-bottom:20px">
<h2>ADD EMPLOYEE</h2>
<h2>EDIT EMPLOYEE</h2>
<div>
<img alt="" class="thumbnail_image" src="images/{{image}}">
<p>
<label for="full_name">Name:</label><br/>
<input name="full_name" id="full_name" type="text" placeholder="Full Name">
</p>
<p>
<label for="department">Department:</label><br/>
<input name="department" id="department" type="text" placeholder="Employee Dept.">
</p>
</div>
<button>✔ SAVE</button>
<button>x CANCEL</button>
</form>
<table border="1">
First, we want to write some code to hide the form when the application first loads. Then, we will write code for the ADD EMPLOYEE and EDIT buttons.
$scope.employeesJSON = [{
id: 0,
fullName: "Ann Ricoh",
department: "HR",
image: "ann_ricoh.jpg"
}, {
id: 1,
fullName: "Bob Anderson",
department: "IT",
image: "bob_anderson.jpg"
}, {
id: 2,
fullName: "Carol Green",
department: "QA",
image: "carol_green.jpg"
}, {
id: 3,
fullName: "Debra Samson",
department: "Admin",
image: "debra_samson.jpg"
}];
// Initialize variables used in application --------------
$scope.hideForm=true; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
<form name="addEditForm" style="padding-bottom:20px" data-ng-hide="hideForm">
...
</form>
Now that we can view all of the current employees, let’s add functionality to add new employees. We will first start by adding code to "see" the form again.
<div class="panel">
<h1>EMPLOYEE DIRECTORY<button data-ng-click="addEmployee()">ADD EMPLOYEE</button></h1>
<form name="addEditForm" style="padding-bottom:20px" data-ng-hide="hideForm">
CODE EXPLANATION:
- The data-ng-click directive is used to "listen" for a click event and when one happen call (or invoke)
the addEmployee() function
$scope.hideForm=true;
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function () {
alert("ADD EMPLOYEE");
};
CODE EXPLANATION:
- It is common practice for developer to create a "stub" code with some pseudo data (e.g., an alert
statement) to check to see if the code block was written correctly. This technique is particularly
useful for beginner developers.
- When a function is used for an event handler, it will be a no-name function (also called an anonymous
function) and is assigned to a variable (e.g., $scope.addEmployee = function () {code goes here...}; ).
A normal function will have a NAME used with the key word function (e.g., function addEmployee()
{code goes here...};) and MAY OR MAY NOT be assigned to a variable.
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function () {
// alert("ADD EMPLOYEE"); $scope.hideForm = false;
};
<form name="addEditForm" style="padding-bottom:20px" data-ng-hide="hideForm">
<h2 data-ng-show="hideTitle">ADD EMPLOYEE</h2>
<h2 data-ng-hide="hideTitle">EDIT EMPLOYEE</h2>
CODE EXPLANATION:
- Notice that there are two directives a data-ng-show and a data-ng-hide and they are BOTH set to
hideTitle which will be set to true in the next step. Since both are set to true and one uses
data-show-ng and the other uses data-hide-ng, one will be turned on and the other turned off.
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function () {
// alert("ADD EMPLOYEE");
$scope.hideForm = false;
$scope.hideTitle = true;
};
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function () {
// alert("ADD EMPLOYEE");
$scope.hideForm = false;
$scope.hideTitle = true;
$scope.fullName = "";
$scope.department = "";
$scope.image = "photo_not_available.png";
}; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
Now that we have added the Add Employee form and functionality, let's see how we can cancel this operation if we change our mind.
<button data-ng-click="closeForm()">x CANCEL</button>
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function () {
// alert("ADD EMPLOYEE");
$scope.hideForm = false;
$scope.hideTitle = true;
$scope.fullName = "";
$scope.department = "";
$scope.image = "photo_not_available.png";
};
// CANCEL FUNCTIONALITY ---------------------------------
$scope.closeForm = function () {
$scope.hideForm = true; }; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
While we have created the add functionality, we have not yet added code to:
<button data-ng-click="saveEmployee()">✔ SAVE</button>
<button data-ng-click="closeForm()">x CANCEL</button>
// CANCEL FUNCTIONALITY ----------------------------------
$scope.closeForm = function () {
$scope.hideForm = true;
};
// SAVE EMPLOYEE FUNCTIONILITY -------------------------
$scope.saveEmployee = function () {
alert("SAVE EMPLOYEE");
}; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
// SAVE EMPLOYEE FUNCTIONILITY -------------------------
$scope.saveEmployee = function () {
// alert("SAVE EMPLOYEE"); $scope.totalNumberEmployees = $scope.employeesJSON.length;
alert("Total number of employees is: " + $scope.totalNumberEmployees);
};
/* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/ }); // End of Controller
CODE EXPLANATION:
- The length property of the employeesJSON object is used to ascertain how many objects are in
the employee's array.
// SAVE EMPLOYEE FUNCTIONILITY -------------------------
$scope.saveEmployee = function () {
// alert("SAVE EMPLOYEE"); $scope.totalNumberEmployees = $scope.employeesJSON.length;
// alert("Total number of employees is: " + $scope.totalNumberEmployees);
$scope.employeesJSON.push({
"id": $scope.totalNumberEmployees,
"fullName": $scope.fullName,
"department": $scope.department,
"image": "photo_not_available.png"
});
};
/* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}; // End of Controller
CODE EXPLANATION:
- The push() method of the employeesJSON object is used to add a new employee to the array
- The $scope.totalNumberEmployee is used to increment the id value in the object by 1
- The fullName, department, and image are properties that will be assigned values from the form elements
in the next step.
<form name="addEditForm" style="padding-bottom:20px">
<h3>ADD EMPLOYEE</h3>
<h3>EDIT EMPLOYEE</h3>
<div>
<img data-ng-model="image" alt="" class="thumbnail_image" src="e-DirectoryEmployees/{{image}}">
<p>
<label>Name:</label><br/>
<input data-ng-model="fullName" name="full_name" id="full_name" type="text" placeholder="Full Name">
</p>
<p>
<label>Title:</label><br/>
<input data-ng-model="department" name="department" id="department" type="text" placeholder="Employee Title">
</p>
</div>
<button data-ng-click="saveEmployee()">✔ SAVE</button>
<button data-ng-click="closeForm()">x CANCEL</button> </form>
Now that we can view and edit employees, let's see how we can edit an existing employee data and save it to the array. While we could create a separate code block for the edit functionality, since the edit and add functionality is similar, we will create a conditional statement inside of the addEmployee function to check for which code to execute.
<button data-ng-click="addEmployee(employee.id)">✎ EDIT</button>
CODE EXPLANATION:
- Notice the employee.id argument that is being passed in the addEmployee function. It will resolve
to the current id that will be used in the addEmployee function.
- The employee.id will be used to pass an argument to the addEmployee function.
<h1>EMPLOYEE DIRECTORY<button data-ng-click="addEmployee('new')">ADD EMPLOYEE</button></h1>
CODE EXPLANATION:
- The string "new" is added to the addEmployee() function so that it can be used in the addEmployee
function in the controller to distinguish it from the edit code.
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function (id) { if(id==="new") {
alert("ADD EMPLOYEE");
$scope.hideForm = false;
$scope.hideTitle = true;
$scope.fullName = "";
$scope.title = "";
$scope.image = "photo_not_available.png";
}
else {
// Edit Employee ------------
alert("EDIT EMPLOYEE");
}
};
CODE EXPLANATION:
- Notice the "id" argument that was added to the addEmployee function. It is used to determine if
the if or the else statement is executed in the conditional statement. - The triple equal sign (===) represents strict equality between two values. Both the type and
the value you're comparing have to be exactly the same.
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function (id) { if(id==="new") {
alert("ADD EMPLOYEE");
$scope.hideForm = false;
$scope.hideTitle = true;
$scope.fullName = "";
$scope.title = "";
$scope.image = "photo_not_available.png";
}
else {
// Edit Employee ------------
alert("EDIT EMPLOYEE");
$scope.hideTitle = false;
$scope.hideForm = false;
$scope.fullName = $scope.employeesJSON[id].fullName;
$scope.department = $scope.employeesJSON[id].department;
$scope.image = $scope.employeesJSON[id].image;
}
};
CODE EXPLANATION:
- The $scope.hideTitle = false; statement is used to show the EDIT EMPLOYEE title instead of the ADD
EMPLOYEE title.
- The $scope.hideForm = false; statement is used to show the form so that the fields can be edited.
- The three variables (fullName, department, and image) will assigned values from the employeesJSON
object based on a specific ID value.
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function (id) { if(id==="new") {
// alert("ADD EMPLOYEE");
$scope.hideForm = false;
$scope.hideTitle = true;
$scope.fullName = "";
$scope.department = "";
$scope.image = "photo_not_available.png";
}
else {
// Edit Employee ------------
// alert("EDIT EMPLOYEE");
$scope.hideTitle = false;
$scope.hideForm = false;
$scope.fullName = $scope.employeesJSON[id].fullName;
$scope.department = $scope.employeesJSON[id].department;
$scope.image = $scope.employeesJSON[id].image;
}
};
Since the ADD EMPLOYEE button SAVE functionality is similar to the EDIT button functionality, we will add an "if/else" conditional statement like we did in the the addEmployee function code block:
// SAVE EMPLOYEE FUNCTIONILITY -------------------------
$scope.saveEmployee = function () {
if ($scope.addNewEmployee === true) {
// alert("SAVE EMPLOYEE FOR ADD EMPLOYEE BUTTON");
var totalNumberEmployees = $scope.employeesJSON.length;
// alert("Total number of employees is: " + totalNumberEmployees);
$scope.employeesJSON.push({
"id": $scope.totalNumberEmployees,
"fullName": $scope.fullName,
"department": $scope.department,
"image": "photo_not_available.png"
}); }
else {
alert("SAVE EMPLOYEE FOR EDIT BUTTON"); alert("Selected Employee Index for Save is: " + $scope.selectedEmployeeIndex);
$scope.employeesJSON[$scope.selectedEmployeeIndex].fullName = $scope.fullName;
$scope.employeesJSON[$scope.selectedEmployeeIndex].department = $scope.department;
}
}; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/ }); // End of Controller
// Initialize variables used in application --------------
$scope.hideForm=true;
$scope.addNewEmployee = false;
$scope.selectedEmployeeIndex = 0;
// CREATE ADD EMPLOYEE FUNCTIONALITY ---------------------
$scope.addEmployee = function (id) {
if(id==="new") {
// alert("ADD EMPLOYEE");
$scope.addNewEmployee = true;
$scope.hideForm = false;
$scope.hideTitle = true;
$scope.fullName = "";
$scope.department = "";
$scope.image = "photo_not_available.png";
}
else {
// Edit Employee ------------
// alert("EDIT EMPLOYEE");
$scope.addNewEmployee = false;
$scope.selectedEmployeeIndex = id;
alert("Selected Employee Index for Edit: " + $scope.selectedEmployeeIndex);
$scope.hideTitle = false;
$scope.hideForm = false;
$scope.fullName = $scope.employeesJSON[id].fullName;
$scope.department = $scope.employeesJSON[id].department;
$scope.image = $scope.employeesJSON[id].image;
}
};
// SAVE EMPLOYEE FUNCTIONILITY -------------------------
$scope.saveEmployee = function () {
if ($scope.addNewEmployee === true)
{
// alert("SAVE EMPLOYEE");
$scope.totalNumberEmployees = $scope.employeesJSON.length;
// alert("Total number of employees is: " + $scope.totalNumberEmployees);
$scope.employeesJSON.push({
"id": $scope.totalNumberEmployees,
"fullName": $scope.fullName,
"department": $scope.department,
"image": "photo_not_available.png"
});
}
else {
// alert("SAVE EMPLOYEE FOR EDIT BUTTON");
// alert("Selected Employee Index for Save is: " + $scope.selectedEmployeeIndex);
$scope.employeesJSON[$scope.selectedEmployeeIndex].fullName = $scope.fullName;
$scope.employeesJSON[$scope.selectedEmployeeIndex].department = $scope.department;
}
};
/* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
// SAVE EMPLOYEE FUNCTIONILITY -------------------------
$scope.saveEmployee = function () {
if ($scope.addNewEmployee === true)
{
// alert("SAVE EMPLOYEE");
$scope.totalNumberEmployees = $scope.employeesJSON.length;
// alert("Total number of employees is: " + $scope.totalNumberEmployees);
$scope.employeesJSON.push({
"id": $scope.totalNumberEmployees,
"fullName": $scope.fullName,
"department": $scope.department,
"image": "photo_not_available.png"
});
}
else {
// alert("SAVE EMPLOYEE FOR EDIT BUTTON");
// alert("Selected Employee Index for Save is: " + $scope.selectedEmployeeIndex);
$scope.employeesJSON[$scope.selectedEmployeeIndex].fullName = $scope.fullName;
$scope.employeesJSON[$scope.selectedEmployeeIndex].department = $scope.department;
}
$scope.hideForm = true; // Close form for both "EDIT" Save or the "ADD EMPLOYEE" Save
};
/* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
Now that we can view, add, and edit employee's data, let's now add functionality to delete an employee.
<button data-ng-click="addEmployee(employee.id)">✎ EDIT</button>
<button data-ng-click="deleteEmployee($index)">☒ DELETE</button
CODE EXPLANATION:
- Notice the $index argument that is being passed in the deleteEmployee function. It will resolve
to the current row index that will be used in the deleteEmployee function.
else {
// alert("SAVE EMPLOYEE FOR EDIT BUTTON");
// alert("Selected Employee Index for Save is: " + $scope.selectedEmployeeIndex);
$scope.employeesJSON[$scope.selectedEmployeeIndex].fullName = $scope.fullName;
$scope.employeesJSON[$scope.selectedEmployeeIndex].department = $scope.department;
}
// Close form for both "EDIT" Save or the "ADD EMPLOYEE" Save $scope.hideForm = true;
}; // DELETE EMPLOYEE FUNCTIONALITY -------------------------------
$scope.deleteEmployee = function () {
alert("DELETE EMPLOYEE");
};
/* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
// DELETE EMPLOYEE FUNCTIONALITY -------------------------------
$scope.deleteEmployee = function (x) {
alert("DELETE EMPLOYEE"); $scope.employeesJSON.splice(x, 1);
};
/* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
CODE EXPLANATION:
- Notice the "x" that is passed as an argument into the deleteEmployee function. It is used to
ascertain the index value ($index) from the HTML code.
- The splice() method of the employeesJSON object is used to delete one element from the array
based on the table row index (x) that was passed into it.
// DELETE EMPLOYEE FUNCTIONILITY ----------------------
$scope.deleteEmployee = function (x) {
// alert("Delete Employee");
$scope.message = "Are you sure you want to delete this employee?";
var choose = confirm($scope.message);
if (choose === true) {
alert("You pressed the OK button!");
$scope.employeesJSON.splice(x, 1);
} else {
alert("You pressed the Cancel button!");
}
};
CODE EXPLANATION:
- The confirm() method is assigned to the message variable so that it can be used in the "if"
statement to determine if the OK or the Cancel button has been pressed. If the OK button is
pressed, it will return true and the first part of the "if" statement will be executed.
If the Cancel button is pressed, it will return false and the second part of the "if" statement
(the else statement) will be executed.
- The message variable is created so that it can be passed into the confirm() method. The message
could have been written inside of the confirm dialog box but it is best practice to create a
variable for a long statement and then use that variable as an argument to make the code easier
to read and update.
You should see the "You pressed the Cancel button!" message in an ALERT dialog box. Press the OK button to dismiss it.
Click the Delete button again, but this time when the "Are you sure you want to delete this employee?" confirmation dialog box appear, click the OK button. You should see the "You pressed the OK button!" message displayed in an ALERT dialog box.
Then, you should see the row that you clicked that the DELETE button disappeared.
// DELETE EMPLOYEE FUNCTIONILITY ----------------------
$scope.deleteEmployee = function (x) {
// alert("Delete Employee");
$scope.message = "Are you sure you want to delete " + $scope.employeesJSON[x].fullName + "?";
var choose = confirm($scope.message);
if (choose === true) {
// alert("You pressed the OK button!");
$scope.employeesJSON.splice(x, 1);
} else {
// alert("You pressed the Cancel button!");
}
};
Currently, the data is not actually being saved when you update, edit, or delete content if you close the browser. There are two major ways to make the data persistent once you exit the application:
See Add Local Storage for more detail on Local Storage.
In order to save the data locally, we will need to create a local storage object. Then, when the page loads, check the local storage and load it or use the default data if no local storage exists.
$scope.saved = localStorage.getItem("employeeLS");
if ($scope.saved !== null) {
alert("Save data to local storage");
$scope.employeesJSON = JSON.parse($scope.saved);
}
else
{
alert("Use default data");
$scope.employeesJSON = [{
id: 0,
fullName: "Ann Ricoh",
department: "HR",
image: "ann_ricoh.jpg"
}, {
id: 1,
fullName: "Bob Anderson",
department: "IT",
image: "bob_anderson.jpg"
}, {
id: 2,
fullName: "Carol Green",
department: "QA",
image: "carol_green.jpg"
}, {
id: 3,
fullName: "Debra Samson",
department: "Admin",
image: "debra_samson.jpg"
}];
}
localStorage.setItem("employeeLS", JSON.stringify($scope.employeesJSON));
// Initialize variables used in application --------------
CODE EXPLANATION:
- The saved variable is used to get the saved data if there is any.
- If the saved variable is NOT NULL, the "if" portion of the conditional statement will be executed
which will result in data being saved to the Local Storage.
- If the saved variable is NULL, the "else" portion of the conditional statement will be executed
which will result in the default data being loaded from the array.
- The setItem() method is used to save the data to the Local Storage.
- The stringify() method of the JSON object is used to convert the employeesJSON array data that is
passed as an argument to a "string"
var app = angular.module("eDirectoryApp", []);
app.controller("eDirectoryCtrl", function ($scope) {
"use strict";
$scope.saved = localStorage.getItem('employeeLS');
if ($scope.saved !== null) {
// alert("Save data to local storage");
$scope.employeesJSON = JSON.parse($scope.saved);
}
else
{
// alert("Use default data");
$scope.employeesJSON = [{
// SAVE EMPLOYEE FUNCTIONALITY -------------------------
$scope.saveEmployee = function () {
if ($scope.addNewEmployee === true)
{
// alert("SAVE EMPLOYEE");
$scope.totalNumberEmployees = $scope.employeesJSON.length;
// alert("Total number of employees is: " + $scope.totalNumberEmployees);
$scope.employeesJSON.push({
"id": $scope.totalNumberEmployees,
"fullName": $scope.fullName,
"department": $scope.department,
"image": "photo_not_available.png"
});
localStorage.setItem("employeeLS", JSON.stringify($scope.employeesJSON));
}
else {
// alert("SAVE EMPLOYEE FOR EDIT BUTTON");
// alert("Selected Employee Index for Save is: " + $scope.selectedEmployeeIndex);
$scope.employeesJSON[$scope.selectedEmployeeIndex].fullName = $scope.fullName;
$scope.employeesJSON[$scope.selectedEmployeeIndex].department = $scope.department;
localStorage.setItem("employeeLS", JSON.stringify($scope.employeesJSON));
}
$scope.hideForm = true; // Close form for both "EDIT" Save or the "ADD EMPLOYEE" Save
};
// DELETE EMPLOYEE FUNCTIONALITY -------------------------------
$scope.deleteEmployee = function (x) {
// alert("Delete Employee");
$scope.message = "Are you sure you want to delete " + $scope.employeesJSON[x].fullName + "?";
var choose = confirm($scope.message);
if (choose === true) {
// alert("You pressed the OK button!");
$scope.employeesJSON.splice(x, 1);
localStorage.setItem("employeeLS", JSON.stringify($scope.employeesJSON));
}
else
{
// alert("You pressed the Cancel button!");
}
}; /* Leave this line blank and the line above so that you don't inadvertently
add code blocks below the controller code block.*/
}); // End of Controller
Extract from: https://docs.angularjs.org/error/ngRepeat/dupes
Error: ngRepeat:dupes
Duplicate key in a repeater are not allowed because AngularJS uses keys to associate DOM nodes with items. By default, collections are keyed by reference which is desirable for most common models but can be problematic for primitive types that are interned (share references).
SOLUTION: To resolve this error either ensure that the items in the collection have unique identity or use the track by syntax to specify how to track the association between models and DOM.
Use 'track by' expression (e.g., track by $index) to specify unique keys which will cause the items to be keyed by their position in the array instead of their value.
<table border="1">
<tr>
<th width="20">#</th>
<th width="30"> </th>
<th width="185">FULL NAME</th>
<th width="135">DEPARTMENT</th>
<th width="170">EDIT / DELETE</th>
</tr>
<tr data-ng-repeat="employee in employeesJSON track by $index">
<td style="text-align:center">{{$index+1}}</td>
<td class="row_image"><img alt="" height="40" width="40"
src="images/{{employee.image}}"></td>
<td>{{employee.fullName}}</td>
<td>{{employee.department | uppercase}}</td>
<td> <button data-ng-click="addEmployee(employee.id)">✎ EDIT</button>
<button data-ng-click="deleteEmployee($index)">☒ DELETE</button>
</td>
</tr>
</table>
While the app is basically complete, there are some things that we can do to enhance it both aesthetically and functionally.
These enhancements are optional and you can add them as you see fit:
DB VERSION:
In order to do AngularJS animation, we need to load the angular-animate JavaScript and then add it as a dependent later:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-animate.js"></script>
<script src="e-Directory.js"></script>
</body>
</html>
<form name="addEditForm" style="padding-bottom:20px" data-ng-hide="hideForm"
class="animate" data-ng-show="nav">
...
</form>
CODE EXPLANATION:
- The data-ng-show directive is used to show or hide the form.
- The animate class is used to create the animation.
/* Other styles -------------------------------*/
h1 button {float:right;}
.row_image {padding:0px;}
img{padding:0; margin:0; border:0;} form h2 {border: 1px solid black; display: inline;
padding:4px;
border-radius:10px;
background-color:white;
}
form {
width: 555px;
height: 200px;
background-color:lightblue;
padding:20px
}
/* Toggle Form ---------------------- */
.animate {
-webkit-transition: all linear 0.25s;
-moz-transition: all linear 0.25s;
transition: all linear 0.5s;
}
.animate.ng-hide {
opacity:0;
height:0px;
}
var app = angular.module("eDirectoryApp", ["ngAnimate"]);
Now, let's write some HTML, CSS and JavaScript to keep track of how many employees are listed in the table.
</table> <p><strong>Employees Total:</strong> {{employeesJSON.length}}</p>
Now that we can display the total number of employees in the table, let's add some code to display another message if ALL of the employees are deleted from the table.
</table>
<p><strong>Employees Total:</strong> {{totalNumberEmployees}}
<span style="color:red; font-weight: bold" data-ng-hide="hideMessage">
All employees have been deleted.</span></p>
// Initialize variables used in application --------------
$scope.hideForm=true;
$scope.addNewEmployee = false;
$scope.selectedEmployeeIndex = 0;
$scope.hideMessage = true;
// DELETE EMPLOYEE FUNCTIONILITY ----------------------
$scope.deleteEmployee = function (x) {
// alert("Delete Employee");
$scope.message = "Are you sure you want to delete " + $scope.employeesJSON[x].fullName + "?";
var choose = confirm($scope.message);
if (choose === true) {
//alert("You pressed the OK button!");
// Remove from EmployeesJSON object
$scope.employeesJSON.splice(x, 1);
localStorage.setItem('employeeLS', JSON.stringify($scope.employeesJSON));
$scope.totalNumberEmployees = $scope.employeesJSON.length;
if($scope.totalNumberEmployees === 0)
{
alert("All employees have been deleted.");
$scope.hideMessage = false;
} } else {
// alert("You pressed the Cancel button!");
} };
Another key feature of a CMS is the ability to be able to perform a search of records.
</form> <p><strong>Search:</strong>
<input type="text" data-ng-model="searchword" placeholder="Search by Name or Dept."></p>
<table border="1">
<tr data-ng-repeat="employee in employeesJSON | filter:searchword track by $index ">
Currently, we are using regular character (e.g., x) or entities (e.g., ✎ for the edit icon) for the button icons. Here, we will enhance them with fonts from Font Awesome.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>e-Directory</title>
<link href="e-DirectoryCSS.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<h1>EMPLOYEE DIRECTORY<button data-ng-click="addEmployee('new')">
<span class="fa fa-user"></span> ADD EMPLOYEE</button></h1>
<form name="addEditForm" style="padding-bottom:20px" data-ng-submit="validateForm()" data-ng-hide="hideForm"
class="animate" data-ng-show="nav">
<h2 data-ng-show="hideTitle">ADD EMPLOYEE</h2>
<h2 data-ng-hide="hideTitle">EDIT EMPLOYEE</h2>
<div>
<img data-ng-model="image" alt="" class="thumbnail_image" src="e-DirectoryEmployees/{{image}}">
<p>
<label for="full_name">Name:</label><br/>
<input data-ng-model="fullName" name="full_name" id="full_name" type="text" placeholder="Full Name">
</p>
<p>
<label for="department">Department:</label><br/>
<input data-ng-model="department" name="department" id="department" type="text" placeholder="Employee Dept.">
</p>
</div>
<button data-ng-click="saveEmployee()"><span class="fa fa-save"></span> SAVE</button>
<button data-ng-click="closeForm()"><span class="fa fa-undo"></span> CANCEL</button>
</form>
<table border="1">
<tr>
<th width="20">#</th>
<th width="30"> </th>
<th width="185">FULL NAME</th>
<th width="135">DEPARTMENT</th>
<th width="170">EDIT / DELETE</th>
</tr>
<tr data-ng-repeat="employee in employeesJSON | filter:searchword track by $index ">
<td style="text-align:center">{{$index+1}}</td>
<td class="row_image"><img alt="" height="40" width="40" src="images/{{employee.image}}"></td>
<td>{{employee.fullName}}</td>
<td>{{employee.department | uppercase}}</td>
<td>
<button data-ng-click="addEmployee(employee.id)"><span class="fa fa-edit"></span> EDIT</button>
<button data-ng-click="deleteEmployee($index)"><span class="fa fa-remove"></span> DELETE</button>
</td>
</tr>
</table>
/* Toggle Form ---------------------- */
.animate {
-webkit-transition: all linear 0.25s;
-moz-transition: all linear 0.25s;
transition: all linear 0.5s;
}
.animate.ng-hide {
opacity:0;
height:0px;
}
form {
width: 555px;
height: 200px;
background-color:lightblue;
padding:20px;
}
/* Font Awesome ----------------------- */
.fa{color:red;}
When dealing with form elements, it is important to validate form elements to ensure that that are filled in (required) and the correct information is entered.
<form data-ng-submit="validateForm()" name="addEditForm" style="padding-bottom:20px" data-ng-hide="hideForm"
class="animate" data-ng-show="nav">
CODE EXPLANATION:
- The data-ng-submit="validateForm()" directive is used to submit the form when the SAVE button is pressed.
<img data-ng-model="image" alt="" class="thumbnail_image" src="e-DirectoryEmployees/{{image}}">
<p>
<label for="full_name">Name:</label><br/>
<input data-ng-model="fullName" name="full_name" id="full_name" type="text" placeholder="Full Name" required>
<span data-ng-show="addEditForm.full_name.$touched && addEditForm.full_name.$invalid" style="color:red">
Full name is required.</span>
</p>
<p>
<label for="department">Department:</label><br/>
<input data-ng-model="department" name="department" id="department" type="text" placeholder="Employee Dept."
required>
<span data-ng-show="addEditForm.department.$touched && addEditForm.department.$invalid" style="color:red">
Department name is required.</span>
</p>
<!-- <button data-ng-click="saveEmployee()"><span class="fa fa-save"></span> SAVE</button>-->
<input type="submit" value="SAVE" />
CODE EXPLANATION:
- Notice that we replaced the button with an input field which is a self-closing tag so it does not have
an icon associated with it.
// DELETE EMPLOYEE FUNCTIONILITY ----------------------
$scope.deleteEmployee = function (x) {
// alert("Delete Employee");
$scope.message = "Are you sure you want to delete " + $scope.employeesJSON[x].fullName + "?";
var choose = confirm($scope.message);
if (choose === true) {
//alert("You pressed the OK button!");
// Remove from EmployeesJSON object
$scope.employeesJSON.splice(x, 1);
localStorage.setItem('employeeLS', JSON.stringify($scope.employeesJSON));
$scope.totalNumberEmployees = $scope.employeesJSON.length;
if($scope.totalNumberEmployees === 0){
alert("All employees have been deleted.");
$scope.hideMessage = false;
} } else {
// alert("You pressed the Cancel button!");
}
};
// Validate form ------------------------------------
$scope.validateForm = function(){
alert("You have submitted the form");
$scope.saveEmployee();
}; // Leave this line blank so that you don't inadvertantly add code OUTSIDE of the cotroller code block.
}); // end of app.controller function
// Validate form ------------------------------------Click the SAVE button. You should see the tool-tip "Please fill out this field." Also, note that the data has not been submitted because the two fields were nfilled out.
$scope.validateForm = function(){
// alert("You have submitted the form");
$scope.saveEmployee();
}