Single-Page Applications With ngRoute and ngAnimate in AngularJS

Single-page applications allow you to refresh a certain portion of a web-page by routing any content stored in a separate .html file. By doing so, you do not reload your main page.

Single-Page Applications With ngRoute and ngAnimate in AngularJS

AngularJS provides a module named ngRoute exactly for this purpose.

Another useful module of AngularJS is ngAnimate, which makes it easy to animate with certain CSS classes.

In this tutorial I will try to explain each step thoroughly, although you still need a basic knowledge of AngularJS in order to be able to follow.

Starting With a Main Page

Basic Structure

This index.html file is going to be our main page where we have both fixed and routed content.

I will start with a basic HTML document and include all the necessary libraries along with our custom stylesheet named style.css and a JavaScript file angularApp.js.

<html>
<head>
    <link href="style.css" rel="stylesheet">
</head>
<body>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script>

<script src="angularApp.js"></script>

</body>
</html>

Now I add two DIVs with id names of fixedContent and routedContent inside a mainWrapper DIV.

routedContent is also wrapped inside another DIV named wrapper. That is because the routedContent should be absolute positioned relative to a parent DIV due to the fact that during routing animation, two different pieces of content clash with each other.

<html>
<head>
    <link href="style.css" rel="stylesheet">
</head>
<body>
<div id="mainWrapper">
    <div id="fixedContent"></div>
    <div id="wrapper">
        <div id="routedContent" ng-view></div>
    </div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script>

<script src="angularApp.js"></script>

</body>
</html>

As the id names imply, fixedContent will be the static content of our main page, and routedContent will be dynamically changing upon user interaction.

In order to define an Angular app in our HTML file, we need to use the ng-app directive. Since the whole page will be an Angular app, we need to assign this directive to the mainWrapper DIV.

We also need the ng-view directive, which tells the DIV it is assigned to display the routed page content.

Now our index.html file looks like this:

<html>
<head>
    <link href="style.css" rel="stylesheet">
</head>
<body>
<div id="mainWrapper" ng-app="mainApp">
    <div id="fixedContent"></div>
    <div id="wrapper">
        <div id="routedContent" ng-view></div>
    </div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script>

<script src="angularApp.js"></script>

</body>
</html>

Navigation Menu

We need a navigation menu in order to route different pieces of content to ng-view.

We are going to use the ul and a elements to create a simple horizontal menu. Below you can see the HTML snippet for the menu structure.

<div id="fixedContent">
    <ul>
        <a href="#page1">Page1</a>
        <a href="#page2">Page2</a>
        <a href="#page3">Page3</a>
        <a href="#page4">Page4</a>
    </ul>
</div>

By default, the ng-route module uses the ! prefix. However, here we only use # in front of our pages to be routed. This is done with the hashPrefix attribute used in the configuration, which I’ll explain later in the related section. For now, take it as it is.

Our final HTML file is as follows:

<html>
<head>
    <link href="style.css" rel="stylesheet">
</head>
<body>
<div id="mainWrapper" ng-app="mainApp">
    <div id="fixedContent">
        <ul>
            <a href="#page1">Page1</a>
            <a href="#page2">Page2</a>
            <a href="#page3">Page3</a>
            <a href="#page4">Page4</a>
        </ul>
    </div>
    <div id="wrapper">
        <div id="routedContent" ng-view></div>
    </div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.js"></script>

<script src="angularApp.js"></script>

</body>
</html>

Styling the Main Page

Since this tutorial focuses on AngularJS, I am not going to detail the CSS styling. If you have former CSS knowledge, style the page as you wish. Otherwise, you can use the styling I provide below.

html, body{
    margin: 0;
    padding: 0;
}

#mainWrapper{
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 50px
}

#fixedContent{
    margin-bottom: 50px;
}

#wrapper{
    width: 350px;
}

#routedContent{
    width: 350px;
    position: absolute;
}

ul{
    display: flex;
    justify-content: space-between;
    width: 350px;
    margin: 0;
    padding: 0;
}

a{
    text-decoration: none;
    color: #FFFFFF;
    font-family: Arial;

    list-style: none;
    background-color: #cecece;
    padding: 7px 10px;
    border-radius: 2px;
}

Pages to Be Routed

Each page that will be routed to DIV with the ng-view directive inside the main HTML file can have a unique HTML structure and CSS styling.

Let’s start with page1.html.

Since we want a specific styling for each page, we need separate CSS files for each page. Therefore, we also create a file named page1.css, which will contain the styling rules of page1.html.

The basic HTML structure for page1 is as follows:

<link href="page1.css" rel="stylesheet">

<div id="page1">
    <h1>Page 1</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</div>

At the top, we linked to the CSS file that will be styling the page, and we declared a DIV with id name of page1, where the whole content will be laid.

I will keep it simple, but it is completely up to you how to structure the HTML file. Just bear in mind that your container will always be the DIV to which the ng-view directive is assigned. So everything in your routed pages will be relative to that DIV.

The styling of page1.html is given below:

#page1{
    font-family: Arial;
}

h1{
    color: #ffa42a;
}

The other three pages can be totally different, but for the sake of simplicity I am just using the same template for each HTML page and slightly different CSS files (different h1 text-colors).

page2.html & page2.css

<link href="page2.css" rel="stylesheet">

<div id="page2">
    <h1>Page 2</h1>
    <p>Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
</div>
#page2{
    font-family: Arial;
}

h1{
    color: cornflowerblue;
}

page3.html & page3.css

<link href="page3.css" rel="stylesheet">

<div id="page3">
    <h1>Page 3</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</div>
#page3{
    font-family: Arial;
}

h1{
    color: #b2ce6f;
}

page4.html & page4.css

<link href="page4.css" rel="stylesheet">

<div id="page4">
    <h1>Page 4</h1>
    <p>Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
</div>
#page4{
    font-family: Arial;
}

h1{
    color: #ff4517;
}

Setting the ngRoute & ngAnimate in JavaScript

So far we have completed all the necessary HTML and CSS files. Now it is time to write the JavaScript code that controls the routing and animation.

Since our ng-app directive is named mainApp, we use this id in the module function. We also need to include the ngRoute and ngAnimate dependencies.

mainAngular = angular.module('mainApp',['ngRoute', 'ngAnimate']);

Now we have access to $routeProvider and $locationProvider.

We are going to use the $routeProvider to manage the routings and $locationProvider to change the hashPrefix, which is set to ! by default.

We use .when('/page1', {templateUrl: 'page1.html'}) to define the page to be routed when <a href="#page1">Page1</a> is clicked in our main HTML file.

We repeat the same line of code for each page to be routed. At the end, we use .otherwise({redirectTo: '/page1'}), which handles unexpected page names. If you try to visit an undefined page name, say page5, you will be redirected to page1.

The complete JavaScript code is below:

var mainAngular = angular.module('mainApp',['ngRoute', 'ngAnimate']);

mainAngular.config(function ($routeProvider, $locationProvider) {
    $routeProvider
        .when('/page1',{
            templateUrl: 'page1.html'
        })
        .when('/page2',{
            templateUrl: 'page2.html'
        })
        .when('/page3',{
            templateUrl: 'page3.html'
        })
        .when('/page4',{
            templateUrl: 'page4.html'
        })
        .otherwise({
            redirectTo: '/page1'
        });

    $locationProvider.hashPrefix('');

});

Extra Note: If you wish to add a specific ng-controller directive for any pages to be routed, you can handle this inside the $routeProvider.

An example for page1:

.when('/page1',{
    templateUrl: 'page1.html',
    controller: 'page1Controller'
})

In the end, our page should look like this, and you should be able to navigate between pages with no transition animations.

Single-Page Applications With ngRoute and ngAnimate in AngularJS

Animating the Page Transitions

Now it is time to animate the route transitions.

For animation purposes, AngularJS has built-in CSS classes thanks to the ngAnimate dependency.

Those classes that we are going to use are:

  • ng-enter: The starting CSS styles for the enter animation.
  • ng-enter-active: The finishing CSS styles for the enter animation.
  • ng-leave: The starting CSS styles for the leave animation.
  • ng-leave-active: The finishing CSS styles for the leave animation.

So the routed content which is coming into the main page has a transition from ng-enter to ng-enter-active. Likewise, the content leaving the main page has a transition from ng-leave to ng-leave-active.

We have to attach the above mentioned classes to our routedContent class.

An example transition is given below. You can either design your own transition animations or use this one inside your style.css file.

#routedContent.ng-enter{
    transform: translateX(-500px);
    opacity: 0;

    -webkit-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    -moz-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    -ms-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    -o-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    transition: all 0.35s cubic-bezier(1,.01,0,.99);
}

#routedContent.ng-enter-active{
    transform: translateX(0px);
    opacity: 1;
}

#routedContent.ng-leave{
    transform: translateX(0);
    opacity: 1;

    -webkit-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    -moz-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    -ms-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    -o-transition: all 0.35s cubic-bezier(1,.01,0,.99);
    transition: all 0.35s cubic-bezier(1,.01,0,.99);
}

#routedContent.ng-leave-active{
    transform: translateX(500px);
    opacity: 0;
}

Below is the final result of the project on Plunker.

Conclusion

In this tutorial, we covered how to create SPA applications with the ng-route module of AngularJS, and then we animated the transitions through the CSS classes of ng-animate.

By using only four CSS classes provided by ng-animate, you can achieve various animations. You can always attach extra classes to have more control over the transition animations. For example, you can make your page transitions direction-aware.

I also mentioned that by attaching the controller directive to each specific page inside the $routeProvider, you can achieve extra control over each page.

JavaScript, with its libraries such as Angular, has become one of the de facto languages of working on the web. It’s not without its learning curves, and there are plenty of frameworks and libraries to keep you busy, as well. If you’re looking for additional resources to study or to use in your work, check out what we have available on ThemeKeeper Market.

I hope this tutorial gave you an overall idea of how to utilize the ng-route and ng-animate modules together.