This post was written by Ben Jones, Front-end Developer for Toptal.

Ben is a skilled developer who always keeps the user in mind, which allows him to see how beneficial or detrimental a development process can be. He wants to create simple but effective software to reduce workload on all sides and to make employees, employers, and customers happy.


JavaScript frameworks/libraries such as Vue can offer a fantastic user experience when browsing your site. Most offer a way of dynamically changing page content without having to send a request to the server each time.

However, there is an issue with this approach. When initially loading your website, your browser doesn’t receive a complete page to display. Instead, it gets sent a bunch of pieces to construct the page (HTML, CSS, other files) and instructions for how to put them all together (a JavaScript framework/library) It takes a measurable amount of time to put all this information together before your browser actually has something to display. It’s like being sent a bunch of books along with a flat-pack bookcase. You’d have to build the bookcase first and then fill it with the books.

The solution to this is clever: Have a version of the framework/library on the server that can build a ready-to-display page. Then send this complete page to the browser along with the ability to make further changes and still have dynamic page content (the framework/library), just like being sent a ready-made bookcase along with some books. Sure, you still have to put the books in the bookcase, but you’ve got something usable immediately.

Visual comparison of client-side and server-side rendering

Beyond the silly analogy, there are also a bunch of other advantages. For example, a page that rarely changes, such as an About Us page, doesn’t need to be recreated every single time a user asks for it. So a server can create it once and then cache it or store it somewhere for future use. These kinds of speed improvements may seem tiny, but in an environment where time until responsiveness is measured in milliseconds (or less), every little bit counts.

If you’d like more information on the advantages of SSR in a Vue environment, you should check out Vue’s own article on SSR. There is a variety of options to achieve these results, but the most popular one, which is also recommended by the Vue team, is Nuxt.

Why Nuxt.js

Nuxt.js is based off an implementation of SSR for the popular React library called Next. After seeing the advantages of this design, a similar implementation was designed for Vue called Nuxt. Those familiar with the React+Next combination will spot a bunch of similarities in design and layout of the application. However, Nuxt offers Vue-specific features to create a powerful yet flexible SSR solution for Vue.

Nuxt was updated to a production-ready 1.0 version in January 2018 and is part of an active and well-supported community. One of the great things is that building a project using Nuxt isn’t that different from building any other Vue project. In fact, it provides a bunch of features that allow you to create well-structured codebases in a reduced amount of time.

Another important thing to note is Nuxt doesn’t have to be used for SSR. It’s promoted as a framework for creating universal Vue.js applications and includes a command (nuxt generate) for creating static generated Vue applications using the same codebase. So if you’re apprehensive about diving deep into SSR, don’t panic. You can always create a static site instead while still taking advantage of Nuxt’s features.

In order to grasp the potential of Nuxt, let’s create a simple project. The final source code for this project is hosted on GitHub if you want to see it, or you can view a live version created using nuxt generate and hosted on Netlify.

Creating a Nuxt Project

To start off, let’s use a Vue project generator called vue-cli to quickly create a sample project:

# install vue-cli globally
npm install -g vue-cli

# create a project using a nuxt template
vue init nuxt-community/starter-template my-nuxt-project

After going through a couple options, this will create a project inside the folder my-nuxt-project or whatever you specified. Then we just need to install dependencies and run the server:

cd my-nuxt-project
npm install # Or yarn
npm run dev

There we go. Open your browser to localhost:3000 and your project should be running. Not much different from creating a Vue Webpack project. However, when we look at the actual structure of the app, there’s not much there, especially when compared to something like the Vue Webpack template.

Diagram of project directories and their relation to the Nuxt config file

Looking in the package.json also shows that we only have one dependency, Nuxt itself. This is because each version of Nuxt is tailored to work with specific versions of Vue, Vue-router, and Vuex and bundles them all together for you.

There is also a nuxt.config.js file at the project root. This allows you to customize a bunch of features that Nuxt provides. By default, it sets the header tags, loading bar color, and ESLint rules for you. If you’re eager to see what you can configure, here’s the documentation; we will be covering some options in this article.

So what’s so special about those directories?

Project Layout

If you browse through the directories created, all of them have an accompanying Readme stating a brief summary of what goes in that directory and often a link to the docs.

This is one benefit of using Nuxt: a default structure for your application. Any good front-end developer will structure an application similar to this, but there are many different ideas about structures, and when working on a team, some time will inevitably go into discussing or choosing this structure. Nuxt provides one for you.

Nuxt will look for certain directories and build your application for you depending on what it finds. Let’s examine these directories one by one.

Pages

This is the only required directory. Any Vue components in this directory are automatically added to vue-router based on their filenames and the directory structure. This is extremely convenient. Normally I would have a separate Pages directory anyway and have to manually register each of those components in another router file. This router file can become complex for larger projects and may need splitting to maintain readability. Instead, Nuxt will handle all of this logic for you.

To demonstrate, we can create a Vue component called about.vue inside the Pages directory. Let’s just add a simple template such as:

<template>
 <h1>About Page</h1>
</template>

When you save, Nuxt will re-generate the routes for you. Seeing as we called our component about.vue, if you navigate to /about, you should see that component. Simple.

There is one filename which is special. Naming a file index.vue will create a root route for that directory. When the project is generated, there’s already an index.vue component in the pages directory which correlates to the homepage or landing page of your site. (In the development example, this would simply be localhost:3000.)

Nuxt scans the Vue files in the pages directory and outputs the appropriate pages.

What about deeper routes? Sub-directories in the Pages directory help to structure your routes. So if we wanted a View Product page, we could structure our Pages directory something like this:

/pages
--| /products
----| index.vue
----| view.vue

Now, if we navigate to /products/view, we will see the view.vue component inside the products directory. If we navigate instead to /products, we will see the index.vuecomponent inside the products directory.

You may be asking why we didn’t just create a products.vue component in the pages directory instead like we did for the /about page. You may think the result would be the same, but there is a difference between the two structures. Let’s demonstrate this by adding another new page.

Say we wanted a seperate About page for each employee. For example, let’s create an About page for me. It should be located at /about/ben-jones. Initially, we may try structuring the Pages directory like this:

/pages
--| about.vue
--| /about
----| ben-jones.vue

When we try to access /about/ben-jones, we instead get the about.vue component, the same as /about. What’s going on here?

Interestingly, what Nuxt is doing here is generating a nested route. This structure suggests that you want a permanent /about route and anything inside that route should be nested in its own view area. In vue-router, this would be signified by specifying a <router-view />component inside the about.vue component. In Nuxt, this is the same concept except, instead of <router-view />, we simply use <nuxt />. So let’s update our about.vuecomponent to allow for nested routes:

<template>
 <div>
   <h1>About Page</h1>
   <nuxt />
 </div>
</template>

Now, when we navigate to /about, we get the about.vue component we had before, with just a title. However, when we navigate to /about/ben-jones, we instead have the title andthe ben-jones.vue component rendered where the <nuxt/> placeholder was.

This wasn’t what we initially wanted, but the idea of having an About page with a list of people that, when clicked on, fill a section on the page with their information is an interesting concept, so let’s leave it as is for now. If you did want the other option, then all we would do is restructure our directories. We’d just have to move the about.vuecomponent inside the /about directory and rename it index.vue, so the resulting structure would be:

/pages
--| /about
----| index.vue
----| ben-jones.vue

Finally, say we wanted to use route params to retrieve a specific product. For example, we want to be able to edit a product by navigating to /products/edit/64 where 64 is the product_id. We can do this the following way:

/pages
--| /products
----| /edit
------| _product_id.vue

Note the underscore at the beginning of the _product_id.vue component—this signifies a route param which is then accessible on the $route.params object or on the params object in Nuxt’s Context (more on that later). Note that the key for the param will be the component name without the initial underscore—in this case, product_id—so try to keep them unique within the project. As a result, in _product_id.vue, we may have something like:

<template>
 <h1>Editing Product {{ $route.params.product_id }}</h1>
</template>

You can start to imagine more complex layouts, which would be a pain to set up using vue-router. For example, we can combine all of the above into a route such as:

/pages
--| /categories
----| /_category_id
------| products.vue
------| /products
--------| _product_id.vue

It’s not too difficult to reason on what /categories/2/products/3 would display. We would have the products.vue component with a nested _product_id.vue component, with two route params: category_id and product_id. This is much simpler to reason on than an equivalent router config.

While we’re on the topic, one thing I tend to do in the router config is set up router guards. As Nuxt is building the router for us, this can be done instead on the component itself with beforeRouteEnter. If you want to validate route params, Nuxt provides a component method called validate. So if you wanted to check if the product_id was a number before trying to render the component, you would add the following to the script tag of _product_id.vue:

export default {
 validate ({ params }) {
   // Must be a number
   return /^\d+$/.test(params.product_id)
 }
}

Now, navigating to /categories/2/products/someproduct results in a 404 because someproduct isn’t a valid number.

That’s it for the Pages directory. Learning how to structure your routes properly in this directory is essential, so spending a little time initially is important to getting the most out of Nuxt. If you’re looking for a brief overview, it is always helpful to refer to the docs for routing.

If you’re worried about not being in control of the router, don’t be. This default setup works great for a wide variety of projects, provided they are well structured. However, there are some cases where you may need to add more routes to the router than Nuxt automatically generates for you or restructure them. Nuxt provides a way to customize the router instance in the config, allowing you to add new routes and customize generated routes. You can also edit the core functionality of the router instance, including extra options added by Nuxt. So if you do encounter an edge case, you still have the flexibility to find the appropriate solution.

Store

Nuxt can build your Vuex store based on the structure of the store directory, similar to the Pages directory. If you don’t need a store, just remove the directory. There are two modes for the store, Classic and Modules.

Classic requires you to have an index.js file in the store directory. There you need to export a function that returns a Vuex instance:

import Vuex from 'vuex'

const createStore = () => {
 return new Vuex.Store({
   state: ...,
   mutations: ...,
   actions: ...
 })
}

export default createStore

This allows you to create the store however you wish, much like using Vuex in a normal Vue project.

Modules mode also requires you to create an index.js file in the store directory. However, this file only needs to export the root state/mutations/actions for your Vuex store. The example below specifies a blank root state:

export const state = () => ({})

Then, each file in the store directory will be added to the store in its own namespace or module. For example, let’s create somewhere to store the current product. If we create a file called product.js in the store directory, then a name-spaced section of the store will be available at $store.product. Here’s a simple example of what that file may look like:

export const state = () => ({
 _id: 0,
 title: 'Unknown',
 price: 0
})

export const actions = {
 load ({ commit }) {
   setTimeout(
     commit,
     1000,
     'update',
     { _id: 1, title: 'Product', price: 99.99 }
   )
 }
}

export const mutations = {
 update (state, product) {
   Object.assign(state, product)
 }
}

The setTimeout in the load action simulates some sort of API call, which will update the store with the response; in this case, it takes one second. Now, let’s use it in the products/view page:

<template>
 <div>
   <h1>View Product {{ product._id }}</h1>
   <p>{{ product.title }}</p>
   <p>Price: {{ product.price }}</p>
 </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
 created () {
   this.$store.dispatch('product/load')
 },
 computed: {
   ...mapState(['product'])
 }
}
</script>

A few things to note: Here, we are calling our fake API when the component is created. You can see that the product/load action we are dispatching is namespaced under Product. This makes it clear exactly what section of the store we are dealing with. Then, by mapping the state to a local computed property, we can easily use it in our template.

There is a problem: We see the original state for a second while the API runs. Later, we will use a solution provided by Nuxt to fix this (known as fetch).

Just to stress this again, we never had to npm install vuex, as it is already included in the Nuxt package. When you add an index.js file to the store directory, all those methods are then opened up to you automatically.

That’s the main two directories explained; the rest are much simpler.

Components

The Components directory is there to contain your reusable components such as a navigation bar, image gallery, pagination, data tables, etc. Seeing as components in the Pages directory are converted into routes, you need somewhere else to store these types of components. These components are accessible in pages or other components by importing them:

import ComponentName from ~/components/ComponentName.vue

Assets

This contains uncompiled assets and has more to do with how Webpack loads and processes files, rather than with how Nuxt works. If you’re interested, I suggest reading the guide in the Readme.

Static

This contains static files which are mapped to the root directory of your site. For example, putting an image called logo.png in this directory would make it available at /logo.png. This is good for meta files like robots.txt, favicon.ico, and other files you need available.

Layouts

Normally, in a Vue project, you have some sort of root component, normally called App.vue. Here is where you can set up your (normally static) app layout, which may include a navbar, footer, and then a content area for your vue-router. The default layout does exactly that and is provided for you in the layouts folder. Initially, all it has is a div with a <nuxt />component (which is equivalent to <router-view />) but it can be styled however you wish. For example, I’ve added a simple navbar to the example project for navigation around the various demonstration pages.

A layout can be applied to multiple pages.

You may want to have a different layout for a certain section of your app. Maybe you have some sort of CMS or admin panel that looks different. To solve this, create a new layout in the Layouts directory. As an example, let’s create an admin-layout.vue layout which just has an extra header tag and no navbar:

<template>
 <div>
   <h1>Admin Layout</h1>
   <nuxt />
 </div>
</template>

Then, we can create an admin.vue page in the Pages directory and use a property provided by Nuxt called layout to specify the name (as a string) of the layout we want to use for that component:

<template>
 <h1>Admin Page</h1>
</template>

<script>
export default {
 layout: 'admin-layout'
}
</script>

That’s all there is to it. Page components will use the default layout unless specified, but when you navigate to /admin, it now uses the admin-layout.vue layout. Of course, this layout could be shared across several admin screens if you wish. The one important thing to remember is layouts must contain a <nuxt /> element.

There’s one final thing to note about layouts. You may have noticed while experimenting that if you type an invalid URL, you are shown an error page. This error page is, in fact, another layout. Nuxt has its own error layout (source code here), but if you wanted to edit it, just create an error.vue layout and that will be used instead. The caveat here is that the error layout must not have a <nuxt /> element. You will also have access to an errorobject on the component with some basic information to display. (This is printed out in the terminal running Nuxt if you want to examine it.)

Middleware

Middleware are functions that can be executed before rendering a page or layout. There is a variety of reasons you may want to do so. Route guarding is a popular use where you could check the Vuex store for a valid login or validate some params (instead of using the validate method on the component itself). One project I worked on recently used middleware to generate dynamic breadcrumbs based on the route and params.

These functions can be asynchronous; just be careful, as nothing will be shown to the user until the middleware is resolved. They also have access to Nuxt’s Context, which I will explain later.

Plugins

This directory allows you to register Vue plugins before the application is created. This allows the plugin to be shared throughout your app on the Vue instance rather than creating it each time you import it into a component. This helps to keep the bundle sizes small.

Most major plugins have a Nuxt version that can be easily registered to the Vue instance by following their docs. However, there will be circumstances when you will be developing a plugin or need to adapt an existing plugin for this purpose. An example I’m borrowing from the docs shows how to do this for vue-notifications. First, we need to install the package:

npm install vue-notifications --save

Then create a file in the plugins directory called vue-notifications.js and include the following:

import Vue from 'vue'
import VueNotifications from 'vue-notifications'

Vue.use(VueNotifications)

Very similar to how you would register a plugin in a normal Vue environment. Then edit the nuxt.config.js file at your project root and add the following entry to the module.exports object:

plugins: ['~/plugins/vue-notifications']

That’s it. Now you can use vue-notifications throughout your app without increasing the bundle size. An example of this is at /plugin in the example project.

So that completes a rundown of the directory structure. It may seem a lot to learn, but if you’re developing a Vue app, you’re already setting up the same kind of logic. Nuxt helps to abstract away the setup and help you focus on building.

Nuxt does more than assist in development though. It supercharges your components by providing extra functionality.

Nuxt’s Supercharged Components

When I first started researching Nuxt, I kept reading about how Page components are supercharged. It sounded great, but it wasn’t immediately obvious what exactly that meant and what benefits it brings.

What it means is that all Page components have extra methods attached to them that Nuxt can use to provide additional functionality. In fact, we’ve already seen one of these earlier when we used the validate method to check params and redirect a user if they are invalid.

The two main ones used in a Nuxt project will be the asyncData and fetch methods. Both are very similar in concept, they are run asynchronously before the component is generated, and they can be used to populate the data of a component and the store. They also enable the page to be fully rendered on the server before sending it to the client even when we have to wait for some database or API call.

What’s the difference between asyncData and fetch?

  • asyncData is used to populate the data of the Page component. When you return an object, it is then merged with the output of data before rendering.
  • fetch is used to populate the Vuex Store. If you return a promise, Nuxt will wait until it is resolved before rendering.

So let’s put these to good use. Remember earlier on the /products/view page we had a problem where the initial state of the store was being displayed briefly while our fake API call was being made? One way of fixing this is having a boolean stored on the component or in the Store such as loading = true and then displaying a loading component while the API call finishes. Afterward, we would set loading = false and display the data.

Instead, let’s use fetch to populate the Store before rendering. In a new page called /products/view-async, let’s change the created method to fetch; that should work, right?

export default {
 fetch () {
   // Unfortunately the below line throws an error
   // because 'this.$store' is undefined...
   this.$store.dispatch('product/load')
 },
 computed: {...}
}

Here’s the catch: These “supercharged” methods run before the component is created, so this doesn’t point to the component and nothing on it can be accessed. So how do we access the Store here?

The Context API

Of course, there is a solution. On all of Nuxt’s methods, you are provided with an argument (normally the first) containing an extremely useful object called the Context. In this is everything you will need reference to across the app, meaning we don’t need to wait for Vue to create those references on the component first.

I would highly recommend checking out the Context docs to see what is available. Some handy ones are app, where you can access all your plugins, redirect, which can be used to change routes, error to display the error page, and some self-explanatory ones such as routequery, and store.

So, to access the Store, we can destructure the Context and extract the Store from it. We also need to make sure we return a promise so that Nuxt can wait for it to resolve before rendering the component, so we need to make a small adjustment to our Store action too.

// Component
export default {
 fetch ({ store }) {
   return store.dispatch('product/load')
 },
 computed: {...}
}

// Store Action
load ({ commit }) {
 return new Promise(resolve => {
   setTimeout(() => {
     commit('update', { _id: 1, title: 'Product', price: 99.99 })
     resolve()
   }, 1000)
 })
}

You could use async/await or other methods depending on your coding style, but the concept is the same—we’re telling Nuxt to make sure the API call finishes and the Store is updated with the result before trying the render the component. If you try navigating to /products/view-async, you will not see the flash of content where the product is in its initial state.

You can imagine how useful this can be in any Vue app even without SSR. The Context is also available to all middlewares as well as to other Nuxt methods such as NuxtServerInitwhich is a special store action that runs before the Store is initialized (an example of this is in the next section)

Considerations When Using SSR

I’m sure many (myself included) who start using a technology such as Nuxt while treating it like any other Vue project eventually hit a wall where something we know would normally work seems impossible in Nuxt. As more of these caveats are documented, it will be easier to overcome, but the main thing to consider when starting to debug is that the client and server are two separate entities.

When you access a page initially, a request is sent to Nuxt, the server builds as much as possible of that page and the rest of the app, and then the server sends it to you. Then the responsibility is on the client to continue with navigation and load chunks as it needs them.

We want the server to do as much as possible first, but sometimes it doesn’t have access to the information it needs, which results in the work being done client-side instead. Or worse, when the final content presented by the client is different from what the server expected, the client is told to rebuild it from scratch. This is a big indication that something is wrong with the application logic. Thankfully, an error will be generated in your browser’s console (in development mode) if this starts to happen.

Let’s take an example of how to solve a common issue, session management. Imagine you have a Vue app where you can log in to an account, and your session is stored using a token (JWT, for example) which you decide to keep in localStorage. When you initially access the site, you want to authenticate that token against an API, which returns some basic user info if valid and puts that information in the Store.

After reading through Nuxt’s docs, you see that there’s a handy method called NuxtServerInit which allows you to asynchronously populate the Store once on initial load. That sounds perfect! So you create your user module in the Store and add the appropriate action in the index.js file in the Store directory:

export const actions = {
 nuxtServerInit ({ dispatch }) {
   // localStorage should work, right?
   const token = localStorage.getItem('token')
   if (token) return dispatch('user/load', token)
 }
}

When you refresh the page, you get an error, localStorage is not defined. Thinking about where this is happening, it makes sense. This method is run on the server, it has no idea what is stored in localStorage on the client; in fact, it doesn’t even know what “localStorage” is! So that’s not an option.

The server tries to execute localStorage.getItem('token') but throws an error, then a caption below explaining the problem.

So what’s the solution? There are a few, actually. You can get the client to initialize the Store instead but end up losing the benefits of SSR because the client ends up doing all the work. You can set up sessions on the server and then use that to authenticate the user, but that’s another layer to set up. What’s most similar to the localStorage method is using cookies instead.

Nuxt has access to cookies because they are sent with the request from the client to the server. As with other Nuxt methods, nuxtServerInit has access to the Context, this time as the second argument because the first is reserved for the store. On the Context, we can access the req object, which stores all the headers and other information from the client request. (This will be especially familiar if you’ve used Node.js.)

So after storing the token in a cookie instead (called “token,” in this case), let’s access it on the server.

import Cookie from 'cookie'

export const actions = {
 nuxtServerInit ({ dispatch }, { req }) {
   const cookies = Cookie.parse(req.headers.cookie || '')
   const token = cookies['token'] || ''
   if (token) return dispatch('user/load', token)
 }
}

A simple solution, but one that might not be immediately obvious. Learning to think about where certain actions are happening (client, server, or both) and what they have access to takes some time but the benefits are worth it.

Deployment

Deployment with Nuxt is extremely simple. Using the same codebase, you can create an SSR app, single-page application, or static page.

Server-side Rendered App (SSR App)

This is probably what you were aiming for when using Nuxt. The basic concept for deployment here is to run the build process on whatever platform you choose and set a few configurations. I’ll use the Heroku example from the docs:

First, set up scripts for Heroku in package.json:

"scripts": {
 "dev": "nuxt",
 "build": "nuxt build",
 "start": "nuxt start",
 "heroku-postbuild": "npm run build"
}

Then set up the Heroku environment using the heroku-cli (setup instructions here:

# set Heroku variables
heroku config:set NPM_CONFIG_PRODUCTION=false
heroku config:set HOST=0.0.0.0
heroku config:set NODE_ENV=production

# deploy
git push heroku master

That’s it. Now your SSR Vue app is live ready for the world to see. Other platforms have different setups, but the process is similar. The official deployment methods currently listed are:

Single-page Application (SPA)

If you wanted to take advantage of some of the extra features Nuxt provides but avoid the server trying to render pages, then you can deploy as an SPA instead.

First, it’s best to test your application without the SSR as by default npm run dev runs with SSR on. To change that, edit the nuxt.config.js file and add the following option:

mode: 'spa',

Now, when you run npm run dev, SSR will be turned off and the application will run as an SPA for you to test. This setting also makes sure no future builds will include SSR.

If everything looks fine, then deployment is exactly the same as for an SSR app. Just remember you need to set mode: 'spa' first to let the build process know you want an SPA.

Static Pages

If you don’t want to deal with a server at all and instead want to generate pages for use with static hosting services such as Surge or Netlify, then this is the option to choose. Just bear in mind that, without a server, you won’t be able to access the req and res in the Context, so if your code relies on that, be sure to accommodate it. For example, when generating the example project, the nuxtServerInit function throws an error because it’s trying to fetch a token from the cookies in the request headers. In this project, it doesn’t matter, as that data isn’t being used anywhere, but in a real application, there would need to be an alternative way to access that data.

Once that’s sorted, deployment is easy. One thing you will probably need to change first is adding an option so that the nuxt generate command will also create a fallback file. This file will prompt the hosting service to let Nuxt handle the routing rather than the hosting service, throwing a 404 error. To do so, add the following line to nuxt.config.js:

generate: { fallback: true },

Here’s an example using Netlify, which isn’t currently in the Nuxt docs. Just bear in mind that if this is your first time using netlify-cli, you will be prompted to authenticate:

# install netlify-cli globally
npm install netlify-cli -g

# generate the application (outputs to dist/ folder)
npm run generate

# deploy
netlify deploy dist

It’s as simple as that! As mentioned at the beginning of the article, there’s a version of this project here. There’s also official deployment documentation for the following services below:

Learn More

Nuxt is updating rapidly, and this is only a small selection of the features it offers. I hope this article encourages you to try it out and see if it could help improve the capabilities of your Vue applications, allowing you to develop faster and take advantage of its powerful features.

If you’re looking for more information, then look no further than Nuxt’s official links:

Looking to up your JavaScript game? Try reading The Comprehensive Guide to JavaScript Design Patterns by fellow Toptaler Marko Mišura.

This article is written by Raoni Boaventura and originally published at Toptal

What is AngularJS?

AngularJS is a JavaScript MVC framework developed by Google that lets you build well structured, easily testable, and maintainable front-end applications.

And Why Should I Use It?

If you haven’t tried AngularJS yet, you’re missing out. The framework consists of a tightly integrated toolset that will help you build well structured, rich client-side applications in a modular fashion—with less code and more flexibility.

AngularJS extends HTML by providing directives that add functionality to your markup and allow you to create powerful dynamic templates. You can also create your own directives, crafting reusable components that fill your needs and abstracting away all the DOM manipulation logic.

It also implements two-way data binding, connecting your HTML (views) to your JavaScript objects (models) seamlessly. In simple terms, this means that any update on your model will be immediately reflected in your view without the need for any DOM manipulation or event handling (e.g., with jQuery).

Angular provides services on top of XHR that dramatically simplify your code and allow you to abstract API calls into reusable services. With that, you can move your model and business logic to the front-end and build back-end agnostic web apps.

Finally, I love Angular because of its flexibility regarding server communication. Like most JavaScript MVC frameworks, it lets you work with any server-side technology as long as it can serve your app through a RESTful web API. But Angular also provides services on top of XHR that dramatically simplify your code and allow you to abstract API calls into reusable services. As a result, you can move your model and business logic to the front-end and build back-end agnostic web apps. In this post, we’ll do just that, one step at a time.

So, Where Do I Begin?

First, let’s decide the nature of the app we want to build. In this guide, we’d prefer not to spend too much time on the back-end, so we’ll write something based on data that’s easily attainable on the Internet—like a sports feed app!

Since I happen to be a huge fan of motor racing and Formula 1, I’ll use an autosport API service to act as our back-end. Luckily, the guys at Ergast are kind enough to provide a free motorsport API that will be perfect for us.

For a sneak peak at what we’re going to build, take a look at the live demo. To prettify the demo and show off some Angular templating, I applied a Bootstrap theme from WrapBootstrap, but seeing as this article isn’t about CSS, I’ll just abstract it away from the examples and leave it out.

Getting Started Tutorial

Let’s kickstart our example app with some boilerplate. I recommend the angular-seed project as it not only provides you with a great skeleton for bootstrapping, but also sets the ground for unit testing with Karma and Jasmine (we won’t be doing any testing in this demo, so we’ll just leave that stuff aside for now; see Part 2 of this tutorial for more info on setting up your project for unit and end-to-end testing).

EDIT (May 2014): Since I wrote this tutorial, the angular-seed project has gone through some heavy changes (including the additon of Bower as package manager). If you have any doubts about how to deploy the project, take a quick look at the first section of their reference guide. In Part 2 of ths tutorial, Bower, among other tools, is covered in greater detail.

OK, now that we’ve cloned the repository and installed the dependencies, our app’s skeleton will look like this:

angularjs tutorial - start with the skeleton

Now we can start coding. As we’re trying to build a sports feed for a racing championship, let’s begin with the most relevant view: the championship table.

the championship table

Given that we already have a drivers list defined within our scope (hang with me – we’ll get there), and ignoring any CSS (for readability), our HTML might look like:

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
</body>

The first thing you’ll notice in this template is the use of expressions (“{{“ and “}}”) to return variable values. In AngularJS, expressions allow you to execute some computation in order to return a desired value. Some valid expressions would be:

  • {{ 1 + 1 }}
  • {{ 946757880 | date }}
  • {{ user.name }}

Effectively, expressions are JavaScript-like snippets. But despite being very powerful, you shouldn’t use expressions to implement any higher-level logic. For that, we use directives.

Understanding Basic Directives

The second thing you’ll notice is the presence of ng-attributes, which you wouldn’t see in typical markup. Those are directives.

At a high level, directives are markers (such as attributes, tags, and class names) that tell AngularJS to attach a given behaviour to a DOM element (or transform it, replace it, etc.). Let’s take a look at the ones we’ve seen already:

  • The ng-app directive is responsible for bootstrapping your app defining its scope. In AngularJS, you can have multiple apps within the same page, so this directive defines where each distinct app starts and ends.
  • The ng-controller directive defines which controller will be in charge of your view. In this case, we denote the driversController, which will provide our list of drivers (driversList).
  • The ng-repeat directive is one of the most commonly used and serves to define your template scope when looping through collections. In the example above, it replicates a line in the table for each driver in driversList.

Adding Controllers

Of course, there’s no use for our view without a controller. Let’s add driversController to our controllers.js:

angular.module('F1FeederApp.controllers', []).
controller('driversController', function($scope) {
    $scope.driversList = [
      {
          Driver: {
              givenName: 'Sebastian',
              familyName: 'Vettel'
          },
          points: 322,
          nationality: "German",
          Constructors: [
              {name: "Red Bull"}
          ]
      },
      {
          Driver: {
          givenName: 'Fernando',
              familyName: 'Alonso'
          },
          points: 207,
          nationality: "Spanish",
          Constructors: [
              {name: "Ferrari"}
          ]
      }
    ];
});

You may have noticed the $scope variable we’re passing as a parameter to the controller. The $scopevariable is supposed to link your controller and views. In particular, it holds all the data that will be used within your template. Anything you add to it (like the driversList in the above example) will be directly accessible in your views. For now, let’s just work with a dummy (static) data array, which we will replace later with our API service.

Now, add this to app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers'
]);

With this line of code, we actually initialize our app and register the modules on which it depends. We’ll come back to that file (app.js) later on.

Now, let’s put everything together in index.html:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>

Modulo minor mistakes, you can now boot up your app and check your (static) list of drivers.

Note: If you need help debugging your app and visualizing your models and scope within the browser, I recommend taking a look at the awesome Batarang plugin for Chrome.

Loading Data From the Server

Since we already know how to display our controller’s data in our view, it’s time to actually fetch live data from a RESTful server.

To facilitate communication with HTTP servers, AngularJS provides the $http and $resource services. The former is but a layer on top of XMLHttpRequest or JSONP, while the latter provides a higher level of abstraction. We’ll use $http.

To abstract our server API calls from the controller, let’s create our own custom service which will fetch our data and act as a wrapper around $http by adding this to our services.js:

angular.module('F1FeederApp.services', []).
  factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });

With the first two lines, we create a new module (F1FeederApp.services) and register a service within that module (ergastAPIservice). Notice that we pass $http as a parameter to that service. This tells Angular’s dependency injection engine that our new service requires (or depends on) the $http service.

In a similar fashion, we need to tell Angular to include our new module into our app. Let’s register it with app.js, replacing our existing code with:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers',
  'F1FeederApp.services'
]);

Now, all we need to do is tweak our controller.js a bit, include ergastAPIservice as a dependency, and we’ll be good to go:

angular.module('F1FeederApp.controllers', []).
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];

    ergastAPIservice.getDrivers().success(function (response) {
        //Dig into the responde to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  });

Now reload the app and check out the result. Notice that we didn’t make any changes to our template, but we added a nameFilter variable to our scope. Let’s put that variable to use.

Filters

Great! We have a functional controller. But it only shows a list of drivers. Let’s add some functionality by implementing a simple text search input which will filter our list. Let’s add the following line to our index.html, right below the <body> tag:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>

We are now making use of the ng-model directive. This directive binds our text field to the $scope.nameFiltervariable and makes sure that its value is always up-to-date with the input value. Now, let’s visit index.html one more time and make a small adjustment to the line that contains the ng-repeat directive:

<tr ng-repeat="driver in driversList | filter: nameFilter">

This line tells ng-repeat that, before outputting the data, the driversList array must be filtered by the value stored in nameFilter.

At this point, two-way data binding kicks in: every time a value is input in the search field, Angular immediately ensures that the $scope.nameFilter that we associated with it is updated with the new value. Since the binding works both ways, the moment the nameFilter value is updated, the second directive associated to it (i.e., the ng-repeat) also gets the new value and the view is updated immediately.

Reload the app and check out the search bar.

app search bar

Notice that this filter will look for the keyword on all attributes of the model, including the ones we´re not using. Let’s say we only want to filter by Driver.givenName and Driver.familyName: First, we add to driversController, right below the $scope.driversList = []; line:

$scope.searchFilter = function (driver) {
    var keyword = new RegExp($scope.nameFilter, 'i');
    return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName);
};

Now, back to index.html, we update the line that contains the ng-repeat directive:

<tr ng-repeat="driver in driversList | filter: searchFilter">

Reload the app one more time and now we have a search by name.

Routes

Our next goal is to create a driver details page which will let us click on each driver and see his/her career details.

First, let’s include the $routeProvider service (in app.js) which will help us deal with these varied application routes. Then, we’ll add two such routes: one for the championship table and another for the driver details. Here’s our new app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.services',
  'F1FeederApp.controllers',
  'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
  $routeProvider.
	when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}).
	when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}).
	otherwise({redirectTo: '/drivers'});
}]);

With that change, navigating to http://domain/#/drivers will load the driversController and look for the partial view to render in partials/drivers.html. But wait! We don’t have any partial views yet, right? We’ll need to create those too.

Partial Views

AngularJS will allow you to bind your routes to specific controllers and views.

But first, we need to tell Angular where to render these partial views. For that, we’ll use the ng-view directive, modifying our index.html to mirror the following:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp">
  <ng-view></ng-view>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>

Now, whenever we navigate through our app routes, Angular will load the associated view and render it in place of the <ng-view> tag. All we need to do is create a file named partials/drivers.html and put our championship table HTML there. We’ll also use this chance to link the driver name to our driver details route:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>
<table>
<thead>
  <tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
  <tr ng-repeat="driver in driversList | filter: searchFilter">
    <td>{{$index + 1}}</td>
    <td>
      <img src="img/flags/{{driver.Driver.nationality}}.png" />
      <a href="#/drivers/{{driver.Driver.driverId}}">
	  	{{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
	  </a>
	</td>
    <td>{{driver.Constructors[0].name}}</td>
    <td>{{driver.points}}</td>
  </tr>
</tbody>
</table>

Finally, let’s decide what we want to show in the details page. How about a summary of all the relevant facts about the driver (e.g., birth, nationality) along with a table containing his/her recent results? To do that, we add to services.js:

angular.module('F1FeederApp.services', [])
  .factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverDetails = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverRaces = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });

This time, we provide the driver’s ID to the service so that we retrieve the information relevant solely to a specific driver. Now, we modify controllers.js:

angular.module('F1FeederApp.controllers', []).

  /* Drivers controller */
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];
    $scope.searchFilter = function (driver) {
        var re = new RegExp($scope.nameFilter, 'i');
        return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);
    };

    ergastAPIservice.getDrivers().success(function (response) {
        //Digging into the response to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  }).

  /* Driver controller */
  controller('driverController', function($scope, $routeParams, ergastAPIservice) {
    $scope.id = $routeParams.id;
    $scope.races = [];
    $scope.driver = null;

    ergastAPIservice.getDriverDetails($scope.id).success(function (response) {
        $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; 
    });

    ergastAPIservice.getDriverRaces($scope.id).success(function (response) {
        $scope.races = response.MRData.RaceTable.Races; 
    }); 
  });

The important thing to notice here is that we just injected the $routeParams service into the driver controller. This service will allow us to access our URL parameters (for the :id, in this case) using $routeParams.id.

Now that we have our data in the scope, we only need the remaining partial view. Let’s create a file named partials/driver.html and add:

<section id="main">
  <a href="./#/drivers"><- Back to drivers list</a>
  <nav id="secondary" class="main-nav">
    <div class="driver-picture">
      <div class="avatar">
        <img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" />
        <img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/>
        {{driver.Driver.givenName}} {{driver.Driver.familyName}}
      </div>
    </div>
    <div class="driver-status">
      Country: {{driver.Driver.nationality}}   <br/>
      Team: {{driver.Constructors[0].name}}<br/>
      Birth: {{driver.Driver.dateOfBirth}}<br/>
      <a href="{{driver.Driver.url}}" target="_blank">Biography</a>
    </div>
  </nav>

  <div class="main-content">
    <table class="result-table">
      <thead>
        <tr><th colspan="5">Formula 1 2013 Results</th></tr>
      </thead>
      <tbody>
        <tr>
          <td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td>
        </tr>
        <tr ng-repeat="race in races">
          <td>{{race.round}}</td>
          <td><img  src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td>
          <td>{{race.Results[0].Constructor.name}}</td>
          <td>{{race.Results[0].grid}}</td>
          <td>{{race.Results[0].position}}</td>
        </tr>
      </tbody>
    </table>
  </div>

</section>

Notice that we’re now putting the ng-show directive to good use. This directive will only show the HTML element if the expression provided is true (i.e., neither false, nor null). In this case, the avatar will only show up once the driver object has been loaded into the scope by the controller.

Finishing Touches

Add in a bunch of CSS and render your page. You should end up with something like this:

page rendered with CSS

You’re now ready to fire up your app and make sure both routes are working as desired. You could also add a static menu to index.html to improve the user’s navigation capabilities. The possibilities are endless.

EDIT (May 2014): I’ve received many requests for a downloadable version of the code that we build in this tutorial. I’ve therefore decided to release it here (stripped of any CSS). However, I really do not recommend downloading it, since this guide contains every single step you need to build the same application with your own hands, which will be a much more useful and effective learning exercise.

This article is written by Raoni Boaventura and originally published at Toptal

About the author:  Raoni is an experienced software developer and who has contributed to a wealth of projects using Ruby on Rails, JavaScript, and PHP on top of many other programming languages and frameworks. He is an excellent problem solver, and a great communicator as both a team member and a team lead.