JeanCarl's Adventures

Project 3: Count It! with Parse

June 17, 2015 |

Have you ever counted the number of steps when you walk up a flight of steps? Wanted to know how many times you’ve done a certain task? We count things everyday!

This third project of my 15 projects in 30 days challenge will help you keep count. Using Parse, we’ll store items (it’s up to the user to determine what an item in a list represents) and keep a count of how many times that item is clicked on.

What’s great about using Parse is that we’ll see it is super easy to integrate user access control and allow multiple users to see only their list.

Parse

First off, we’ll need a place to store this list of items. Head over to https://www.parse.com and sign up for an account. Then go to your apps. Click on Create a new App tab in the top center of the page.

Click on the gear icon in the top right of the box representing the newly created app.

Photo

Select Keys from the list. Copy the Application ID and JavaScript Key for later.

Photo

Count It!

This project contains two files index.html, and countit.js. The neat part about this project is that there is no server backend, since we are using Parse. countit.js is the AngularJS controller that is used by index.html.

<!-- Filename: index.html -->
<!doctype html>

<!DOCTYPE html> 
<html ng-app="countItApp">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1"> 
  <title>Count It!</title> 
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.css" />
  <script src="http://www.parsecdn.com/js/parse-1.2.13.min.js"></script>
  <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.1.1/jquery.mobile-1.1.1.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.16/angular.min.js"></script>
  <script src="countit.js"></script>
</head> 

<body ng-controller="CountItCtrl"> 

<div data-role="page">

  <div data-role="header" data-theme="b">
    ## Count It!
  </div><!-- /header -->

  <div data-role="content" > 
    <div ng-show="isLoggedIn()">
      <ul data-role="listview" data-inset="true" data-split-icon="delete">
        <li ng-repeat="item in items" >
          <a href="#" ng-click="item.increaseCount()">
            <h2>{{item.getTitle()}}</h2>
            <span class="ui-li-count">{{item.getTimesCompleted()}}</span>
          </a>
          <a href="#" ng-click="removeItem(item)" data-rel="popup" data-position-to="window" data-transition="pop">Remove Item</a>
        </li>
      </ul>

      <div style="clear: both; margin-top: 10px">
        <input type="text" ng-model="itemTitle" placeholder="Enter item name" /> <input type="button" ng-click="addItem()" value="Add" />
      </div>
    </div>

    <div ng-hide="isLoggedIn()">
      Username: <input type="text" ng-model="username" />
      Password: <input type="password" ng-model="password" />
      <input type="button" ng-click="logIn()" value="Login" /> or <input type="button" ng-click="signUp()" value="Sign Up!" />
    </div>
  </div><!-- /content -->
  
  <div data-role="footer" data-theme="b">
    <h4 ng-show="isLoggedIn()">Welcome {{getUsername()}}! <a href="#" ng-click="logout()">Logout</a></h4>
  </div><!-- /footer -->
  
</div><!-- /page -->

</body>
</html>
// countit.js
angular.module('countItApp', [])
.controller('CountItCtrl', function($scope) {
	$scope.username = $scope.password = $scope.itemTitle = '';
	$scope.items = [];

	Parse.initialize('APPLICATIONID',
                   'JAVASCRIPTKEY');

	var Item = Parse.Object.extend('Item', {
		increaseCount: function() {
		  this.save({timesCompleted: this.get('timesCompleted') + 1});
		},

		getTitle: function() {
			return this.get('title');
		},

		getTimesCompleted: function() {
			return this.get('timesCompleted');
		}
	})

	$scope.isLoggedIn = function() {
		return Parse.User.current() ? true : false;
	}

	$scope.logout = function() {
		Parse.User.logOut();
	}

	$scope.getUsername = function() {
		return Parse.User.current() ? Parse.User.current().getUsername() : '';
	}

	$scope.logIn = function() {
		Parse.User.logIn($scope.username, $scope.password, {
		  success: function(user) {
		    $scope.findItems();
		  },
		  error: function(user, error) {
		    alert('Error: ' + error.code + ' ' + error.message);
		  }
		});
	}

	$scope.signUp = function() {
		var user = new Parse.User();

		user.signUp({username: $scope.username,password: $scope.password}, {
		  success: function(user) {
		    $scope.logIn();
		  },
		  error: function(user, error) {
		    alert('Error: ' + error.code + ' ' + error.message);
		  }
		});
	}	

	$scope.addItem = function() {
		var item = new Item();

		item.save({
			title: $scope.itemTitle, 
			timesCompleted: 0,
			user: Parse.User.current(),
			ACL: new Parse.ACL(Parse.User.current())
		}, {
			success: function(item) {
				$scope.$apply(function() {
					$scope.items.push(item);
				});
			},
			error: function(t, error) {
				alert('Error: '+error.message);
			}
		});

		$scope.itemTitle = '';
	}

	$scope.removeItem = function(item) {
		for(var i=0; i<$scope.items.length; i++) {
			if($scope.items[i] == item) {
				$scope.items.splice(i, 1);
				item.destroy();
				break;
			}
		}
	}

	$scope.findItems = function() {
		var query = new Parse.Query(Item);
		query.find().then(function(list) {
			$scope.$apply(function() {
				$scope.items = list;
			});
		});
	}

	$scope.findItems();
});

Using the Application ID and JavaScript Key from Parse, insert these values in the countit.js file.

Parse.initialize('APPLICATIONID',
                   'JAVASCRIPTKEY');

And that’s it for setting up this project! Head over to index.html and you’ll be prompted to login, or sign up. Since we don’t have a user account yet, enter a username and password, and click Sign Up!

Photo

It should login automatically.

Photo

Enter a name of something you want to start counting. For my example, I’m wanting to count the number of times I’ve used different types of technologies thus far in my 30 day challenge. I’ll add “Parse” to the list.

Photo

When I click Add, the app will create a new Item object on Parse, and add it to the list. If I click on the item, the bubble count increases by one.

Photo

If I click on the X, the item is removed from the list and from Parse.

After adding a number of items and counting them, here’s the stats thus far.

Photo

If you click on Logout at the bottom of the page, you’re logged out and can either login again, or create another user. This second user has their own list and cannot see the items of that the first user created. This is because when the Item object is created, we specify the user in the ACL (Access Control List). Parse automatically filters the results to only ones that the current user has access to.

Source Code

You can find the repo on GitHub.

That’s it for this project. Here are additional ideas that might be added to this project:

  • Order the list with count in descending order. The more I use a technology, the higher up the list goes.
  • Add groupings, where you might have a list for APIs, Frameworks, etc.

Post-mortem

The ACL is deceivingly simple yet very powerful. If you’ve ever tried creating a database of user objects and had to create your own ACL methods, you know the logic and legwork that it requires. I feel like there’s a lot more that can be done with the ACL model.

The one hiccup that took me time to understand is why AngularJS wasn’t updating the item list. The trick was to wrap the code in $scope.apply(function() { … }) to notify AngularJS that the data was being updated.

15 Projects in 30 Days Challenge

This blog post is part of my 15 projects in 30 days challenge. I’m hacking together 15 projects with different APIs, services, and technologies that I’ve had little to no exposure to. If my code isn’t completely efficient or accurate, please understand it isn’t meant to be complete and bulletproof. When something is left out, I try to mention it. Reach out to me and kindly teach me if I go towards the dark side. ?

This challenge serves a couple of purposes. First, I’ve always enjoyed hacking new things together and using APIs. And I haven’t had the chance (more like a reason) to dive in head first with things like AngularJS and Parse. This project demonstrated AngularJS and Parse.