http://lekkerlogic.com/2015/05/angularjs-and-bootstrap-mysql-shopping-list-tutorial/
Purpose: Build a shopping list web app working with AngularJS, MySQL and Bootstrap. See the working sample here. Download the project files here.
I am quickly trying to learn AngularJS for an upcoming project and I
wanted to work with the framework to create something I can learn
with. I chose to make a very quick and easy shopping list web app for
my family to use building off another tutorial. This way we can all share in the creation of a shopping list no matter where we are or who ends up at the store.
Yes, there are many 3rd party apps and services that provide this
same functionality, but none of those allow for me to learn Angular or
offer the speed and convenience as this.
I wont go into heavy details here as there are many AngularJS
tutorials on the web to get the basic info for creating the general
structure of an Angular app (such
as ng-app=”myApp”, ng-controller=”MyController”). Lets just jump right
into the mix.
First, lets create the MySQL table. The sql file for this is located
in the download link above. Use that to setup your tables.
For this tutorial I am first going to go through all the HTML and
template pages then circle back to show the app.js controller potion and
PHP that ties it all together. Hopefully that makes things a little
clearer rather than jumping between.
Looking at the index.html file there are few things to note:
<html ng-app="shopApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"/>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="css/shop.css"/>
<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
<!-- Favicon -->
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<!--[if lt IE 9]><script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<title>Shopping List</title>
</head>
<body ng-controller="shopController">
<div class="navbar navbar-default" id="navbar">
<div class="container" id="navbar-container">
<div class="navbar-header navbar-brand"><i class="fa fa-check-square-o"></i> Shopping List
<!-- /.navbar-header -->
<div class="pull-right">
<button class="btn btn-default btn-sm mt-10" ng-click="clearItem()">
Clear Completed Items
</button>
<div class="container">
<div class="row">
<div class="col-lg-12 col-sm-12">
<div ng-include src="'templates/shop.html'">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script type="text/javascript" src="app/app.js"></script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
<html ng-app="shopApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"/>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="css/shop.css"/>
<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
<!-- Favicon -->
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="icon" href="favicon.ico" type="image/x-icon">
<!--[if lt IE 9]><script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<title>Shopping List</title>
</head>
<body ng-controller="shopController">
<div class="navbar navbar-default" id="navbar">
<div class="container" id="navbar-container">
<div class="navbar-header navbar-brand"><i class="fa fa-check-square-o"></i> Shopping List <!-- /.navbar-header -->
<div class="pull-right">
<button class="btn btn-default btn-sm mt-10" ng-click="clearItem()">
Clear Completed Items
</button>
<div class="container">
<div class="row">
<div class="col-lg-12 col-sm-12">
<div ng-include src="'templates/shop.html'">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script type="text/javascript" src="app/app.js"></script>
</body>
</html>
|
Notice we are using ng-include src="'templates/shop.html'" to
include a separate html file that contains the majority of our Angular
items. The actual index.html is rather straight forward so wont I spend
time on this. Don’t forget to declare your app <html ng-app="shopApp"> and controller <body ng-controller="shopController"> here and also include the Angular library as well as the path to your app.js file.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script type="text/javascript" src="app/app.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script type="text/javascript" src="app/app.js"></script>
|
Now lets take a look at our templates/shop.html that we use to display out items.
<form class="form-inline" name="shopList">
<div class="form-actions">
<div class="input-group">
<input type="text" class="form-control" ng-model="itemInput" placeholder="Add New Item" autofocus required />
<div class="input-group-btn">
<button class="btn btn-info" type="submit" ng-click="addItem(itemInput); itemInput = null" ng-disabled="shopList.$invalid">
<i class="fa fa-plus"></i> Add Item
</button>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
|
<form class="form-inline" name="shopList">
<div class="form-actions">
<div class="input-group">
<input type="text" class="form-control" ng-model="itemInput" placeholder="Add New Item" autofocus required />
<div class="input-group-btn">
<button class="btn btn-info" type="submit" ng-click="addItem(itemInput); itemInput = null" ng-disabled="shopList.$invalid">
<i class="fa fa-plus"></i> Add Item
</button>
</form>
|
First we have our form to add a new item to our list. This is a
standard web form but we use some Angular directives to bind and trigger
our insert statement. Using ng-model="itemInput" we can capture the data being typed in our input field and pass it to the click function in ng-click="addItem(itemInput); itemInput = null" that will eventually be inserted into our MySQL table using the addItem() function (more later on this).
To filter our results once they are loaded into the page we create another text input.
<div class="col-sm-3 hidden-xs">
<input type="text" ng-model="filterItem" class="form-control" placeholder="Filter Items" />
|
<div class="col-sm-3 hidden-xs">
<input type="text" ng-model="filterItem" class="form-control" placeholder="Filter Items" />
</div>
|
Using the ng-model="filterItem" directive we can again capture the data and pass it along somewhere else in our program.
Finally, we need to output our list of items and make the associated triggers to show those items as complete or deleting them:
<fieldset class="items-list">
<label class="items-list-item" ng-repeat="item in items | filter : filterItem">
<input
type="checkbox"
value="{{item.STATUS}}"
ng-checked="item.STATUS==2"
ng-click="changeStatus(item.ID,item.STATUS,item.ITEM)"
class="items-list-cb"/>
<span class="items-list-mark">
<span class="items-list-desc" ng-class="{strike:item.STATUS==2}">{{item.ITEM}} <span class="date">[{{item.CREATED_AT | date:"MMM d"}}]
<a ng-click="deleteItem(item.ID)" class="pull-right red"><i class="fa fa-minus-circle"></i>
</label>
</fieldset>
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<fieldset class="items-list">
<label class="items-list-item" ng-repeat="item in items | filter : filterItem">
<input
type="checkbox"
value="{{item.STATUS}}"
ng-checked="item.STATUS==2"
ng-click="changeStatus(item.ID,item.STATUS,item.ITEM)"
class="items-list-cb"/>
<span class="items-list-mark">
<span class="items-list-desc" ng-class="{strike:item.STATUS==2}">{{item.ITEM}} <span class="date">[{{item.CREATED_AT | date:"MMM d"}}]
<a ng-click="deleteItem(item.ID)" class="pull-right red"><i class="fa fa-minus-circle"></i>
</label>
</fieldset>
|
Notice we are using the ng-repeat directive for ng-repeat="item in items | filter : filterItem"
to show each item in the items data set. ng-repeat is a very powerful
directive in Angular and is the heart of this particular web app. Here
we also tie in our filterItem field so we can filter the results.
For the input we are setting the value to the status value from the
data table using {{item.STATUS}} and when the item status is ==2 we set
the input state as checked. To trigger this we use ng-click="changeStatus(item.ID,item.STATUS,item.ITEM) to
update the entry in MySQL with the changeStatus() function found in our
controller (more on that in a bit). We then output our results in a
<span> with {{item.ITEM}} using ng-class="{strike:item.STATUS==2}" to
set the CSS for a strike-thru as well as format the date field
with [{{item.CREATED_AT | date:”MMM d”}}]. Last we have a delete
function in place to remove items as needed calling on another function
in our controller via ng-click="deleteItem(item.ID)"
OK, on to our controller found in app.js
//Define an angular module for our app
var app = angular.module('shopApp', []);
app.controller('shopController', function($scope, $http) {
|
//Define an angular module for our app
var app = angular.module('shopApp', []);
app.controller('shopController', function($scope, $http) {
|
First we define our app ‘shopApp’ and our controller to use
‘shopController’. Remember these are linking the asscoatiated areas
found within index.html using <html ng-app="shopApp"> and <body ng-controller="shopController">
Now we enter our functions to make our various MySQL requests for creating, reading, updating and deleting our items (CRUD).
getItem(); // Load all available items
function getItem(){
$http.post("ajax/getItem.php").success(function(data){
$scope.items = data;
});
};
$scope.addItem = function (item) {
$http.post("ajax/addItem.php?item="+item).success(function(data){
getItem();
$scope.itemInput = "";
});
};
$scope.deleteItem = function (item) {
if(confirm("Are you sure to delete this item?")){
$http.post("ajax/deleteItem.php?itemID="+item).success(function(data){
getItem();
});
}
};
$scope.clearItem = function () {
if(confirm("Delete all checked items?")){
$http.post("ajax/clearItem.php").success(function(data){
getItem();
});
}
};
$scope.changeStatus = function(item, status, task) {
if(status=='2'){status='0';}else{status='2';}
$http.post("ajax/updateItem.php?itemID="+item+"&status="+status).success(function(data){
getItem();
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
getItem(); // Load all available items
function getItem(){
$http.post("ajax/getItem.php").success(function(data){
$scope.items = data;
});
};
$scope.addItem = function (item) {
$http.post("ajax/addItem.php?item="+item).success(function(data){
getItem();
$scope.itemInput = "";
});
};
$scope.deleteItem = function (item) {
if(confirm("Are you sure to delete this item?")){
$http.post("ajax/deleteItem.php?itemID="+item).success(function(data){
getItem();
});
}
};
$scope.clearItem = function () {
if(confirm("Delete all checked items?")){
$http.post("ajax/clearItem.php").success(function(data){
getItem();
});
}
};
$scope.changeStatus = function(item, status, task) {
if(status=='2'){status='0';}else{status='2';}
$http.post("ajax/updateItem.php?itemID="+item+"&status="+status).success(function(data){
getItem();
});
};
|
getItem() is our main function that you will see called. This
retrieves the data from our MySQL using the getItem.php file located in
/ajax.
<?php
require_once '../includes/db.php'; // The mysql database connection script
$status = '%';
if(isset($_GET['status'])){
$status = $mysqli->real_escape_string($_GET['status']);
}
$query="SELECT ID, ITEM, STATUS, CREATED_AT from shop where status like '$status' order by status,id desc";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
$arr = array();
if($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$arr[] = $row;
}
}
# JSON-encode the response
echo $json_response = json_encode($arr);
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?php
require_once '../includes/db.php'; // The mysql database connection script
$status = '%';
if(isset($_GET['status'])){
$status = $mysqli->real_escape_string($_GET['status']);
}
$query="SELECT ID, ITEM, STATUS, CREATED_AT from shop where status like '$status' order by status,id desc";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
$arr = array();
if($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$arr[] = $row;
}
}
# JSON-encode the response
echo $json_response = json_encode($arr);
?>
|
The remaining functions are all very similar once you understand the
basic concepts. By using $_GET within PHP we can pass the data into the
MySQL query to get what we need. By looking at addItem() we can see
how this is done. We call this function using ng-click="addItem(itemInput)" where the itemInput is from our input field.
$scope.addItem = function (item) {
$http.post("ajax/addItem.php?item="+item).success(function(data){
getItem();
$scope.itemInput = "";
});
};
|
$scope.addItem = function (item) {
$http.post("ajax/addItem.php?item="+item).success(function(data){
getItem();
$scope.itemInput = "";
});
};
|
Here we pass the item entered into our input field thru to MySQL to be inserted. The PHP looks like this:
<?php
require_once '../includes/db.php'; // The mysql database connection script
if(isset($_GET['item'])){
$item = $mysqli->real_escape_string($_GET['item']);
$status = "0";
$created = date("Y-m-d", strtotime("now"));
$query="INSERT INTO shop(item,status,created_at) VALUES ('$item', '$status', '$created')";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
$result = $mysqli->affected_rows;
echo $json_response = json_encode($result);
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?php
require_once '../includes/db.php'; // The mysql database connection script
if(isset($_GET['item'])){
$item = $mysqli->real_escape_string($_GET['item']);
$status = "0";
$created = date("Y-m-d", strtotime("now"));
$query="INSERT INTO shop(item,status,created_at) VALUES ('$item', '$status', '$created')";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
$result = $mysqli->affected_rows;
echo $json_response = json_encode($result);
}
?>
|
Again we are saying $_GET[‘item’] from our form and set it as the $item variable to be inserted in the item column.
The only function that is slightly different is our toggleStatus()
function. This triggers the item to be complete or not, by updating the
status from 0 to 2.
$scope.changeStatus = function(item, status, task) {
if(status=='2'){status='0';}else{status='2';}
$http.post("ajax/updateItem.php?itemID="+item+"&status="+status).success(function(data){
getItem();
});
};
|
$scope.changeStatus = function(item, status, task) {
if(status=='2'){status='0';}else{status='2';}
$http.post("ajax/updateItem.php?itemID="+item+"&status="+status).success(function(data){
getItem();
});
};
|
<?php
require_once '../includes/db.php'; // The mysql database connection script
if(isset($_GET['itemID'])){
$status = $mysqli->real_escape_string($_GET['status']);
$itemID = $mysqli->real_escape_string($_GET['itemID']);
$query="UPDATE shop set status='$status' where id='$itemID'";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
$result = $mysqli->affected_rows;
$json_response = json_encode($result);
}
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?php
require_once '../includes/db.php'; // The mysql database connection script
if(isset($_GET['itemID'])){
$status = $mysqli->real_escape_string($_GET['status']);
$itemID = $mysqli->real_escape_string($_GET['itemID']);
$query="UPDATE shop set status='$status' where id='$itemID'";
$result = $mysqli->query($query) or die($mysqli->error.__LINE__);
$result = $mysqli->affected_rows;
$json_response = json_encode($result);
}
?>
|
As you can see Angular provides some pretty cool feature that help
HTML become what it should have been when it was first released.
Granted we had no idea back then what we would be doing with it now.
This proved a great way for me to learn some of the basic features of
this framework and looking forward to doing more with it. Let me know
how you might improve this in the comments below.
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(21) | 评论(0) | 转发(0) |