As promised here is the second part of building our Shopping List Application with the excellent AngularJS the JavaScript MVW framework. In this final part we are going to look at how to extend our application so that it can receive data (via JSON) from a REST based application to populate our shopping list with items. This is as opposed to the static method we have at the moment.

AngularJS + Laravel: Prerequisites

The most obvious prerequisite is that you have followed or at least read the first part of this tutorial. If you haven’t go back & read part one of our AngularJS tutorial now. I’ll wait for you. You back? Good.

AngularJS + Laravel: Notes

First a couple of notes. There are a couple of ways to do this. I’ve picked the simplest as it is a simple application & we don’t want to overcomplicate things. Also I’m going to show you how to set up a REST based application to feed data into our AngularJS app using Laravel. To do this you need a few things. Please note: Laravel requires PHP 5.3.7 & MCrypt to work.

First is Composer. Just go to the download page. If you are on Windows like me, get the Windows install & you should be done in no time. You may need to restart or at least log out for some changes to take effect so go on. I’ll wait again. Back? Good.

Now that you have Composer, let’s get on with setting up Laravel.

AngularJS + Laravel: Setting Up

There are two ways to do this. I prefer the slightly more manual method so we are going to follow that one. Get yourself a copy of the Master of Laravel from Github (Hit ‘Download ZIP’ on the bottom right) and extract it to a folder in your localhost. I’m using the folder name rest for this. Now I think I should explain how this is going to work first.

Instead of having Laravel running our AngularJS application and our REST system we are just going to have Laravel run our REST system. This keeps things simpler as this tutorial is more about AngularJS & not Laravel.

Now that you have Laravel in it’s folder let’s get it setup. Open a command prompt, or terminal and navigate to the directory you just extracted Laravel to. In my case the command looked like this:

cd ../../xampp/htdocs/rest

That’s cd or ‘change directory’. Out 2 folders, into the xampp folder, htdocs and then rest. Now that you are there just run:

composer install

Then wait for a while as Composer installs Laravel & it’s dependencies.

All done? Good. Now you’ve done that we need to set a few more things up. First open up app/config/app.php and use a random string generator to get a 32 character string to place in the key field. Also update the url in here which will eventually be http://localhost/rest once we’ve setup an Alias.

Now rather than faff around with Virtual Hosts we are just going to set up an Alias. To do this open up your Apache httpd.conf file. In my case (using XAMPP) this is located at C:/xampp/apache/conf/httpd.conf. At around line 220 there is a block of code that starts with:

<Directory "C:/xampp/htdocs">

You want to copy that all the way down to the closing directory tag (</Directory>) and paste it directly underneath. Then change the path inside the directory tag to C:/xampp/htdocs/rest/public or whatever the path is to your Laravel public folder. Now look around line 340 for the <IfModule alias_module> tag and inside add:

Alias /rest C:/xampp/htdocs/rest/public

Again change the path to your Laravel public folder if it is different. Now restart Apache using your web servers control panel to apply the changes. Finally open up the .htaccess file located in the Laravel public folder and add the highlighted line:

<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteBase /rest

	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteRule ^ index.php [L]
</IfModule>

Once you’ve done that visiting http://localhost/rest should give you the Laravel welcome page. If not try restarting Apache once more, if all else fails leave a comment and I’ll see if I can help out. These steps should be the same for all web servers using Apache, just the paths might be different.

AngularJS + Laravel: REST

Now we’ve set up Laravel we can move on to getting our REST app working. For this we’ll have to visit the command line a few more times. In Laravel open up app/config/database.php and enter your local Database details. Once you’ve done that go back to your command prompt (open one & navigate to the ‘rest’ folder if you closed it) and run:

php artisan migrate:make create_shopping_table

This will make you a migration file. If it failed for some reason try these steps. Open it up from app/database/migrations now in the up function enter the following:

Schema::create('items', function($table) {
	$table->increments('id');
	$table->string('text');
	$table->boolean('bought');
});

In the down function enter this:

Schema::drop('items');

This tells Laravel to create us a table with those fields when we run the migration. If we were to reverse it the it runs the down function which simply drops the table. This is used for versioning your modifications to database tables within Laravel & is very handy. That, however, is something for a Laravel tutorial so let’s move on.

Now back in the command prompt run:

php artisan migrate

This will create the table we need.

Now we need to enter some data so we can check things are working. We could use Laravel’s Seeding system, but to save complicating things just jump into your Database Admin (Normally PHPMyAdmin) and add some data manually. If you aren’t sure how to do that, don’t worry it just means you won’t be able to see any results until we’ve added in the ability to add items via our app. That’s perfectly fine though so don’t worry.

Now to do some CRUD (Create, Read, Update, Delete) on our table we need a Model so Laravel can access our table. Let’s make one. In app/models create a file called Item.php. Inside all you need is the following:

<?php

Class Item extends Eloquent {

	public $timestamps = false;	

}

I’ve included the PHP tag just to show it is needed, but that’s all the code you need. The $timestamps variable is set just to tell Laravel we don’t want to use it’s automatic timestamp system. If you don’t add this Laravel will error so make sure you leave it in there.

Now we just need to create our REST controller. Again at the command prompt run this:

php artisan controller:make ShoppingController

Then open up your new controller found at app/controllers/ShoppingController.php. We are only going to use a few of the methods available here, but it is fine to leave the others for this demo. If you are using it in production I would remove the others. You are only going to need index, store, and destroy for this demo. First let’s take a look at index:

public function index()
{
	$items = Item::all();
	return $items;	
}

That’s all it is. This returns all of our items listed in some lovely clean JSON ready for AngularJS.

Next up is store:

public function store()
{
	$input = Input::get('text');

	$item = new Item;
	$item->text = $input;
	$item->save();
		
	if($item)
		return 1;
	else
		return 0;

}

This just grabs the name we have sent & saves it as a new item in the database. It also returns 1 or 0 to tell Angular if it saved successfully.

Finally is the destroy method:

public function destroy($id)
{
	$item = Item::find($id);

	if(!$item)
		return false;
	
	$item->delete();
}

All this does is find the item via the ID & deletes it.

The last thing we need to do is to setup some routing for Laravel. This is pretty simple, just open up app/routes.php, delete what you find and replace it with:

Route::resource('shop', 'ShoppingController');

This will make it so if you visit http://localhost/rest/shop/ you will hopefully be faced with some JSON. You can also setup a method called missingMethod in your REST controller to catch any calls to unknown methods.

Now let’s setup a route to catch any visits to other URLs so that when we turn off debugging there will be a message shown to visitors. First still in app/routes.php type this above the route you just added:

Route::get('/', function()
{
    App::abort(404);
});

This will tell Laravel to throw a 404 error for any url that doesn’t match shop. Then if you want to show a nice custom message you can open up app/start/global.php and add this above the Require The Filters File comment.

App::missing(function()
{
	return Response::make("This is not a valid URL for this REST service.", 404);
});

If you are more familiar with Laravel (again this is a AngularJS tutorial) you can return a view if you like, but this will do for this demo.

Phew! That’s our Laravel REST system complete, now we just need to tie our AngularJS code into it. Let’s do that now.

AngularJS + Laravel: $http

Now let’s get back to Angular. Reopen your app.js & index.html files from the last part.

First up we need to define our App this time. To do that in index.html change ng-app to ng-app="myApp". Then in app.js add this to the top of the file:

var app = angular.module('myApp', ['SharedServices'] );

This defines our app & declares the SharedServices module as a dependency, we’ll define that next.

Under that code let’s add in that dependency. It actually allows us to add a nice loading spinner while our App retrieves the data. To do this we use a nice little snippet, I honestly don’t know who came up with this first but thank you however you are:

angular.module('SharedServices', [])
    .config(function ($httpProvider) {
        $httpProvider.responseInterceptors.push('myHttpInterceptor');
        var spinnerFunction = function (data, headersGetter) {
            // todo start the spinner here
            $('#loading').show();
            return data;
        };
        $httpProvider.defaults.transformRequest.push(spinnerFunction);
    })
// register the interceptor as a service, intercepts ALL angular ajax http calls
    .factory('myHttpInterceptor', function ($q, $window) {
        return function (promise) {
            return promise.then(function (response) {
                // do something on success
                // todo hide the spinner
                $('#loading').hide();
                return response;

            }, function (response) {
                // do something on error
                // todo hide the spinner
                $('#loading').hide();
                return $q.reject(response);
            });
        };
    });

I don’t want to go into this code too much as it is outside of the scope of this tutorial, but it basically intercepts any requests made by the $http service and shows/hides a loader element as needed.

You may notice that this uses jQuery. Well since we don’t have it attached to our HTML file let’s do that. Above all our other scripts I’ve attached it using the Google CDN like this:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>

Again hosting it yourself is advised but this is fine for our demo. Next we need to add in the element it is using. Let’s add that in too. Again in your HTML file (index.html) find your ul and add a new li just above your ng-repeat. Like this:

<ul class="unstyled">
    <li id="loading"></li>
    <li ng-repeat="item in items">
        <input type="checkbox" id="item-{{item.id}}" ng-model="item.bought">
        <label for="item-{{item.id}}" class="{{isBought(item.bought)}}">{{item.name}}<span></span></label>
    </li>
</ul>

Now I’m going to use a Canvas loader. To do this just add the Heartcode CanvasLoader to the page with your other script tags:

<script src="//heartcode-canvasloader.googlecode.com/files/heartcode-canvasloader-min-0.9.1.js"></script>

Then add the following code just before the closing body tag.

    <script type="text/javascript">
      var cl = new CanvasLoader('loading');
      cl.setDiameter(100); // default is 40
      cl.setDensity(12); // default is 40
      cl.setRange(0.7); // default is 1.3
      cl.setSpeed(1); // default is 2
      cl.show(); // Hidden by default
    </script>

Then a little bit of CSS:

.container .main #loading {
	position: absolute;
	top: 25%;
	left: 40%;
	z-index: 900;
}

This is entirely optional, you can just add in a standard gif, but I like the new canvas option & though you’d might like it too.

Right, before we continue you need to know how you can access the REST controller’s different methods. It’s not as simple as calling them via URL such as rest/store. They have a special way to access them because of being a resource (as Laravel calls it) controller. Let’s take a look:

HTTP Verb url method
GET /rest/shop index
POST /rest/shop store
DELETE /rest/shop/:id destroy

These are just the ones we are using to keep it simple the others can be found over in Laravel’s documentation.

Now, you might be wondering why this section is subtitled $http, well we are getting to that now. I’m going to post all the new controller (ShopCtrl) code in one go this time as I think it would be too awkward to explain what has been changed:

function ShopCtrl($scope, $http) {

	$http({ method: 'GET', url: '/rest/shop/', cache: true }).
		success(function(data, status) {
			$scope.items = data
		}).
		error(function(data, status) {
			console.log('Status: ' + status);
		});

	$scope.clearBought = function() {
		$scope.items = _.filter($scope.items, function(item) {
			if(item.bought) {
				$http.delete('/rest/shop/' + item.id).error(function(data, status) {
					console.log(status);
				});
			}
			return !item.bought;
		});

	}

	$scope.addItem = function() {
		$http.post('/rest/shop', { text: $scope.itemEntry } ).success(function(data, status) {
			if(data) {
				var last = _.last($scope.items);
				$scope.items.push({text: $scope.itemEntry, bought: false, id: (last.id + 1) });
				$scope.itemEntry = '';
				console.log($scope.items);
			} else {
				console.log('There was a problem. Status: ' + status + '; Data: ' + data);
			}
		}).error(function(data, status) {
			console.log('status: ' + status);
		});
		
	}

	$scope.isBought = function(bought) {
		return (bought) ? 'bought' : 'not-bought';
	}

}

You’ll probably be able to see the biggest change is the use of Angular’s $http service. Let’s go through each new part of this code. First the most important part that I’ve noticed causes a lot of bother.

function ShopCtrl($scope, $http) {

Yep, there are lots of beginners that completely forget to send the $http service through and wonder why it doesn’t work. Well, in the infamous words of Adam Savage, “There’s your problem!”

Next up is the part that actually grabs the data from our REST service when the page loads.

$http({ method: 'GET', url: '/rest/shop/', cache: true }).
	success(function(data, status) {
		$scope.items = data
	}).
	error(function(data, status) {
		console.log('Status: ' + status);
	});

So this is the extended version of using the $http service. It is not necessary to use this version here, but I thought I would in this demo to show what it looked like. Basically we give it the method (HTTP VERB), in this case GET, the URL to visit, in this case /rest/shop/, and we also want it to cache the data to save resources. Then we have two callbacks, if you are familiar with jQuery’s AJAX then you might feel at home with these. Basically if the request was successful we populate the $scope.items (the variable that holds all the shopping items, remember?) variable with the data. If there was an error, you would need to trigger something for the user to see. Again as this is just a demo I have just triggered a console log so I can see the status code. You could trigger a modal with an error in or a small text message, anything you like really.

Notice that the code we have just been talking about is just inside the controller & is not held in a method. This is because we want to trigger it as soon as the controller is called. I’ve seen some arguments about whether this is valid, but I haven’t had any trouble with it so I’d say go for it. If it makes you feel better though you can add it as a method of $scope & call it at the bottom of your app.js file to trigger everything off.

Next up is the new version of clearBought.

$scope.clearBought = function() {
	$scope.items = _.filter($scope.items, function(item) {
		if(item.bought) {
			$http.delete('/rest/shop/' + item.id).error(function(data, status) {
				console.log(status);
			});
		}
		return !item.bought;
	});
}

Now this probably isn’t the most efficient way to do this, but as we were using a REST service you can only pass 1 ID at a time so this is the best I could come up with. If you have another idea that is more efficient, answers on a postcard (or the comments) please.

All this code does is exactly the same as before. Filters out the items that have been bought, but this time as it is filtering we check if the current item has been bought & fire off a $http request to delete the item. This time I’m using the short version with the delete method. All you do is pass the URL, which in this case is /rest/shop plus the ID of the item we want to delete. Again I’ve added a error callback which just sends back the status code for debugging, but you’d want to add your own result to notify the visitor. Again a modal or something like that would be perfect.

Finally is the code to add a new item. This is the longest code so hold onto your hats, we’re going in.

$scope.addItem = function() {
	$http.post('/rest/shop', { text: $scope.itemEntry } ).success(function(data, status) {
		if(data) {
			$scope.items.push({text: $scope.itemEntry, bought: false, id: data });
			$scope.itemEntry = '';
		} else {
			console.log('There was a problem. Status: ' + status + '; Data: ' + data);
		}
	}).error(function(data, status) {
		console.log('status: ' + status);
	});
}

So straight away we trigger $http this time using the post method, again we give it the url /rest/shop and this time we pass along some data. Note the lack of forward slash on the end of the URL. I’ve noticed if you add that in it will fail to add the item. In this case all we want is the text. How? Well the id & the bought value will be automatically be set to the correct values by the database as the ID is auto incremented & bought should be false by default.

Now in the success callback we check if our data was added, remember our Laravel method returns 1 on success, 0 on failure (0 is treated as falsy). The problem with this is that we need the ID that was created for the inserted item. To fix this go back to your Laravel application & open up app/controllers/ShoppingController.php and in the store method find:

	if($item)
		return 1;
	else
		return 0;

Change it to:

	if($item)
		return $item->id;
	else
		return 0;

This will send us back the ID created by the database when the item was inserted or 0 if it fails.

Now back to Angular. The last important line is:

$scope.items.push({text: $scope.itemEntry, bought: false, id: data });

This adds our new item to our array using the text from the text field again, automatically setting bought to false & using the ID we retrieved from Laravel (held in the data) variable. The rest of the code is just for error handling. Again I have console logs in there for debugging purposes but you could add some sort of modal to show visitors a friendly error.

AngularJS + Laravel: Artisan Doesn’t Work


This is normally because you are on a windows machine & PHP isn’t in your PATH environment variable. To solve this open an Windows Explorer window (the folder), right-click Computer and press ‘Properties’. Now click ‘Advanced System Settings’ on the left (Win 7 >), then press ‘Environment Variables’. In the bottom panel click Path, then click ‘Edit’. Now at the end of the 2nd text field enter a ; if there isn’t one there then type the path to the folder where PHP is held. If you are using XAMPP on your C drive this is normally C:\xampp\php\. That’s it.

You may need to restart or log out for the changes to take effect but normally you can just start using it without any problems.

AngularJS + Laravel: Summary

That’s it. It’s been a long tutorial but we’ve finally finished. If you reached this point you should now have a Shopping List application that is nearly identical to part 1, but it is now loading it’s data from an external source.

This is a huge tutorial so if you do have any problems please let me know in the comments & give me as much detail as you can about the issue you are having as it is pretty hard to debug without any error data. Unfortunately there is no demo for this part since, if this tutorial became popular, my server would hate me for it. If you are itching to see what it would look like though, use the demo in part 1.

As always thank you for reading. If you have any tips, suggestions or improvements I’m always happy to hear them. If you have any ideas for the kind of tutorials you’d like to see next, please post them on our Facebook wall or get in touch using Google Plus (links in the sidebar on the left).