Implementing AngularJS

Now it's time to implement most of our view logic by implementing a more robust AngularJS application. The first thing we need to do is to add AngularJS code to our dashboard.ejs view template:

<!DOCTYPE html> 
<html ng-app> 
<head> 
    <title>Dashboard for <%= user.firstName %> <%= user.lastName %> </title> 
 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script> 
 
 
</head> 
<body> 
<nav class="nav navbar-default"> 
    <div class="container-fluid"> 
        <div class="navbar-header"> 
            <a class="navbar-brand" href="#"><%= user.firstName %> <%= user.lastName %> Dashboard</a> 
        </div> 
    </div> 
</nav> 
 
 
<div class="container"> 
    <div class="row"> 
        <div class="col-xs-12 col-md-6"> 
            <h2>My Lists</h2> 
            <button class="btn btn-primary"> 
                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 
                Add List</button> 
            <ul class="list-unstyled"> 
            <% giftlists.forEach(function(giftlist, index){ -%> 
                <li><a class="btn btn-link" href="#" role="button"><%= giftlist.name %></a></li> 
            <% }); %> 
            </ul> 
        </div> 
 
        <div class="col-xs-12 col-md-6"> 
            <h2>Lists Shared With Me</h2> 
        </div> 
    </div> 
</div> 
 
</body> 
</html> 

We linked to AngularJS version 1.4.8 on a CDN, as well as a plugin called UI-router. We'll be talking about UI-router in depth. We also added the AngularJS directive ng-app to the opening html tag. When AngularJS loads, it looks for this directive to see what part of the document it should manage. Most applications will have Angular manage from the top level by doing this, though one could Bootstrap AngularJS into any part of the document.

Our AngularJS module

AngularJS packages applications, parts of applications, and dependencies using modules. Everything we're going to do with AngularJS is going to be done by using modules or using code, such as controllers, which have been added to modules.

This is a core part of AngularJS architecture. Modules are containers for the parts of your application, and allow AngularJS to properly Bootstrap your application.

For now, our module is going to be simple, then we'll add to it as we go on. Create a new file called giftapp.js inside public/javascripts:

var giftAppModule = angular.module('giftAppModule', ['ui.router']); 

We create our module by invoking the angular.module() function. The first argument is the name of the module. The second argument is an array containing a list of dependencies we want to inject into our module. In this case, the only one we're injecting at the moment is UI-router.

Now, we need to add our module to our dashboard.ejs template:

<!DOCTYPE html> 
<html ng-app="giftAppModule"> 
<head>  
    <title>Dashboard for <%= user.firstName %> <%= user.lastName %> </title> 
 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script> 
 
 
</head> 
<body> 
<nav class="nav navbar-default"> 
    <div class="container-fluid"> 
        <div class="navbar-header"> 
            <a class="navbar-brand" href="#"><%= user.firstName %> <%= user.lastName %> Dashboard</a> 
        </div> 
    </div> 
</nav> 
 
 
<div class="container"> 
    <div class="row"> 
        <div class="col-xs-12 col-md-6"> 
            <h2>My Lists</h2> 
            <button class="btn btn-primary"> 
                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 
                Add List</button> 
            <ul class="list-unstyled"> 
            <% giftlists.forEach(function(giftlist, index){ -%> 
                <li><a class="btn btn-link" href="#" role="button"><%= giftlist.name %></a></li> 
            <% }); %> 
            </ul> 
        </div> 
 
        <div class="col-xs-12 col-md-6"> 
            <h2>Lists Shared With Me</h2> 
        </div> 
    </div> 
</div> 
 
<script src="/javascripts/giftapp.js"></script> 
</body> 
</html> 

We simply load our module using a normal script tag. Also, we change the ng-app directive so that it will use our new module as the main application entry point for the page.

Controlling state with UI-router

State can mean a lot of things in applications, but in our SPA it refers to a given set of views, controllers, and data that can be invoked using a URL changer. By far the most popular way for developers to handle state in their AngularJS applications is with a plugin called UI-router.

The UI-router plugin allows us to control state rather elegantly, and is extremely flexible.

Let's implement UI-router in our application. First, we will reference UI-router from a CDN in our dashboard.ejs template:

<!DOCTYPE html> 
<html ng-app="giftapp"> 
<head > 
    <title>Dashboard for <%= user.firstName %> <%= user.lastName %> </title> 
 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script> 
 
 
</head> 
<body> 
<nav class="nav navbar-default"> 
    <div class="container-fluid"> 
        <div class="navbar-header"> 
            <a class="navbar-brand" href="#"><%= user.firstName %> <%= user.lastName %> Dashboard</a> 
        </div> 
    </div> 
</nav> 
 
 
<div class="container"> 
 
    <div ui-view></div> 
 
 
</div> 
 
 
<script src="/javascripts/giftapp.js"></script> 
</body> 
</html> 

We linked to UI-router on a CDN and loaded it using a normal script tag. The other major change in our template is the addition of a ui-view directive implemented as an attribute on a div element. The ui-view directive tells UI-router where to load the views that it's going to be painting.

The next step is to edit our giftapp.js application file to add routing:

angular.module('giftapp', ['ui.router']) 
 
    .config( 
        ['$stateProvider', '$urlRouterProvider', 
            function ($stateProvider,   $urlRouterProvider) { 
 
                $urlRouterProvider 
                    .otherwise('/dash'); 
 
                $stateProvider 
 
                    .state('dash', { 
                        url:'/dash', 
                        templateUrl: '/templates/dash-main.tpl.html' 
                    }) 
                    .state('add', { 
                        url:'/add', 
                        templateUrl: '/templates/dash-add.tpl.html' 
                    }); 
 
            }]); 

First, we make sure to inject the ui.router module into our module. We chain a config function onto our module declaration. Using an array notation, we inject $stateProvider and $urlRouteprovider into the config function.

Inside that function, the magic happens. First, we invoke $urlRouterProvider.otherwise('/dash');, which sets the default route. When we load our page, unless another route is triggered with a URL fragment, #/dash will be appended to the URL.

Next, we set up two states on $stateProvider. For now, each is named and has a URL and templateURL property. The template URL points to a URL for a visual template to load.

Let's mock up our two templates. Create a new directory at public/templates.

Here's our dash-main.tpl.html:

<div class="row"> 
    <div class="col-xs-12 col-md-6"> 
        <h2>My Lists</h2> 
        <a class="btn btn-primary" role="button" ui-sref="add" href="#/add"> 
            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 
            Add List</a> 
        <ul class="list-unstyled"> 
 
            <li><a class="btn btn-link" href="#" role="button">Angular Router List</a></li> 
            <li><a class="btn btn-link" href="#" role="button">Angular Router List 2</a></li> 
        </ul> 
    </div> 
 
    <div class="col-xs-12 col-md-6"> 
        <h2>Lists Shared With Me</h2> 
    </div> 
</div> 

And this is our dash-add.tpl.html file:

<div class="row"> 
    <div class="col-md-12"> 
        <h2>Add a new list</h2> 
        <form class="form-horizontal"> 
            <div class="form-group"> 
                <label for="listname" class="col-sm-2 control-label">List Name</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" id="listname" placeholder="Name"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <label for="item[]" class="col-sm-2 control-label">Item 1</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" id="Item[]"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <label for="item[]" class="col-sm-2 control-label">Item 1</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" id="Item[]"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <label for="item[]" class="col-sm-2 control-label">Item 1</label> 
                <div class="col-sm-10"> 
                    <input type="text" class="form-control" id="Item[]"> 
                </div> 
            </div> 
            <div class="form-group"> 
                <div class="col-sm-offset-2 col-sm-10"> 
                    <a href="#/dash" class="btn btn-default"> 
                    Save 
                    </a> 
                </div> 
            </div> 
        </form> 
 
    </div> 
 
</div> 

Here, we've mocked up a form that could be used to add a new list. We'll flesh it out later, and actually connect it to the backend to store data.

AngularJS controllers

Right now, our templates are basically just dumb HTML, the AngularJS method of linking our DOM to data and functionality. Controllers contain business logic, but should not be used to manipulate the DOM directly.

In using UI-router, we can easily attach controllers to states, making their $scope available to our views.

Create a new controllers folder inside public/javascripts. Create a new JavaScript file called dashMainController.js:

angular.module('giftappControllers',[]) 
    .controller('DashMainController', ['$scope', function($scope) { 
        $scope.lists = [{'name':'Christmas List'}, {'name':'Birthday List'}]; 
    }]); 

We create a new module called giftAppControllers that takes no dependencies. Then, we build a controller called DashMainController. Using an array notation, we inject $scope and then declare a constructor function.

Inside that function we attach a lists array to $scope, which will make it available to any view that references this controller.

Next, we need to load that file into the dashboard.ejs view template:

<!DOCTYPE html> 
<html ng-app="giftapp"> 
<head > 
    <title>Dashboard for <%= user.firstName %> <%= user.lastName %> </title> 
 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"> 
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script> 
 
 
</head> 
<body> 
<nav class="nav navbar-default"> 
    <div class="container-fluid"> 
        <div class="navbar-header"> 
            <a class="navbar-brand" href="#"><%= user.firstName %> <%= user.lastName %> Dashboard</a> 
        </div> 
    </div> 
</nav> 
 
 
<div class="container"> 
 
    <div ui-view></div> 
 
    <!-- div class="row"> 
        <div class="col-xs-12 col-md-6"> 
            <h2>My Lists</h2> 
            <a class="btn btn-primary" role="button" ui-sref="add"> 
                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 
                Add List</a> 
            <a class="btn btn-primary" role="button" ui-sref="dash"> 
                <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 
                Add List</a> 
            <ul class="list-unstyled"> 
            <% giftlists.forEach(function(giftlist, index){ -%> 
                <li><a class="btn btn-link" role="button"><%= giftlist.name %></a></li> 
            <% }); %> 
            </ul> 
        </div> 
 
        <div class="col-xs-12 col-md-6"> 
            <h2>Lists Shared With Me</h2> 
        </div> 
    </div --> 
 
</div> 
 
 
<script src="/javascripts/giftapp.js"></script> 
<script src="/javascripts/controllers/dashMainController.js"></script> 
</body> 
</html> 

You'll note that you can load the controller module after your main module.

Next, we need to edit our main giftapp.js module to use the new controller as part of a route:

angular.module('giftapp', ['ui.router', 'giftappControllers' ]) 
 
    .config( 
        ['$stateProvider', '$urlRouterProvider', 
            function ($stateProvider, $urlRouterProvider) { 
 
                $urlRouterProvider 
                    .otherwise('/dash'); 
 
                $stateProvider 
 
                    .state('dash', { 
                        url:'/dash', 
                        templateUrl: '/templates/dash-main.tpl.html', 
                        controller: 'DashMainController' 
                    }) 
                    .state('add', { 
                        url:'/add', 
                        templateUrl: '/templates/dash-add.tpl.html', 
 
                    }); 
 
            }]); 

The first thing we do is to inject our new controller module into our giftapp module. This makes the DashMainController available in the module. We then set its name, as a string, to the controller property on our dash state.

The last thing we should do is to modify our template to take advantage of our new controller. Any methods or properties added to $scope in a controller become available inside the view.

Here's our new dash-main.tpl.html:

<div class="row"> 
    <div class="col-xs-12 col-md-6"> 
        <h2>My Lists</h2> 
        <a class="btn btn-primary" role="button" ui-sref="add" href="#/add"> 
            <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> 
            Add List</a> 
        <ul class="list-unstyled"> 
            <li ng-repeat="list in lists"><a class="btn btn-link" href="#" role="button">{{ list.name }}</a></li> 
 
        </ul> 
    </div> 
 
    <div class="col-xs-12 col-md-6"> 
        <h2>Lists Shared With Me</h2> 
    </div> 
</div> 

Instead of canned list items, we rely on the ng-repeat directive (provided by AngularJS itself). The ng-repeat directive iterates over things that are iterable - in this case an array called list. For each member of the array, the directive will paint a li element, assigning the instance to the variable list (essentially creating a new scope). Since our list objects all have name properties, we can access this in a markup expression with {{list.name}}.

Making sure our database and server are running, refreshing our dashboard should look like this:

AngularJS controllers

Christmas List and Birthday List are coming from $scope in our new controller. Clicking on the Add List button takes us to our add state and the page suddenly looks like this:

AngularJS controllers

So now we have the essence of a single page web application working. We have a model, views, and a controller. We have a method of managing state.

In mocking up this functionality, we did remove the connection to the database. So let's add that back in the AngularJS way.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.154.64