Foundation for Apps (F4A) is a new single-page app (SPA) framework from Zurb which is closely related to Foundation 5 (aka Foundation for Sites), a widely used front-end framework. It’s built around Angular JS and a flexbox grid framework, and it’s intended to make creating a web app very quick & simple, letting us quickly jump into the code that’s unique to our application.
As Foundation for Apps was released only at the end of 2014, it hasn’t yet seen widespread usage, so there are previous few sources of good information on using the framework. This article is meant to be the comprehensive guide to building a functional web app with Foundation for Apps, from start to finish. The techniques detailed here are fundamental to building practically any kind of app for any client, and this tutorial makes for a thorough introduction to the wider world of Angular JS and single-page apps.
In light of the new film being released later this year, we’ll be building a Star Wars knowledge base. It will be a responsive web application using a RESTful API, caching, and many of the features that Foundation for Apps and Angular JS offer.
Prefer to skip to the good stuff?
Getting Started
Take a quick squiz through the official F4A documentation, which is good for explaining the styling aspects, but it doesn’t go into detail about application functionality. Also utilize the excellent Angular JS documentation, but bear in mind that F4A includes some services that aren’t standard, and some Angular functions may not work out of the box. Our app will get its data from the handy Star Wars API found at swapi.co. Take a look through the swapi documentation to get a feel for the data served and its structure. Our application will be based on that structure for simplicity.
First, lets install F4A & create our project. Make sure you have Ruby (already on OS X by default) & Node.js installed, then follow the 4-step process detailed here in the F4A docs. It’s pretty straightforward, even if you haven’t used the command line before. Once you’ve finished the process, your browser should be displaying the default homepage of your application at http://localhost:8080/#!/
.
Let’s get acquainted with the project file/folder set up.
The only file in our app’s base directory that we need to pay attention to is gulpfile.js
, which gives instructions to the gulp process, which you’ve already used to start the server for our app. Gulp is a build system, and it’s very similar to Grunt. Later on, if we want to add some additional Angular modules or plugins, we’ll need to update this gulpfile with references to JS or CSS files for those modules/plugins.
The client
folder is where we’ll find all other files we’re concerned with. clients/assets/js/app.js
is where our controller, directives, and custom filters will be for this application. All of our app’s SCSS is found in clients/assets/scss
, naturally. clients/index.html
is the base template for our application, and clients/templates/
is where we can find the template for each of our pages, most of which haven’t been created yet.
Let’s start building! First, modify the index.html
page, which doesn’t start out very well-optimized to suit a real application. We’ll add an off-canvas menu for smaller screens, a button to toggle its opening, and a nice fade effect using classes from Foundation for Apps’ “Motion UI”.
Add the following to your settings.scss
to change some colors and add breakpoints:
Now trick out app.scss
to add a bit of pizzazz – use app.scss in the repo as an example.
Quickly overwrite the default home.html
file in clients/templates/
with a simple menu of links to each of the pages we’ll build:
Our template is now looking pretty unique to us now – no cookie-cutter Foundation at this point:
Creating a list of films
Now we’re cooking with gas. In our templates
folder, create a template file for our first subpage: films.html
. Paste this snippet into the top:
---
name: films
url: /films/:id?p=
controller: FilmsCtrl
---
This tells our app three things:
- In links to this page, we’ll refer to it as
films
- The URL will have two possible parameters:
id
(the ID of the film as per our data) andp
(the page number in the listing of all films) - We’ll be using our own custom Angular JS controller called
FilmsCtrl
instead of the default blank one that F4A creates automatically.
Since we’re using our own controller, we need to create. Look through the controller below, which we’ll be using for our list of films as well as our single-film pages. You can see that this controller keeps track of URL parameters, figures out what page of results we’re on, gets the necessary data (either a list of films, or details of a single film, depending on the URL parameters) from our external API, and returns it to our view using the $scope
variable. Add it to app.js
after the angular.module
declaration closes:
After saving app.js
, you may need to restart your server using the terminal (ctrl
+ c to cancel operation, then foundation-apps watch
again) to ensure that your app includes the new template file you’ve created along with the new controller.
And just like that, we have a fully functional controller that gets data from an external RESTful API source, caches the result in the browser’s session, and returns the data to our view!
Open up films.html
again, and let’s start building out the view of the data that we can now access. Begin by adding the base view, which will show a list of films. We can access all properties that we’ve added to our $scope
variable without prefixing them with $scope
, such as (in this case) films
, prevPage
, and nextPage
. Add the following below the template’s existing content:
Bodacious! We’ve got a list of film names and pagination if there are multiple pages of data. But that’s not especially useful yet — let’s turn the film name into a link to that film’s page in our app.
We plan to use the film’s ID as the id
parameter in our URL, and we have access to the film’s url
attribute, which happens to have the film’s ID as its last parameter before the final slash. But how can we grab only the ID out of the URL that we have access to? Angular JS makes it easy, with custom filters. Let’s wrap our {{film.title}}
in a link, add a ui-sref
attribute (which sets up an internal link) and use our film.url
data with a custom filter applied to it:
<a ui-sref="films({ id:( film.url | lastdir ), p:'' })">{{film.title | capitalize}}</a>
Egad! Now our page is broken, because our app doesn’t know what the lastdir
and capitalize
filters are. We need to define those filters in our app.js
file, placed just after our controller:
Bingo, we now have a list of films, each of which links to its respective film page!
However, that link just takes us to an empty page at the moment, because our films.html
isn’t set up to handle showing a specific film instead of the whole list. That’s our next step.
Displaying details of a single film
We’ve already set up all the data we need for the single-film page in our FilmsCtrl
controller in the $scope.film
(same as $scope['film']
) variable, so let’s reuse films.html
and add another section that’s visible only when the singular film
variable is set. We’ll set each key/value pair to use <dt>
and <dd>
within a <dl>
because we’re not unsemantic swine. Remember also that some of film
‘s fields, such as characters
, have multiple values in an array, so we’ll need to use ng-repeat
for those to display each value. To link each character to its character page, we’ll use the same method we did on the listing of films: using our lastdir
filter, we link to each character’s people
page by his/her ID.
But look, when we view a film’s entry now, the list of characters shows just a URL relating to the character, rather than the character’s name.
We need to replace that text with the character’s name, but we don’t have it. Perhaps we could look that URL up using the same method we used to get our film in the first place – then the data we receive from the call will contain the character’s name. Let’s open app.js
up again and add a directive we’ll call getProp
.
getProp
returns a single property from the data resulting from our $http.get
call, and we can specify which property we want. To use this directive, we need to add it to the area within our ng-repeat
like so:
<span get-prop prop="'name'" url="character">{{character}}</span>
Nice, we now have each character’s name instead of just a wild URL, and each links to its respective page. Now our single film
view will be complete once the rest of the data fields are added to the view (see films.html in the repo for the rest).
Refactoring our code for re-use
Looking through the swapi.co API and our plans for the rest of the application, we can clearly see that our controllers for all other pages will be extremely similar to this one, varying only in the category of data we’re getting.
With that in mind, let’s move the code inside our films
controller to its own function called genericController
, placed just before the last closing brackets of app.js
. We also need to replace every instance of the string 'films'
with the variable multiple
(5 instances) and 'film'
with single
(1 instance), as they represent the multiple and singular forms of the entity of each page. This allows us to create very DRY, reusable code that’s also easier to read & understand.
Now we can add a call in our FilmsCtrl
controller to our new genericController
function with our two variables (multiple
& single
versions of our data) passed as parameters:
.controller('FilmsCtrl', function($scope, $state, $http){
$scope = genericController($scope, $state, $http, 'films', 'film');
})
Excellent, now we have a reusable controller that grabs the data we need for any given page and puts it in a usable format! We can now easily create our our other pages’ controllers just after FilmsCtrl
, in the same way:
Now, go ahead and create the template HTML files for planets
, species
, people
, starships
, and vehicles
in the same way we created films.html
, but referencing the fields in the swapi docs for each respective category of data.
And voila!, all of our pages now show their correct respective data and inter-link with one another!
Final notes
And our application is complete! Our demo below is hosted by Aerobatic, which is currently has a free plan and is targeted towards front-end web applications. You’ll see in our repository that we’ve added some domain-specific options so that when we’re hosting on Aerobatic’s servers, we can take advantage of their API Gateway, which sets up a proxy that caches the API’s data on the server once we request it. Without caching, the application above would be both latency-limited and request-limited (swapi.co allows only so many requests per domain, as do most other APIs), and because our data isn’t likely to change often, that server caching makes everything very speedy after the first load.
In the demo & repository, you can see that we’ve also added another API call on the details pages, which grabs an image URL for each entity from a Google Custom Search that we set up to trawl through Wookiepedia and StarWars.com, so we’ve now got dynamic, highly relevant images showing up on each detail page.
Take a look at the demo below, look through the source code for some hidden goodies & more Foundation-specific tricks, or download the repository & build on it locally with your own improvements.