Categories
howto work

Taking Flight with AngularJs

Lately at work we have been getting into the Flight PHP framework for simple REST-like services with an AngularJS front end. We have had to do a bit of juggling however to get one of the nicer AngularJS features to work: “html5Mode”. Here is what we did.

Flight Setup

After a basic composer setup through

{
	"require" : {
		"mikecao/flight" : "dev-master",
	},
	"autoload" : {
		"psr-0" : {
			"Flight" : "vendor/mikecao/flight",
		}
	}
}

and ultra simple index.php install, we were ready to rock.

Flight, just like ZF1, ZF2, and a ton of other frameworks wants our webserver to rewrite our urls. Flight recommends:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

For those of you who don’t know, that just means if a file or directory doesn’t exist, use the Flight bootstrap file instead. This allows nice urls such as http://yoursite.com/2013/my/third/dog without needing a file called “dog” three directories deep in the code.

Angular Setup

Angular may be even easier to initially set up, buy just adding a line to our page head or at the bottom of the body.

 <script src="https://yoursite.com/js/angular.min.js"></script>

In this case we want to get angular-resource.min.js as well. Just go to the AngularJS website, click the big Download button, click “Unstable” to get the 1.2.x series, and then click “Extras” to find the files.

Front vs Back

In order to have greater separation of view and business logic, we made a design decision to have two index pages on our site. Our core template for everything resides in index.html, and our backend bootstraps from index.php. This allows us to serve up our base page without any PHP spin-up lag. Raw HTML is seriously faster than PHP. Besides, we want this site to be HTML5 and REST right?

Routing

Normally, Angular uses # for routing. So instead of having a pretty URL like
-   http://yoursite.com/about
it would look like
-   http://yoursite.com/#/about
This has been a fairly common practice it seems when it comes to ajaxified sites. I don’t know about you, but that looks a bit ugly. Thankfully, Angular provides a great solution.

html5Mode prettifies your app by removing the hash from the url if your browser supports the HTML5History API. If you lack support (I am looking at you, IE 8 & 9), the URL will remain with the hash.

Test Page

So lets throw up a test page! We need to make our index.html, a js, and a template.

<!doctype html>
<html lang="en" data-ng-app="MyApp" ng-csp>
<head><meta charset="UTF-8"></head>
<body>
    <a href="/">Home</a> - <a href="/#/about">About</a>
    <div class="container" ng-view>
        This will get overwritten!
    </div>

    <script src="/js/angular.js"></script>
    <script src="/js/angular-route.js"></script>
    <script src="/js/app.js"></script>
</body>
</html>
//bootstrap angular with one extra route
var base = angular.module("MyApp",['ngResource','ngRoute'])
    .config(['$routeProvider', '$locationProvider',
        function($routeProvider, $locationProvider){
            $routeProvider.
                when('/about', {
                    templateUrl:'/template/about.html'}
                    ).
                otherwise({redirectTo : '/'});
    
    }]);
base.config(['$locationProvider', function($location) {
  $location.html5Mode(true); //suppress hashbang in browers supporting HTML5 history
}]);
<p>About how AngularJS + Flight Rocks...</p>

Load the site and click on the About link. Angular will know to replace the content of the div ng-view with whatever templateUrl we specify.

Sounds simple right?

Well hold your horses, padre. Refresh the page (or directly nav to /about) and you might be in for a surprise using most PHP frameworks. Remember our rewrite rules up above? Since there is no “about” directory or page, Apache will send us to the index.php file. We never told Flight about any routes, so it will give us an error. As a friend of mine often says, that is sub-optimal behaviour (I think he would like the “u” in there).

Solution

Initially I tried to let Flight is_file “about.html” to see if it existed in the templates folder, but that smelled really bad when we started to need sub folders. We told flight about the /about route, but why bootstrap flight when all we need is the html template? Besides, that wouldn’t give us the Angular wrapped index.html back without a couple more backflips.

The solution we found was in our Apache config (or .htaccess file).

 RewriteEngine on
#let flight do its thing
 RewriteRule ^(api/.*)$ index.php?url=$1 [QSA,L,NC]
 RewriteRule ^(app/.*)$ index.php?url=$1 [QSA,L,NC]
#let angular do its thing
 RewriteCond %{REQUEST_FILENAME} !-f      
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule ^(.*) index.html [NC,L]

What is going on here?

Line 3 lets Flight return REST data off api/ endpoints.
Line 4 lets Flight send back standard pages if and when we need it to.
Lines 6-8 should look familiar, but in this case we redirect to the index.html file so that Angular can work its magic.

Our routes stay simple and understandable. Angular is happy. Flight is happy. The browser is happy. Yay!

Question: Does hash or octothorp sound better? Comment below!