Startseite Learning Web Development with Bootstrap and AngularJS

Learning Web Development with Bootstrap and AngularJS

Jahr: 2015
Edition: Paperback
Verlag: Packt Publishing Limited
Sprache: english
Seiten: 224
ISBN 10: 1783287551
ISBN 13: 9781783287550
File: EPUB, 3.91 MB
 
You can write a book review and share your experiences. Other readers will always be interested in your opinion of the books you've read. Whether you've loved the book or not, if you give your honest and detailed thoughts then people will find new books that are right for them.
Table of Contents


Learning Web Development with Bootstrap and AngularJS

Credits

About the Author

About the Reviewers

www.PacktPub.com

Support files, eBooks, discount offers, and more

Why subscribe?

Free access for Packt account holders

Preface

What this book covers

What you need for this book

Who this book is for

Conventions

Reader feedback

Customer support

Downloading the example code

Errata

Piracy

Questions

1. Hello, {{name}}

Setting up

Installing AngularJS and Bootstrap

Installing Bootstrap

Installing AngularJS

Using AngularJS

Bootstrap

Self-test questions

Summary

2. Let's Build with AngularJS and Bootstrap

Setting up

Scaffolding

Navigation

Getting to grips with Bootstrap's grid

Helper classes

Floats

Center elements

Show and hide

Working with directives

ng-click and ng-mouseover

ng-init

ng-show and ng-hide

ng-if

ng-repeat

ng-class

ng-style

ng-cloak

Self-test questions

Summary

3. Filters

Applying a filter from the view

Currency and numbers

Lowercase and uppercase

limitTo

Date

Filter

orderBy

JSON

Applying filters from JavaScript

Building your own filter

Modules

Creating a module

Creating a filter

Self-test questions

Summary

4. Routing

Installing ngRoute

Creating basic routes

Routes with parameters

The fallback route

HTML5 routing or removing #

Enabling HTML5Mode

Linking routes

Self-test questions

Summary

5. Building Views

Populating the Index view

Populating the Add Contact view

Horizontal forms

Populating the View Contact view

Title and Gravatar

The form-horizontal class

Self-test questions

Summary

6. CRUD

Read

Sharing data between views

Sharing data using $rootScope

Creating a custom service

Value

Factory

Service

Rolling our own service

Using route parameters

Creating a custom directive

Respecting line-endings

Search and adding the active page class

Search

The active page class

Create

Update

Scope

Controller

Piecing it together

Delete

Self-test questions

Summary

7. AngularStrap

Installing AngularStrap

Using AngularStrap

The modal window

Tooltip

Popover

Alert

Utilizing AngularStrap's services

Integrating AngularStrap

Self-test questions

Summary

8. Connecting to the Server

Connecting with $http

Posting data

Connecting with ngResource

Including ngResource

Configuring ngResource

Getting from the server

Posting to the server

Deleting contacts

Error handling

Alternative ways of connecting

RestAngular

Using RestAngular

Firebase

Self-test questions

Summary

9. Using Task Runners

Installing Node and NPM

Utilizing Grunt

Installing the command-line interface

Installing Grunt

Creating a package.json file

Building the Gruntfile.js file

Running Grunt

Setting up watch

Creating the default task

Utilizing gulp

Installing gulp globally

Installing gulp dependencies

Setting up the gulpfile

Restructuring our project

Self-test questions

Summary

10. Customizing Bootstrap

Compiling Less with Grunt or gulp

Downloading the source

Compiling with Grunt

Setting up Watch and LiveReload

Compiling with gulp

Setting up Watch and LiveReload

Less 101

Importing

Variables

Nested rules

Mixins

Customizing Bootstrap's styles

Typography

navbar

Forms

Buttons

The Bootstrap themes

Where to find additional Bootstrap themes

Self-test questions

Summary

11. Validation

Form validation

Pattern validation

Using minlength, maxlength, min, and max

Creating a custom validator

Self-test questions

Summary

12. Community Tools

Batarang

Installing Batarang

Inspecting the scope and properties

Monitoring performance

Visualizing dependencies

Batarang options

ng-annotate

Installing ng-annotate

Using ng-annotate with Grunt

Configuring the task

Hooking into our watch task

Using ng-annotate with gulp

Self-test questions

Summary

A. People and Projects to watch

Bootstrap projects and people

The core team

Bootstrap Expo

BootSnipp

Code guide by @mdo

Roots

Shoelace

Bootstrap 3 snippets for Sublime Text

Font Awesome

Bootstrap Icons

AngularJS projects and people

The core team

RestAngular

AngularStrap and AngularMotion

AngularUI

Mobile AngularUI

Ionic

AngularGM

Now it's your turn…

B. Where to Go for Help

The official documentation

GitHub issues

Stack Overflow

The AngularJS Google group

Egghead.io

Twitter

C. Self-test Answers

Chapter 1

Chapter 2

Chapter 3

Chapter 4

Chapter 5

Chapter 6

Chapter 7

Chapter 8

Chapter 9

Chapter 10

Chapter 11

Chapter 12

Index





Learning Web Development with Bootstrap and AngularJS





* * *





Learning Web Development with Bootstrap and AngularJS





Copyright © 2015 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

First published: May 2015

Production reference: 1260515

Published by Packt Publishing Ltd.

Livery Place

35 Livery Street

Birmingham B3 2PB, UK.

ISBN 978-1-78328-755-0

www.packtpub.com





Credits





Author

Stephen Radford

Reviewers

Tasos Bekos

Jack Hsu

Ole B. Michelsen

Jurgen Van de Moere

Commissioning Editor

Rebecca Youé

Acquisition Editor

Rebecca Youé

Content Development Editor

Manasi Pandire

Technical Editor

Namrata Patil

Copy Editors

Puja Lalwani

Laxmi Subramanian

Project Coordinator

Shipra Chawhan

Proofreaders

Stephen Copestake

Safis Editing

Indexer

Tejal Soni

Graphics

Jason Monteiro

Production Coordinator

Manu Joseph

Cover Work

Manu Joseph





About the Author





Stephen Radford is a full-stack web developer based in the heart of Leicester, England. Originally from Bristol, Stephen moved to Leicester after studying graphic design in college to accept a job at one of UK's largest online marketing companies.

While working at a number of agencies, Stephen developed several side projects, including FTPloy, a SaaS designed to make continuous deployment available to everyone. The project was subsequently a finalist in the .Net Awards Side Project of the Year category.

He and his business partner now run Cocoon, a web development company that builds and maintains web apps such as FTPloy and Former. Cocoon also works closely with a handful of startups and businesses to develop ideas into websites and apps.



I'd like to thank a few people who supported me during the writing of this book. First of all, my partner, Declan. He's been incredibly supportive and I couldn't ask for anyone better in my life. Paul Mckay was the first person I showed the book to and he even helped me write my bio, because for some reason I find writing about myself extremely difficult. And of course, I'd like to thank my parents. My dad has been patiently awaiting his print copy of the book, so hopefully, it's now pride of place on their coffee table.





About the Reviewers





Tasos Bekos is a software engineer and has been working with web technologies for over a decade. He has worked as a freelance developer and consultant for some of the biggest international telecom and financial companies. He is currently working as a frontend architect for ZuluTrade, where he makes use of the latest in frontend technologies. He has deep knowledge of AngularJS and is active in the open source community as a major contributor to the AngularUI Bootstrap project. When not coding, Tasos spends time playing with his two sons.

Jack Hsu is a web developer specializing in frontend tools and technologies. He is the lead frontend developer at Nulogy, bringing his JavaScript and AngularJS expertise to the team. Prior to joining Nulogy, he worked at a variety of companies, including The Globe & Mail, Ontario Institute of Cancer Research, and Wave Accounting. During his spare time, Jack can be found playing video games, experiencing the diverse neighborhoods of Toronto, or travelling the world. You can find an assortment of programming-related posts on his personal blog.

Ole B. Michelsen has been working with full-stack web development for more than 12 years, and has completed his BSc in computer science from DIKU, University of Copenhagen. In recent years, he has specialized in frontend JavaScript development, with particular emphasis on WebRTC and single-page app frameworks.

Jurgen Van de Moere was born in 1978, grew up in Evergem, Belgium, with his parents, sister, and pets. At the age of 6, he started helping his dad, who owned a computer shop, with assembling computers for clients. While his friends were playing computer games, Jurgen was rather fascinated by writing custom scripts and programs to solve problems that his dad's clients were dealing with. After graduating in Latin-Mathematics from Sint-Lievens college in Ghent, Jurgen continued his education at University of Ghent, where he studied computer science. His Unix username at the university was "jvandemo," the nickname he still goes by on the Internet today. In 1999, Jurgen started his professional career at Infoworld. After years of hard work and dedication as a developer and network engineer, he was awarded different management positions in 2005 and 2006. Being a true developer at heart, he missed writing code, and in 2007, he decided to end his management career to pursue his true passion again—development. Since then, he has been studying and working from his home office in Belgium, where he currently lives with his girlfriend, son, and dogs. In a rapidly evolving world of data-intensive, real-time applications, he now focuses on JavaScript-related technologies with a heavy emphasis on AngularJS and Node.js. His many private and public contributions have helped form the foundation of numerous successful projects around the world. If you need help with your project, Jurgen can be reached at <hire@jvandemo.com>. You can follow him on Twitter at @jvandemo. You can go through his blog at http://www.jvandemo.com.





www.PacktPub.com





Support files, eBooks, discount offers, and more





For support files and downloads related to your book, please visit www.PacktPub.com.

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at <service@packtpub.com> for more details.

At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.



https://www2.packtpub.com/books/subscription/packtlib

Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book library. Here, you can search, access, and read Packt's entire library of books.





Why subscribe?





Fully searchable across every book published by Packt

Copy and paste, print, and bookmark content

On demand and accessible via a web browser





Free access for Packt account holders





If you have an account with Packt at www.PacktPub.com, you can use this to access PacktLib today and view 9 entirely free books. Simply use your login credentials for immediate access.





Preface





I've worked on projects of various sizes over the course of my career, ranging from small brochure sites to building entire social networks. One thing in common with all of them was the need for well-structured JavaScript and CSS.

This book covers two fantastic open source projects that stemmed from this need—Bootstrap and AngularJS.





What this book covers





Chapter 1, Hello, {{name}}, looks at the basics of AngularJS and Bootstrap whilst building a simple "Hello, World" app.

Chapter 2, Let's Build with AngularJS and Bootstrap, introduces to the main app we'll be building over the course of the book, a look at Bootstrap's grid system, and some of the components that make up AngularJS.

Chapter 3, Filters, takes a look at some of AngularJS's built-in filters and also build our own.

Chapter 4, Routing, uses AngularJS's built-in router, and we'll learn how to utilize partials to create a multiview web app.

Chapter 5, Building Views, covers Bootstrap's grid system, and we'll flesh out the partials.

Chapter 6, CRUD, shows that our views are in place we can implement create, read, update, and delete functions.

Chapter 7, AngularStrap, covers the third-party module, which will allow us to use all of Bootstrap's plugins via AngularJS.

Chapter 8, Connecting to the Server, looks at the two official ways of connecting to a server.

Chapter 9, Using Task Runners, minifies all of our JS and Less files using Grunt and gulp.

Chapter 10, Customizing Bootstrap, allows you to easily customize Bootstrap now that Grunt.js is setup.

Chapter 11, Validation, includes validation out of the box; we'll implement that and manage server errors.

Chapter 12, Community Tools, takes a look at some of the tools built by the AngularJS community.

Appendix A, People and Projects to Watch, covers some key people in the AngularJS and Bootstrap worlds as well as the projects to watch.

Appendix B, Where to Go for Help, provides answers to the questions you might have.

Appendix C, Self-test Answers, provides all the answers enlisted in the self-test questions sections of the book.





What you need for this book





AngularJS and Bootstrap have no dependencies at all, so you will not need a lot for this book. Really, all you need is a web browser and a text editor. I recommend you use Chrome and Atom.





Who this book is for





If you're interested in modern web development at all, you'll no doubt have come across Bootstrap and AngularJS. This book is aimed at people with a little bit of JavaScript experience who want to dive head first into building web apps.

However, one thing that's definitely required is an understanding of JavaScript. If you're not sure what the difference is between a string and an object, you'll need to pick that up beforehand.

Of course, if you've used AngularJS or Bootstrap earlier, and want to learn more, then you'll feel right at home here.





Conventions





In this book, you will find a number of text styles that distinguish between different kinds of information. Here are some examples of these styles and an explanation of their meaning.

Code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles are shown as follows: "Pop this script tag with in the <head> of your page."

A block of code is set as follows:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title></title> </head> <body> </body> </html>



Any command-line input or output is written as follows:

open -a 'Google Chrome' --args -allow-file-access-from-files



New terms and important words are shown in bold. Words that you see on the screen, for example, in menus or dialog boxes, appear in the text like this: "We're going to need to display all the same information we entered in the Add Contact view as well as our Gravatar."





Note


Warnings or important notes appear in a box like this.





Tip


Tips and tricks appear like this.





Reader feedback





Feedback from our readers is always welcome. Let us know what you think about this book—what you liked or disliked. Reader feedback is important for us as it helps us develop titles that you will really get the most out of.

To send us general feedback, simply e-mail <feedback@packtpub.com>, and mention the book's title in the subject of your message.

If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, see our author guide at www.packtpub.com/authors.





Customer support





Now that you are the proud owner of a Packt book, we have a number of things to help you to get the most from your purchase.





Downloading the example code





You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.





Errata





Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you find a mistake in one of our books—maybe a mistake in the text or the code—we would be grateful if you could report this to us. By doing so, you can save other readers from frustration and help us improve subsequent versions of this book. If you find any errata, please report them by visiting http://www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details of your errata. Once your errata are verified, your submission will be accepted and the errata will be uploaded to our website or added to any list of existing errata under the Errata section of that title.

To view the previously submitted errata, go to https://www.packtpub.com/books/content/support and enter the name of the book in the search field. The required information will appear under the Errata section.





Piracy





Piracy of copyrighted material on the Internet is an ongoing problem across all media. At Packt, we take the protection of our copyright and licenses very seriously. If you come across any illegal copies of our works in any form on the Internet, please provide us with the location address or website name immediately so that we can pursue a remedy.

Please contact us at <copyright@packtpub.com> with a link to the suspected pirated material.

We appreciate your help in protecting our authors and our ability to bring you valuable content.





Questions





If you have a problem with any aspect of this book, you can contact us at <questions@packtpub.com>, and we will do our best to address the problem.





Chapter 1. Hello, {{name}}





The best way to learn code is to write code, so we're going to jump right in. To show you just how easy it is to get up and running with Bootstrap and AngularJS, we're going to make a super simple application that will allow us to enter a name and have it displayed on the page in real time. It's going to demonstrate the power of Angular's two-way data binding and the included templating language. We'll use Bootstrap to give the app a bit of style and structure.

Before we install our frameworks, we need to create our folder structure and the index.html file that will be the basis of our web app.





Setting up





In order to create our Angular and Bootstrap application, we need to do a little bit of setting up, which just involves creating an HTML page and including a few files. First, create a new directory called chapter1 and open it up in your editor. Create a new file called index.html directly inside it and pop in this boilerplate code:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title></title> </head> <body> </body> </html>



This is just a standard HTML page that we can do something with once we've included Angular and Bootstrap.

Now, create a couple of folders inside chapter1 folder: css and js. Your completed folder structure should look something like this:





Installing AngularJS and Bootstrap





Installing both of our frameworks is as simple as including a CSS or JavaScript file on our page. We can do this via a content delivery network (CDN) like Google Code or MaxCDN, but we're going to fetch the files manually for now. Let's take a look at what steps you should be aware of when including AngularJS and Bootstrap in your project.





Installing Bootstrap





Head to http://getbootstrap.com and hit the Download Bootstrap button. This will give you a zip of the latest version of Bootstrap that includes CSS, fonts, and JavaScript files. Previous versions of Bootstrap included an images directory but Version 3 brings the change to icon fonts.

For our app, we're only interested in one file at the moment: bootstrap.min.css from the css directory. The stylesheet provides the entire structure and all of the lovely elements, such as buttons and alerts, that Bootstrap is famous for. Copy it over to your project's css directory and open up the index.html file in your text editor.

Including Bootstrap is as easy as linking that CSS file we just copied over. You just need to add the following within your <head> tag. Pop this script tag within the <head> of your page:

<link rel="stylesheet" href="css/bootstrap.min.css">





Installing AngularJS





Okay, now that we've got Bootstrap included in our web app, we need to install Angular. Visit https://angularjs.org/ and click on the Download button. You'll be presented with a few options; we want the minified stable version.

Copy the downloaded file over to your project's js directory and open up your index.html file. Angular can be included in your app just like any other JavaScript file.

It's recommended that Angular is included in the <head> tag of your page or certain functions we'll be taking advantage of throughout the course of the book won't work. While it's not necessary, there will be extra steps you'll need to take if you choose to load Angular further down your HTML file.

Pop this <script> tag within the <head> of your page.

<script src="js/angular.min.js"></script>



Ready to go? Well, almost. We need to tell Angular that we want to utilize it in our app. Angular calls this bootstrapping and the framework makes this extremely simple for us. All we need to do is include an additional attribute in our opening <html> tag:

<html lang="en" ng-app>



That's it! Angular now knows we want to take advantage of it.





Tip


Angular also allows us to prefix these attributes with data- (for example, data-ng-app) should we be concerned about writing valid HTML5.





Using AngularJS





So we've got a lot of the theory behind Angular down; it's time to actually put it in place. Once we've got our application working, we'll take a look at how we can make it shine with Bootstrap.

Let's open that index.html file again, but this time also open it up in your browser so we can see what we're working with. This is what we've got so far:

<html lang="en" ng-app> <head> <meta charset="utf-8"> <link rel="stylesheet" href="css/bootstrap.min.css"> <title></title> <script type="text/javascript" src="js/angular.min.js"></script> </head> <body> </body> </html>



So, we've got Bootstrap and Angular there and we've initialized our app with the ng-app attribute in the opening <html> tag; let's get cracking.

We're going to have a Hello, World app with a bit of a difference. Instead of saying hello to the world, we're going to have an input field that will bind the data and echo it out in our view automatically, and we're going to do all of this without writing a line of JavaScript.

Let's start out by getting an <h1> tag in our <body> tag:

<h1>Hello, World</h1>



If you view this in your browser, you should notice that Bootstrap has tidied up the default. We no longer have Times New Roman but instead Helvetica and those excess margins around the edge have been removed:



We now need to include our text input and also specify the model we want to use. Remember, a model can be any type, but in this case it will be a string that the input will return:

<input type="text" ng-model="name">



The ng-model attribute declares a model binding on that element, and anything we type into the input box will be automatically bound to it by Angular. Obviously this isn't going to be displayed on our page by magic; we need to tell the framework where we want it echoed. To display our model on the page, we just need to wrap the name of it in double curly braces:

{{name}}



Pop this in place of World in your <h1> tag and refresh the page in your browser. If you pop your name in the input field, you'll notice that it's automatically displayed in your heading in real time. Angular is doing all of this for us and we haven't written a single line of JavaScript.



Now, while that's great, it would be nice if we could have a default in place so it doesn't look broken before a user has entered their name. What's awesome is that everything in between those curly braces is parsed as an AngularJS expression, so we can check and see if the model has a value, and if not, it can echo World. Angular calls this an expression and it's just a case of adding two pipe symbols as we would in JS:

{{name || 'World'}}





Tip


Angular describes an expression as the following: "JavaScript-like code snippets that are usually placed in bindings such as {{ expression }}."





It's good to remember that this is JavaScript, and that's why we need to include the quotation marks here, to let it know that this is a string and not the name of a model. Remove them and you'll notice that Angular displays nothing again. That's because both the name and World models are undefined.

These models can be defined directly from within our HTML using an attribute as we've seen, but we can also assign a value to them from a controller. To do this, we're going to need to create a new JS file called controller.js and include it in our app:

<script type="text/javascript" src="js/controller.js"></script>



Pop this in after you've included Angular on your page to avoid any errors being thrown.

Controllers are just functions that Angular can utilize; let's take a look at one:

function AppCtrl($scope){ }



Here, we've declared our controller (essentially just a plain JavaScript constructor function) and have injected the scope service into it. The scope is what we can access from within our view. There can be multiple controllers and multiple scopes on a single page. It's essentially a JavaScript object of our models and functions that Angular works its magic with, for example, the scope of our application so far looks like this:

{ name: "Stephen" }



The scope changes depending upon what's entered into the input field. This can then be accessed from our view as well as the controller.

Now that we've created our controller, we need to tell Angular we want to use it. For our application we only need a single controller, so let's add a second attribute to the <html> tag again:

ng-controller="AppCtrl"



This attribute tells Angular we want to use the AppCtrl function we've just created as our controller for the page. We could of course add this to any element on the page including the body if we so wish.

To check everything's working okay, we're going to specify an initial value for our model. This is as easy as setting a property on any object:

function AppCtrl($scope) { $scope.name = "World"; }



If you refresh your app in your browser, you'll notice that World is now pre-filled as the model's value. This is a great example of Angular's powerful two-way data binding. It allows us to use pre-defined data perhaps from an API or database and then change this in the view directly before picking it back up in the controller.





Tip


Angular describes data binding as "the automatic synchronization of data between the model and view components". Two-way data binding means that if you change the value of a model in your view or in your JavaScript controller, everything will be kept up-to-date.





Bootstrap





Now that we've created our Hello World application and everything is working as expected, it's time to get involved with Bootstrap and add a bit of style and structure to our app.

The application is currently misaligned to the left, and everything is looking cramped so let's sort that out first with a bit of scaffolding. Bootstrap comes with a great mobile first responsive grid system that we can utilize with the inclusion of a few divs and classes. First though, let's get a container around our content to clean it up immediately:





Tip


Mobile first is a way of designing/developing for the smallest screens first and adding to the design rather than taking elements away.





<div class="container"> <h1>Hello, {{name || 'World'}}</h1> <input type="text" ng-model="name"> </div>



If you resize your browser window, you should start to notice some of the responsiveness of the framework coming through and see it collapsing:



Now, I think it's a good idea to wrap this in what Bootstrap calls a Jumbotron (in previous versions of Bootstrap this was a Hero Unit). It'll make our headline stand out a lot more. We can do this by wrapping our <h1> and <input> tags in a new div with the jumbotron class:

<div class="container"> <div class="jumbotron"> <h1>Hello, {{name || 'World'}}</h1> <input type="text" ng-model="name"> </div> </div>





It's starting to look a lot better but I'm not too happy about our content touching the top of the browser like that. We can make it look a lot nicer with a page header but that input field still looks out of place to me.

First, let's sort out that page header:

<div class="container"> <div class="page-header"> <h2>Chapter 1 <small>Hello, World</small></h2> </div> <div class="jumbotron"> <h1>Hello, {{name || 'World'}}</h1> <input type="text" ng-model="name"> </div> </div>





I've included the chapter number and title here. The <small> tag within our <h2> tag gives us a nice differentiation between the chapter number and the title. The page-header class itself just gives us some additional margin and padding as well as a subtle border along the bottom.





Tip


Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.





The last thing I think we could improve upon is that input box. Bootstrap comes with some great input styles so let's include those. First, we need to add the class of form-control to the text input. This will set the width to 100% and also bring out some nice styling such as rounded corners and a glow when we focus on the element:

<input type="text" ng-model="name" class="form-control">





Much better, but to me it looks a little small when you compare it with the heading. Bootstrap provides two additional classes we can include that will either make the element smaller or larger: input-lg and input-sm respectively. In our case, the input-lg class is the one we want, so go ahead and add that to the input.

<input type="text" ng-model="name" class="form-control input-lg">





That's better but we still need to sort the spacing out, as it looks a bit snug against our <h1> tag. It's probably also a good idea that we add a label in so it's clear what the user should be entering in the box. Bootstrap allows us to kill two birds with one stone as it includes a margin on the label:

<label for="name">Enter Your Name</label> <input type="text" ng-model="name" class="form-control input-lg" id="name">





Self-test questions





How is Angular initialized on the page?

What is used to display a model's value on the page?

What does MVC stand for?

How do we create a controller and tell Angular we want to use it?

In Bootstrap 3, what's the new name for a Hero Unit?





Summary





Our app's looking great and working exactly how it should, so let's recap what we've learnt in the first chapter.

To begin with, we saw just how easy it is to get AngularJS and Bootstrap installed with the inclusion of a single JavaScript file and stylesheet. We also looked at how an Angular application is initialized and started building our first application.

The Hello, World app we've created, while being very basic, demonstrates some of Angular's core features:

Expressions

Scopes

Models

Two-way data binding





All of this was possible without writing a single line of JavaScript, as the controller we created was just to demonstrate two-way binding and wasn't a required component of our app.

With Bootstrap, we utilized a few of the many available components such as the jumbotron and the page-header classes to give our application some style and substance. We also saw the framework's new mobile first responsive design in action without cluttering up our markup with unnecessary classes or elements.

In Chapter 2, Let's Build with AngularJS and Bootstrap we're going to explore some more AngularJS and Bootstrap fundamentals and introduce the project we're going to be building over the course of this book.





Chapter 2. Let's Build with AngularJS and Bootstrap





Now that you've officially built your first web app using AngularJS and Bootstrap, it's time to up the ante. Over the course of the book we're going to be using both frameworks to build a contacts manager complete with full text search, creation, editing, and deletion. We'll look at building a maintainable code base as well as exploring the full potential of both frameworks. So, let's build!





Setting up





Let's quickly create a new directory for our app and set up a similar structure to our Hello, World app we made in Chapter 1, Hello, {{name}}.

The following folder structure is perfect for now:



You'll notice I've popped our directories into an assets directory to keep things tidy. Copy Angular and Bootstrap from Chapter 1, Hello, {{name}} into the relevant directories and create an index.html file in the root, which will become the basis of the contacts manager. The following code snippet is just a base HTML page with Bootstrap and Angular included. I've also initialized Angular on the page with the ng-app attribute on the <html> tag. Here's what our page should look like at this stage:

<!DOCTYPE html> <html lang="en" ng-app> <head> <meta charset="utf-8"> <title>Contacts Manager</title> <link rel="stylesheet" href="assets/css/bootstrap.min.css"> <script type="text/javascript" src="assets/js/angular.min.js"></script> </head> <body> </body> </html>





Scaffolding





Okay, now that we've got our base file and folder structure sorted we can begin to scaffold out our app using Bootstrap. Apart from including a collection of components, such as navigation and buttons, that we can use throughout our contacts manager, Bootstrap also includes an extremely powerful and responsive grid system that we're going to harness the power of.





Navigation





We're going to need a navbar to switch between each of our views. Naturally, this will be placed at the top of the screen.

Let's take a look at our completed navigation before we break it down:

<nav class="navbar navbar-default"role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#nav-toggle"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Contacts Manager</a> </div> <div class="collapse navbar-collapse" id="nav-toggle"> <ul class="nav navbar-nav"> <li class="active"><a href="/">Browse</a></li> <li><a href="/add">Add Contact</a></li> </ul> <form class="navbar-form navbar-right" role="search"> <input type="text" class="form-control" placeholder="Search"> </form> </div> </nav>



It can look quite intimidating for what is a very simple component of our page, but if we break it down, it becomes clear that everything here is completely necessary.

The <nav> tag holds everything within our navbar. Inside of this, the navigation is split into two sections: the navbar-header and navbar-collapse. These elements are exclusively for mobile navigation and control what is shown and what is hidden under the toggle button.

The data-target attribute on the button directly corresponds with the id attribute of the navbar-collapse element so Bootstrap knows what it should be toggling. The following screenshot is what our navigation will look like on devices bigger than a tablet.



We're going to include our navigation directly within our <body> tag. This will allow it to span across the full width of the browser.

If you scale the browser down, you'll notice Bootstrap displays the mobile header with the toggle button below 768px—the size of an iPad screen in portrait. However, if you click the button to toggle the navigation, you'll notice nothing happens. That's because we haven't included Bootstrap's JavaScript file that was included in the ZIP file we downloaded earlier.

Copy it across to your app's js directory and reference it in your index.html file. You also need to include jQuery in the application as Bootstrap's JS depends on this. You can fetch the latest version from http://jquery.com/—again, add this to your directory and include it on your page before bootstrap.js. Ensure your JavaScript files are included in the following order:

<script src="assets/js/jquery.min.js"></script> <script src="assets/js/bootstrap.min.js"></script> <script src="assets/js/angular.min.js"></script>



If you reload the browser you should now be able to click the toggle button to display the mobile navigation.





Getting to grips with Bootstrap's grid





Bootstrap's 12-column grid system is very powerful and allows us to scaffold our responsive web app with very few elements, taking advantage of modular CSS along the way. The grid is composed of rows and columns that can be adapted using a series of classes. Before we begin, we need to include a container for our rows or the framework won't respond as expected. This is just a <div> tag that we can place below our navbar:

<div class="container"></div>



This will center our grid as well as add a max-width property to keep things nice and tidy.

There are four class prefixes, which define the behavior of the columns. For the most part, we'll be utilizing the col-sm- prefix. This collapses our columns down so they appear atop one another when the container is less than 750px wide.

The other classes all relate to different device screen sizes and react in a similar way. The following table from http://getbootstrap.com/ shows the variations between all four classes:


Phones

(<768px)

Tablets

(≥768px)

Desktops

(≥992px)

Desktops

(≥1200px)



Grid behavior

Horizontal at all times

Collapsed to start, horizontal above breakpoints



Max container width

None (auto)

750px

970px

1170px



Class prefix

.col-xs-

.col-sm-

.col-md-

.col-lg-



Max column width

Auto

60px

78px

95px



Offsets

N/A

Yes



Column Ordering

N/A

Yes





Let's quickly make a two-column layout with a main content area and a sidebar. As the grid is made up of 12 columns, we're going to need to ensure our content area adds up to this or we'll end up with some empty space.

I think eight columns for our content area and four for our sidebar sounds perfect, so how would we go about implementing that?

Inside our container we first need to create a new <div> tag with the row class. We can have as many rows as we like, which can each house up to twelve columns.

<div class="container"> <div class="row"> </div> </div>



As we only want our columns to stack on mobile devices, we're going to be using the col-sm- prefix. Creating a column is as simple as taking the desired prefix and appending the number of columns you wish for it to span. Let's take a look at how our basic two-column layout will look:

<div class="container"> <div class="row"> <div class="col-sm-8"> This is our content area </div> <div class="col-sm-4"> Here is our sidebar </div> </div> </div>



When displayed on a screen larger than a mobile device, Bootstrap will automatically add 30px of guttering between each column (15px on either side). However, there will be times when you're going to want to create additional space between your columns or pull them in a little. Bootstrap provides a way to do this by adding an additional class to the column.

Once again, take the required prefix, but this time you need to include the keyword offset:

<div class="col-sm-4 col-sm-offset-1"></div>



This time, the number on the end controls the number of columns you wish to offset over. The additional class does this by adding an additional margin to the left.





Note


Remember: Offset columns count toward your total of 12 columns in a row.





Inside our columns, we can nest additional rows and columns to create a more complex layout. Let's take a look:

<div class="container"> <div class="row"> <div class="col-sm-8"> <div class="row"> <div class="col-sm-6"> <p>Lorem ipsum dolor...<p> </div> <div class="col-sm-6"> <p>Class aptent taciti...</p> </div> </div> </div> </div> </div>



This will create two columns within our main content container we created earlier. I've popped in some dummy text to demonstrate this.

If you open it up in your browser, you'll notice there are now three columns. However, as our grid is nested, we can create a new row and have a single column, three columns, or whatever our layout requires.





Helper classes





Bootstrap also includes a few helper classes that we can use to adapt our layout. These are generally utilitarian and are designed to serve a single purpose. Let's take a look at some examples.





Floats





Floating is often essential to creating a decent layout on the Web and Bootstrap gives us two classes to pull elements left or right:

<div class="pull-left">...</div> <div class="pull-right">...</div>



In order to use floats effectively, we need to wrap our floated elements in a clearfix class. This will clear the elements and keep the flow of the document as expected:

<div class="clearfix"> <div class="pull-left">...</div> <div class="pull-right">...</div> </div>



If the float classes are directly within an element with the row class, then our floats are cleared automatically by Bootstrap and the clearfix class does not need to be applied manually.





Center elements





Alongside floats, there's often cause to center block-level elements. Bootstrap allows us to do this with the center-block class:

<div class="center-block">...</div>



This sets the margin-left and margin-right properties to auto, which will center the element.





Show and hide





You may wish to show and hide elements with CSS, and Bootstrap gives you a couple of classes to do this:

<div class="show">...</div> <div class="hidden">...</div>



It's important to note that the show class sets the display property to block, so only apply this to block-level elements and not elements you wish to be displayed inline or inline-block.

Bootstrap also includes numerous classes to enable elements to be shown or hidden at specific screen sizes. The classes use the same pre-defined sizes as Bootstrap's grid.

For example, the following will hide an element at a specific screen size:

<div class="hidden-md"></div>



This will hide the element on medium devices but it will still be visible on mobiles, tablets, and large desktops. To hide an element on multiple devices, we need to use multiple classes:

<div class="hidden-md hidden-lg"></div>



Likewise, the visible classes work in reverse, showing elements at specific sizes. However, unlike the hidden classes, they also require us to set the display value. This can be block, inline, or inline-block:

<div class="visible-md-block"></div> <div class="visible-md-inline"></div> <div class="visible-md-inline-block"></div>



Of course, we can use the various classes in tandem. If, for example, we wanted a block-level element on a smaller screen but have it become inline-block later, we would use the following code:

<div class="visible-sm-block visible-md-inline-block"></div>



If you can't remember the various class sizes, be sure to take another look at the Getting to grips with Bootstrap's grid section.





Working with directives





Something we've been using already without knowing it is what Angular calls directives. These are essentially powerful functions that can be called from an attribute or even its own element, and Angular is full of them. Whether we want to loop data, handle clicks, or submit forms, Angular will speed everything up for us.

We first used a directive to initialize Angular on the page using ng-app, and all of the directives we're going to look at in this chapter are used in the same way—by adding an attribute to an element.

Before we take a look at some more of the built-in directives, we need to quickly make a controller. Create a new file and call it controller.js. Save this to your js directory within your project and open it up in your editor.

As we learnt in Chapter 1, Hello, {{name}}, controllers are just standard JS constructor functions that we can inject Angular's services such as $scope into. These functions are instantiated when Angular detects the ng-controller attribute. As such, we can have multiple instances of the same controller within our application, allowing us to reuse a lot of code. This familiar function declaration is all we need for our controller.

function AppCtrl(){ }



To let the framework know this is the controller we want to use, we need to include this on the page after Angular is loaded and also attach the ng-controller directive to our opening <html> tag:

<html ng-controller="AppCtl"> … <script type="text/javascript" src="assets/js/controller.js"></script>





ng-click and ng-mouseover





One of the most basic things you'll have ever done with JavaScript is listened for a click event. This could have been using the onclick attribute on an element, using jQuery, or even with an event listener. In Angular, we use a directive.

To demonstrate this, we'll create a button that will launch an alert box—simple stuff. First, let's add the button to our content area we created earlier:

<div class="col-sm-8"> <button>Click Me</button> </div>



If you open this up in your browser, you'll see a standard HTML button created—no surprises there. Before we attach the directive to this element, we need to create a handler in our controller. This is just a function within our controller that is attached to the scope. It's very important we attach our function to the scope or we won't be able to access it from our view at all:

function AppCtl($scope){ $scope.clickHandler = function(){ window.alert('Clicked!'); }; }



As we already know, we can have multiple scopes on a page and these are just objects that Angular allows the view and the controller to have access to. In order for the controller to have access, we've injected the $scope service into our controller. This service provides us with the scope Angular creates on the element we added the ng-controller attribute to.

Angular relies heavily on dependency injection, which you may or may not be familiar with. As we've seen, Angular is split into modules and services. Each of these modules and services depend upon one another and dependency injection provides referential transparency. When unit testing, we can also mock objects that will be injected to confirm our test results. DI allows us to tell Angular what services our controller depends upon, and the framework will resolve these for us.

An in-depth explanation of AngularJS' dependency injection can be found in the official documentation at https://docs.angularjs.org/guide/di.

Okay, so our handler is set up; now we just need to add our directive to the button. Just like before, we need to add it as an additional attribute. This time, we're going to pass through the name of the function we're looking to execute, which in this case is clickHandler. Angular will evaluate anything we put within our directive as an AngularJS expression, so we need to be sure to include two parentheses indicating that this is a function we're calling:

<button ng-click="clickHandler()">Click Me</button>



If you load this up in your browser, you'll be presented with an alert box when you click the button. You'll also notice that we don't need to include the $scope variable when calling the function in our view. Functions and variables that can be accessed from the view live within the current scope or any ancestor scope.



Should we wish to display our alert box on hover instead of click, it's just a case of changing the name of the directive to ng-mouseover, as they both function in the exact same way.





ng-init





The ng-init directive is designed to evaluate an expression on the current scope and can be used on its own or in conjunction with other directives. It's executed at a higher priority than other directives to ensure the expression is evaluated in time.

Here's a basic example of ng-init in action:

<div ng-init="test = 'Hello, World'"></div> {{test}}



This will display Hello, World onscreen when the application is loaded in your browser. Above, we've set the value of the test model and then used the double curly-brace syntax to display it.





ng-show and ng-hide





There will be times when you'll need to control whether an element is displayed programmatically. Both ng-show and ng-hide can be controlled by the value returned from a function or a model.

We can extend upon our clickHandler function we created to demonstrate the ng-click directive to toggle the visibility of our element. We'll do this by creating a new model and toggling the value between true or false.

First of all, let's create the element we're going to be showing or hiding. Pop this below your button:

<div ng-hide="isHidden"> Click the button above to toggle. </div>



The value within the ng-hide attribute is our model. Because this is within our scope, we can easily modify it within our controller:

$scope.clickHandler = function(){ $scope.isHidden = !$scope.isHidden; };



Here we're just reversing the value of our model, which in turn toggles the visibility of our <div>.

If you open up your browser, you'll notice that the element isn't hidden by default. There are a few ways we could tackle this. Firstly, we could set the value of $scope.hidden to true within our controller. We could also set the value of hidden to true using the ng-init directive. Alternatively, we could switch to the ng-show directive, which functions in reverse to ng-hide and will only make an element visible if a model's value is set to true.





Note


Ensure Angular is loaded within your header or ng-hide and ng-show won't function correctly. This is because Angular uses its own classes to hide elements and these need to be loaded on page render.





ng-if





Angular also includes an ng-if directive that works in a similar fashion to ng-show and ng-hide. However, ng-if actually removes the element from the DOM whereas ng-show and ng-hide just toggles the elements' visibility.

Let's take a quick look at how we'd use ng-if with the preceding code:

<div ng-if="isHidden"> Click the button above to toggle. </div>



If we wanted to reverse the statement's meaning, we'd simply just need to add an exclamation point before our expression:

<div ng-if="!isHidden"> Click the button above to toggle. </div>





ng-repeat





Something you'll come across very quickly when building a web app is the need to render an array of items. For example, in our contacts manager, this would be a list of contacts, but it could be anything. Angular allows us to do this with the ng-repeat directive.

Here's an example of some data we may come across. It's array of objects with multiple properties within it. To display the data, we're going to need to be able to access each of the properties. Thankfully, ng-repeat allows us to do just that.

Here's our controller with an array of contact objects assigned to the contacts model:

function AppCtrl($scope){ $scope.contacts = [ { name: 'John Doe', phone: '01234567890', email: 'john@example.com' }, { name: 'Karan Bromwich', phone: '09876543210', email: 'karan@email.com' } ]; }



We have just a couple of contacts here, but as you can imagine, this could be hundreds of contacts served from an API that just wouldn't be feasible to work with without ng-repeat.

First, add an array of contacts to your controller and assign it to $scope.contacts. Next, open up your index.html file and create a <ul> tag. We're going to be repeating a list item within this unordered list so this is the element we need to add our directive to:

<ul> <li ng-repeat="contact in contacts"></li> </ul>



If you're familiar with how loops work in PHP or Ruby, then you'll feel right at home here. We create a variable that we can access within the current element being looped. The variable after the in keyword references the model we created on $scope within our controller. This now gives us the ability to access any of the properties set on that object with each iteration or item repeated gaining a new scope. We can display these on the page using Angular's double curly-brace syntax as we discovered in Chapter 1, Hello, {{name}}:

<ul> <li ng-repeat="contact in contacts"> {{contact.name}} </li> </ul>



You'll notice that this outputs the name within our list item as expected, and we can easily access any property on our contact object by referencing it using the standard dot syntax.





ng-class





Often there are times where you'll want to change or add a class to an element programmatically. We can use the ng-class directive to achieve this. It will let us define a class to add or remove based on the value of a model.

There are a couple of ways we can utilize ng-class. In its most simple form, Angular will apply the value of the model as a CSS class to the element:

<div ng-class="exampleClass"></div>



Should the model referenced be undefined or false, Angular won't apply a class. This is great for single classes, but what if you want a little more control or want to apply multiple classes to a single element? Try this:

<div ng-class="{className: model, class2: model2}"></div>



Here, the expression is a little different. We've got a map of class names with the model we wish to check against. If the model returns true, then the class will be added to the element.

Let's take a look at this in action. We'll use checkboxes with the ng-model attribute we've already seen in Chapter 1, Hello, {{name}}, to apply some classes to a paragraph:

<p ng-class="{'text-center': center, 'text-danger': error}"> Lorem ipsum dolor sit amet </p>



I've added two Bootstrap classes: text-center and text-danger. These observe a couple of models, which we can quickly change with some checkboxes:





Note


The single quotations around the class names within the expression are only required when using hyphens, or an error will be thrown by Angular.





<label><input type="checkbox" ng-model="center"> text-center</label> <label><input type="checkbox" ng-model="error"> text-danger</label>



When these checkboxes are checked, the relevant classes will be applied to our element.





ng-style





In a similar way to ng-class, this directive is designed to allow us to dynamically style an element with Angular. To demonstrate this, we'll create a third checkbox that will apply some additional styles to our paragraph element.

The ng-style directive uses a standard JavaScript object, with the keys being the property we wish to change (for example, color and background). This can be applied from a model or a value returned from a function.

Let's take a look at hooking it up to a function that will check a model. We can then add this to our checkbox to turn the styles off and on.

First, open up your controller.js file and create a new function attached to the scope. I'm calling mine styleDemo:

$scope.styleDemo = function(){ if(!$scope.styler){ return; } return { background: 'red', fontWeight: 'bold' }; };



Inside the function, we need to check the value of a model; in this example, it's called styler. If it's false, we don't need to return anything, otherwise we're returning an object with our CSS properties. You'll notice that we used fontWeight rather than font-weight in our returned object. Either is fine, and Angular will automatically switch the CamelCase over to the correct CSS property. Just remember than when using hyphens in JavaScript object keys, you'll need to wrap them in quotation marks.

This model is going to be attached to a checkbox, just like we did with ng-class:

<label><input type="checkbox" ng-model="styler"> ng-style</label>



The last thing we need to do is add the ng-style directive to our paragraph element:

<p .. ng-style="styleDemo()"> Lorem ipsum dolor sit amet </p>



Angular is clever enough to recall this function every time the scope changes. This means that as soon as our model's value changes from false to true, our styles will be applied and vice versa.





ng-cloak





The final directive we're going to look at is ng-cloak. When using Angular's templates within our HTML page, the double curly braces are temporarily displayed before AngularJS has finished loading and compiling everything on our page. To get around this, we need to temporarily hide our template before it's finished rendering.

Angular allows us to do this with the ng-cloak directive. This sets an additional style on our element whilst it's being loaded: display: none !important;.





Note


To ensure there's no flashing while content is being loaded, it's important that Angular is loaded in the head section of our HTML page.





Self-test questions





What did we add to the top of our page to allow us to switch views?

How many columns a Bootstrap's grid system comprises of?

What is a directive and how are most of them used?

Which directive would we use to loop data?





Summary





We've covered a lot in this chapter, so before we continue onto the next chapter, let's recap it all.

Bootstrap allowed us to quickly create a responsive navigation. We needed to include the JavaScript file included with our Bootstrap download to enable the toggle on the mobile navigation.

We also looked at the powerful responsive grid system included with Bootstrap and created a simple two-column layout. While we were doing this, we learnt about the four different column class prefixes as well as nesting our grid. To adapt our layout, we discovered some of the helper classes included with the framework to allow us to float, center, and hide elements.

In this chapter, we saw in detail Angular's built-in directives: functions Angular allows us to use from within our view. Before we could look at them, we needed to create a controller, which is just a function that we can pass Angular's services into using dependency injection.

The directives we looked at here are ones that will be essential as we build our contact manager throughout the course of the book. Directives such as ng-click and ng-mouseover are essentially just new ways of handling events that you will have no doubt done using either jQuery or vanilla JavaScript. However, directives such as ng-repeat will probably be a completely new way of working. It brings some logic directly within our view to loop through data and display it on the page.

We also looked at directives that observe models on our scope and perform different actions based on their values. Directives like ng-show and ng-hide will show or hide an element based on a model's value. We also saw this in action in ng-class, which allowed us to add some classes to our elements based on our models' values.





Chapter 3. Filters





In the previous chapter, we looked at one of the core components of AngularJS: directives. As with many frameworks, Angular also has other paradigms to help us build our web app. Filters allow us to easily manipulate and sort data from either the view or controller, and just like with directives, there are a good few filters included out of the box.

There are many use cases for filters, and we'll take a look at a few of them over the course of the chapter. For example, you may simply want to manipulate a string. This could be by converting, localizing, or even truncating. Of course, filters also allow you to work with other JavaScript types, such as arrays and objects. Perhaps you'd want to create a live search to filter through a dataset you've looped using ng-repeat. All of that is possible with filters.

Before we take a look at some of the pre-included filters, we should probably see how a filter is applied from the view.





Applying a filter from the view





Filters can be applied directly to expressions within our templates. Remember, an expression is anything within the double curly-brace syntax or a directive:

{{expression | filter}}



It's easy to apply a filter; we just add a pipe symbol followed by the name of the filter we want to place on the expression. We can follow the same idea to apply multiple filters to a single expression. Multiple filters are chained and applied in succession. In the following example, filter2 will be applied to the output of filter1 and so forth:

{{expression | filter1 | filter2 | filter3}}



Some filters may have arguments, and these can be applied using a similar syntax:

{{expression | filter:argument1:argument2}}



Throughout the chapter, we'll demonstrate a number of the filters included with Angular directly from the view using the syntax we've just looked at. We'll then take a look at how we can apply the same filters from the controller and also how we can create our own.





Currency and numbers





The first filter we're going to look at is one that formats numbers into currency. In the en-US locale, it adds a comma to separate thousands and a decimal point in the right place. It also prepends the relevant symbol:

{{12345 | currency}}



The currency symbol will depend on locale. As we're using en-US, by default, Angular prepends a dollar symbol ($), but we can pass through the symbol of our choosing as an argument:

{{12345 | currency:'£'}}



It's important to remember to wrap the symbol in quotation marks, as this is a string.

Angular also includes a second filter to format numbers, which gives us a little more control. It allows us to specify the number of decimal places we wish the number to be rounded to:

{{12345.225 | number:2}}



The output of this filter will be 12,345.23. You'll notice that the number has been rounded up to two decimal places and a comma has been added to separate thousands.





Lowercase and uppercase





These two filters are perhaps the simplest ones included with Angular. They simply convert the provided string to lowercase or uppercase:

{{'Stephen' | lowercase}} {{'Declan' | uppercase}}



These filters output the following:

stephen DECLAN





limitTo





There are times when you need to limit a string or an array, and this can easily be achieved in AngularJS using the limitTo filter:

{{'Lorem ipsum dolor sit amet' | limitTo:15}}



You'll notice that this filter takes a single argument, which is the number to which the input should be limited. Here we've limited a string, but this could quite easily be an array in an ng-repeat directive, for example:

<div ng-repeat="array | limitTo:2"></div>





Date





When working with data from an API, it's often the case that the date will be given as a UNIX time or a full timestamp. This isn't the friendliest thing to work with, and thankfully, Angular includes an easy way to format dates with a filter:

{{expression | date:format}}



The filter takes one argument: format. For example, if we wanted to take a timestamp and just output the year, we could easily do that with the following expression:

{{725508723000 | date:'yyyy'}}



We can combine this with the input for day and month and output a standard date string easily:

{{725508723000 | date:'dd/MM/yyyy'}}



Here's a list of some of the most useful elements the format string can be comprised of. A full list can be found on the AngularJS website:


Element

Output

Example



yyyy

4-digit year

2013



yy

2-digit year

13



MMMM

Full text month

December



MMM

Short text month

Dec



MM

Padded numerical month

01



M

Numerical month

1



dd

Padded day

01



d

Day

1



EEEE

Day in week

Monday



EEE

Short day in week

Mon



HH

Padded 24-hour

01



H

24-hour

1



hh

Padded 12-hour

01



h

12-hour

1



mm

Padded minute

05



m

Minute

5



ss

Padded second

09



s

Second

9



a

AM/PM

AM or PM



Z

Timezone

+0100



ww (1.3+ Only)

Week of the year, padded.

03



w (1.3+ Only)

Week of the year

3





There are also a number of predefined formats we can use; let's take a look at one:

{{725508723000 | date:'medium'}}



The medium keyword is just one of the predefined formats this filter recognizes and outputs Dec 28, 1992 2:12:03 AM.

Here's a full list of predefined formats that the date filter will accept:


Keyword

Equivalent

Example



medium

MMM d, y h:mm:ss a

Sep 3, 2010 12:05:08 pm



short

M/d/yy h:mm a

9/3/10 12:05 pm



fullDate

EEEE, MMMM d,y

Friday, September 3, 2010



longDate

MMMM d, y

September 3, 2010



mediumDate

MMM d, y

Sep 3, 2010



shortDate

M/d/yy

9/3/10



mediumTime

h:mm:ss a

12:05:08 pm



shortTime

h:mm a

12:05 pm





We can also include literal values within our format string; for example:

{{725508723000 | date:"h 'in the morning'"}}



Literal values must be wrapped in single quotations. In order for this to happen, we need to swap our single quotations used around the argument for double quotes. Should you wish to include a single quotation mark within the string, you simply need to use two single quotes:

{{725508723000 | date:"h 'o''clock'"}}





Filter





This confusingly named filter allows us to select a subset of items from an array easily. Within our view, this can be used in combination with the ng-repeat directive we looked at in the previous chapter.

We can use this to build a pretty powerful search tool that will filter through our array. Let's take a look at the ng-repeat example we used in the previous chapter:

<ul> <li ng-repeat="contact in contacts"> {{contact.name}} - {{contact.phone}} </li> </ul>



Before we can add our filter, we just need to add the pattern object that will be used for selection from our array. This can be a model, string, pattern object, or function. As we're creating a search, let's just hook a model up to a text input:

<input type="text" ng-model="search">



The last thing to do is to attach our model to the ng-repeat directive. This is done exactly the same as any other filter: a pipe symbol followed by the name of the filter. In this case, we also need to add one argument telling the filter which model, string, object, or function we wish to use:

<li ng-repeat="contact in contacts | filter:search">



This will allow us to use the input field we create to search through everything within our array, which includes names, phone numbers, and email addresses. However, what happens if we want to limit our search to only the name property on our objects? We simply just need to change our model:

<input type="text" ng-model="search.name">



It's important that we leave the name of the model on our ng-repeat as search or the filtering won't be limited to our desired property. Alternatively, we could use the following syntax on our ng-repeat directive to limit our filtering to specific properties. This would allow us to leave the name of our model as search:

<li ng-repeat="contact in contacts | filter:{'name': search}">





orderBy





Apart from filtering our object from within the ng-repeat directive, we can also order it. This is great if the data you're given from an array isn't sorted already or doesn't provide an option to do so.

Currently, our object is all jumbled up and there's no apparent order to it. Let's take a look at how we could go about sorting this by name:

<li ng-repeat="contact in contacts | filter:search | orderBy:'name'">



The first argument we can pass through is a string with the name of the property we want to sort our array by. Should we want to filter by phone number or email address instead, we could pass those values through here.

We can also pass a second argument through a Boolean which controls whether the filter should reverse the order or not:

<li ng-repeat=" .. | orderBy:'name':true">





JSON





The last included filter is mainly for debugging purposes. It will output any JavaScript object into a JSON string for output onto the page.

Let's take our array of contacts that we used in the last chapter to demonstrate ng-repeat and apply the json filter to it:

{{contacts | json}}



The following is the output to our view:

[ { "name":"John Doe", "phone":"01234567890", "email":"john@example.com" }, { "name":"Karan Bromwich", "phone":"09876543210", "email":"karan@email.com" }, { "name":"Declan Proud", "phone":"2341234231", "email":"declan@email.com" }, { "name":"Paul McKay", "phone":"912345678", "email":"p.mckay@domain.com" } ]



As you can see, this is just a JSON representation of the array of objects we created earlier.





Applying filters from JavaScript





There are times when you'll want to apply a filter using JavaScript, usually from your controller, so it's important we take a look at how to do this; there are a couple of options.

We can either inject the $filter service into our controller and utilize any filter included within our application. Alternatively, we can inject the filter as its own dedicated service and use it on its own. Both methods are perfectly valid, and it's down to you, whichever you prefer.

Let's first take a look at using the $filter service. We'll take the json filter we've just looked at and console.log the very same array. To begin, let's inject that service into our controller:

function AppCtl($scope, $filter){ … }



Great! We can now utilize this just as we can $scope. To use it, we simply need to call it as a function and pass through the name of the filter we wish to use, which in our case is json:

$filter('json');



This actually returns the filter itself, and we can see this in the output if we console.log it directly. This means we can call the function immediately by adding a second set of parenthesis straight after:

$filter('json')($scope.contacts);



As we know, the json filter doesn't accept any arguments. However, the first argument of all filter functions is actually the input. We don't see this when we're calling them from the view as Angular does its magic behind the scenes to simplify things.

If you wrap the preceding expression in a console.log, you'll see that the output is identical to the output in our view using the same filter:



Alternatively, if you don't want to use the $filter service, you can inject each filter separately as a service. These are named in the pattern filternameFilter. So for our example, we need to inject jsonFilter:

function AppCtl($scope, jsonFilter){ … }



This can then be used identically as the function returned by the $filter service, allowing us to pass through our object to filter:

jsonFilter($scope.contacts);



Now that we know how we can use filters from within our controller, let's take a look at how we could create our own.





Building your own filter





As we've seen, the limitTo filter is great for truncating strings of text. I've always felt that the filter could do with appending an ellipsis should the string cross the limit. Thankfully, Angular lets us expand upon the included filters and build our own.





Modules





In order to create our filter, we first need to create what's called a module. This will enable us to attach a filter and utilize it in our views or controllers. I think the AngularJS documentation explains what a module is perfectly:


"You can think of a module as a container for the different parts of your app: controllers, services, filters, directives, and so on."





Okay great, but why would we or do we need to use one? There are a couple of reasons why you might want to use a module. Primarily, the most popular reason for their existence is the ease of creation of reusable code.

Imagine you are working on a blogging platform. You might build a module to allow for a media browser/uploader. This would be a collection of controllers, services, and filters all bundled up nicely. Should you wish to use this media browser in another project, then you'd just need to copy the module over.

There are also other reasons why you'd want to use modules. If you were unit testing, the tests only need to load relevant modules to keep them quick, and code becomes easier to understand and follow as each component is neatly packaged amongst other things.





Creating a module





It's very easy to create a module, and it allows us to extend upon the core much more, as Angular won't allow custom filters without one. Here, we've made a new module called contactsMgr. The second argument is just an empty array. We can have as many modules as we like and include them as dependencies, but for now we'll just leave it empty:

angular.module('contactsMgr', []);



We do, however, need to make a slight adjustment to how our controller is added. Currently it's just a function, but we need to add this to our module for Angular to be able to pick it up:

angular.module('contactsMgr', []) .controller('AppCtl', function($scope, jsonFilter){ … });



We can chain our controllers onto our module. You'll notice we need to use the controller method. The first argument is our controller name, and the second is our callback function with our injected services.

If you load up your app now, you'll see that nothing is working as expected and that the controller function cannot be found. That's because we haven't told Angular which module we wish to use. To do this, we just need to add the name of our module to the ng-app directive:

<html lang="en" ng-app="contactsMgr" ng-controller="AppCtl">



Once that's in, everything should start to work just as it did previously. You're now utilizing the module we just created.





Creating a filter





Now that our module is created and working, we can get to work on our improved limitTo filter. It's wise to work out exactly what we want our filter to do before we dive in. We can break down the functions we want to perform into a just a few short steps:

Take our input with a single argument for our limit

Check the length of the input against the limit

If the input is greater than the limit, truncate and add an ellipsis

Otherwise, just return the input





When working with modules, creating a filter follows a very similar pattern to creating a controller:

.filter('truncate', function(){ });



Just like we did when we moved our controller over to our new module, we use a new method, which accepts two arguments: the filter name and a callback function. As we learnt when we applied filters from the controller, when a filter is called, it actually returns a second function, so we need to add that here:

.filter('truncate', function(){ return function(){ }; })



We also discovered that the first argument of a filter is always the input or data that we're going to be filtering. Within this function, we can also include additional arguments. For our truncate filter, we only need one argument to tell the filter how many characters it should limit the string by:

.filter('truncate', function(){ return function(input, limit){ }; })



The construction of our filter is complete and we can now actually use the filter in exactly the same way as the filters we looked at before. Of course, we don't have any of our logic in place here and nothing is being returned from the filter function, so we'd actually end up displaying nothing on our page.

All we need to do now is check the length, truncate the string, and append an ellipsis. All of this can be done in one string with the help of a ternary statement:

return (input.length > limit) ? input.substr(0, limit)+'…' : input;



We check the length of the string, and if it's greater than the limit, we truncate it and append an ellipsis. If it doesn't match our condition, we return the original input. This is important because Angular won't display anything if nothing's returned from our filter.

Okay, let's put everything together and take a look at our completed function:

.filter('truncate', function(){ return function(input, limit){ return (input.length > limit) ? input.substr(0, limit)+'…' : input; }; })



Our new filter can now be used in the exact same way as the built-in limitTo filter, so let's swap the filter out and take a look:

{{'Lorem ipsum dolor sit amet' | truncate:15}}



As expected, the output now includes an ellipsis, whereas previously the string was just chopped off after the limit.





Self-test questions





How do we apply a filter from the view?

How do we pass through arguments to our filter from the view?

Which filter would we use to create a live search?

How can we use a filter from the controller?

What do we need to create before we can create our own filter?

What three arguments does the filter method accept?





Summary





By now you should definitely know what a filter does and why it is so helpful, but let's recap everything we've covered in this chapter.

We started off by looking at how a filter is applied directly from our view using the pipe symbol syntax and separating any arguments with a colon. Once we had the basics covered, it was time to look at the numerous included filters.

A few filters were basic, not requiring any arguments at all, but we also looked at some of the more advanced filters that allow us to order or filter an array of objects.

Apart from applying filters from the view, we also looked at two methods of filtering from our controller. We could either use the included $filter service or choose to inject our filters separately.

Finally, we looked at extending Angular to create our own filter to truncate text. Before we could do this, we had to look at creating a module to contain our filters and controllers. Once our module was up and running, we were able to create our filter and use it identically to the ones included.

We've now covered many of the core paradigms and ideologies of Angular. In the next chapter, we're going to look at setting up routing to handle multiple views and controllers for our contacts manager.





Chapter 4. Routing





All web apps will require more than one page or view, and Angular is well-equipped to handle this with its router. You may be familiar with routing in server-side frameworks, such as Ruby on Rails or Laravel. Angular's is, of course, entirely on the client side and all the magic happens within the HTML file our browser is pointed to.

In this chapter, we'll take a look at how we can create static routes as well as routes containing parameters. We'll also discover some of the pitfalls you might face.

Before we begin, let's plan out exactly what routes we're going to need for our contacts manager:

Index: This is going to be our main page, which will list all of our contacts in a table

View Contact: Here, we'll be able to see the contact in more detail and edit any of the information it presents

Add Contact: This will include a form that will allow us to add a contact to the manager





These are all of our essential routes; so let's take a look at how we can create them.





Installing ngRoute





Since AngularJS 1.2, the router has been packaged as a separate module outside of Angular's core. The file we're looking for—angular-route.min.js—can be downloaded from Angular's website below the Extras section within the download window.



Once you've got the download, drag it into your project's js directory and include it in the page after AngularJS:

<script src="assets/js/angular-route.min.js"></script>



We also need to let our module know that we want to utilize the router. We can do this by adding it to the module's dependency list. We can have as many dependencies as we like; currently all we need to include is ngRoute:

angular.module('contactsMgr', ['ngRoute'])





Creating basic routes





As we've already discovered, in order to configure the router within AngularJS, a module is required. In Chapter 3, Filters, we created one to allow us to build a custom filter. We can utilize this same module to build our routes.

Routes are created within the config method of our application's module:

angular.module('contactsMgr', ['ngRoute']) .config(function($routeProvider){ })



The method accepts an anonymous function that we can inject our required $routeProvider service into. This service has just two methods: when and otherwise. To add a route, we use the when method, which accepts two parameters: the path as a string and options for the route as an object:

angular.module('contactsMgr', ['ngRoute']) .config(function($routeProvider){ $routeProvider.when('/', {}); })



There are two properties within our route options object that we're particularly interested in: controller and templateUrl. The controller property calls an existing controller constructor or defines a new one using an anonymous function. Meanwhile, the templateUrl property allows us to define the path to an HTML file that will house our entire markup for that view. Alternatively, we could define the template directly within the route object. However, things can get messy fairly quickly that way and are only really recommended for one- or two-line templates.

Let's take a look at the route we're going to define for our index page:

$routeProvider.when('/', { controller: 'indexCtl', templateUrl: 'assets/partials/index.html' });



The path to the template is relative to our base HTML file; hence, it includes the assets directory in the path. We can now go ahead and create that HTML template. Angular refers to these as partials and we'll be using them for all of our views.

The controller argument within our route is optional, but we've included it as we're going to need one for our application. Let's create that controller to allow us to build models and functions exclusively for our index view.

Within our controller.js file, let's can chain this onto the end:

.controller('indexCtrl', function($scope){ });



Let's quickly add our second route with our config method. This will house our add-contact form:

$routeProvider.when('/', { controller: 'indexCtl', templateUrl: 'assets/partials/index.html' }) .when('/add-contact', { controller: 'addCtl', templateUrl: 'assets/partials/add.html' });



Just as we can with controllers, we can chain our routes. Now just create the relevant controller and partial:

.controller('addCtl', function($scope){ });



The last thing we need to do before Angular kicks the router into action is include the ng-view directive on our page. This pulls in the partial we've defined in the route.





Note


Note: You can only include ng-view once on per page.





<div class="container"> <ng-view></ng-view> </div>



This directive can be included as its own element. I've opted to include the directive as an element in my root index.html file. If you have anything in your container already, clear it out and replace it with ng-view instead.

If you open the project in your browser, you'll notice that the route has been appended to the URL with the # symbol preceding it. Unfortunately, if you're using Chrome, it's likely that the partials will fail to load. If you open up the console, you'll probably see a similar error to the following:

Cross origin requests are only supported for HTTP.

There are a couple of ways to fix this. We can either load the code up on a web server, or if we're using Chrome, we can run the browser using a flag to enable cross-origin requests over the file:// protocol on OS X or over c:/ on Windows.

On OS X, run the following in Terminal:

open -a 'Google Chrome' --args -allow-file-access-from-files



On other *nix-based systems run the following:

google-chrome --allow-file-access-from-files



On Windows, you need to edit the desktop shortcut to add a flag at the end of the Target:

C:\ ... \Application\chrome.exe --allow-file-access-from-files



If you don't want to run Chrome with a flag, you can run the contact manager on a web server. You could use the web server built into Python or PHP, or a full-stack app like MAMP or WAMP.

Change directory into your project and run the following command to server your application using Python's web server:

python -m SimpleHTTPServer 8000



You can now navigate to localhost:8000 in your browser to view your app. Alternatively, if you would prefer to run PHP's web server, you can do that with the following:

php -S localhost:8000





Routes with parameters





Okay, we've set up multiple routes but we still need to look at how we can include parameters within them. This is important to allow a level of dynamism within our contacts manager. For example, we're going to be using them to view a specific contact by referencing an ID number or index.

Adding a parameter is easy; we just need to add a placeholder in. This is done with a colon followed by the name of the parameter we wish to create. Let's take a look at the route we're going to make to view our contact. Once more, we can chain this onto our existing routes:

.when('contact/:id', { controller: 'contactCtl', templateUrl: 'assets/partials/contact.html' });



We can add as many parameters as required, and it's easy to pull these out in our controller. It's just a case of injecting a service into the controller and we'll have access to all route parameters as objects:

.controller('contactCtl', function($scope, $routeParams){ console.log($routeParams); });



If you navigate to localhost:8000/#/contact/1 and open up your console, you'll see the route parameters logged as a JS object:



That means we can access any of the properties on the object using the standard syntax:

$routeParams.id;





The fallback route





The last route we need to configure is the one that will show when no route is matched. You could create a 404 page for this, but let's take a look at how we can redirect a route instead of displaying a template.

To create our fallback route, we use the second method that the $routeProvider service gives us—otherwise:

.otherwise({ redirectTo: '/' });



Now, if the requested route doesn't match any of the ones defined in our router, Angular will redirect us back to our index page.





HTML5 routing or removing #





All of our essential routes are configured and we now have access to separate partials for all of them. That's great, but I'm not really happy with the routes following the # symbol in the URL. Thankfully, there's an easy way to eradicate that, by enabling what Angular calls html5Mode.

The mode enables Angular to take advantage of pushState in modern browsers while still providing a fallback for legacy browsers, such as IE 8.





Enabling HTML5Mode





To enable the new mode, we need to look at our config method again. Like before, we're going to need to inject a service into it:

.config(function($routeProvider, $locationProvider){ ... $locationProvider.html5Mode(true); })



You'll notice that we've now injected a second service: $locationProvider. This allows us to take advantage of the html5Mode method, which accepts a Boolean to turn it off or on.

The service also provides us with a second method, and though we won't be taking advantage of it during our build, it's still good to know. The hashPrefix method allows us to add a prefix after the # symbol in the URL. For example, we could add an exclamation mark and turn the prefix into a hashbang (#!):

$locationProvider.hashPrefix('!');



The following diagram shows our application's URL and splits the address down into the sections of our route:





Linking routes





Linking routes is no different than linking to pages on a website. We still use an anchor tag and in place of the link to the page we want to link the route.

For example, if we wanted to link up the Add Contact button in our navbar, we would do the following:

<a href="/add-contact">Add Contact</a>



Angular will automatically display the correct partial when we click the link and also change the URL. If you've opted not to use html5Mode, we can still link using an anchor tag, but the href attribute is a little different—we need to add the hash:

<a href="#/add-contact">Add Contact</a>





Self-test questions





What file/module do we need to include to enable routing?

Which method is used to create our routes?

What needs to be injected into the method for us to be able to create a route?

How do we create a route?

What can we use when none of our routes match the current path?

How can we remove the # symbol from the URL?





Summary





In this chapter, we transformed our application from a single page into a multi-page view and multi-route app that we can build our contacts manager upon. We started by planning out the essential routes in our application before installing the requisite module.

We then looked at how we can use the config method on our own module to set up our routes. This was done by injecting the $routeProvider service and using the when and other methods provided. This allowed us to set up static and dynamic routes containing parameters.

Finally, we looked at how we can remove the # symbol from the URL using HTML5's pushState and how we can link both types of routes. In the next chapter, we'll populate our partials with layouts we'll be building using Bootstrap.





Chapter 5. Building Views





In Chapter 4, Routing, we took a look at how we could turn our application into a multi-route and multi-view web app. We took advantage of Angular's router and set up partials for all of our core views. Now it's time to build up our views using Bootstrap so that we're ready to populate our app with data. Let's break down each of the partials one by one.





Populating the Index view





Our Index view is what's displayed when we first open the app. It is probably a good idea to list all of our contacts here, as we're going to need quick access to the information stored.

A table seems like it would be a good option, but first we need to think about what's going to be stored in our contact manager. Here's a list of possible items:

Name

Email address

Phone number

Address

Website

Notes

Gravatar (A global avatar service from the creators of WordPress)





Not all of this information will need to be displayed in our Index view. Don't forget that we also have the option to click through to the contact so we can display more information there.

A sensible option seems to be name, email address, and phone number displayed in our table with a link to click through.

Open up your Index's partial, which is located at assets/partials/index.html. Currently, this file is completely blank, so let's add a page header to begin with:

<div class="page-header"> <h1>All Contacts</h1> </div>



Remember, we don't need to include a container around this, as our partial is nested within our app's main index.html file on the route and we've already included the container there.

<table> <thead> <tr> <th>Name</th> <th>Email Address</th> <th>Phone Number</th> <th>Actions</th> </tr> </thead> <tbody> <tr> <td>Karan Bromwich</td> <td>karan@example.com</td> <td>01234 56734</td> <td><a href="#">View</a></td> </tr> <tr> <td>Declan Proud</td> <td>declan@example.com</td> <td>01234 567890</td> <td><a href="#">View</a></td> </tr> </tbody> </table>



That looks like a pretty good structure to me, but it's not looking too great on our page. Like most components, Bootstrap does include styles for tables, but we need to include an extra class to activate them. Simply add the table class to our opening table tag and Bootstrap will tidy it up immediately by adding some much-needed borders and making it span the full width.

There are also some secondary classes we can include to add a bit of extra pizzazz to our table:

table-bordered: includes a border around all sides of the table and all cells.

table-striped: adds a grey background to alternating rows to make it easier to read.

table-hover: changes the background of the row when hovered upon.

table-condensed: removes some of the top and bottom padding, making it take up less vertical height.





Apart from these classes, there are also some classes that can be applied to rows or cells specifically, which color the background of the rows to give it some context:

active: adds the hover state to the row

success: colors the background green, indicating a successful action

info: uses below to draw attention to the row or cell

warning: indicates that an action may be required and colors the cell yellow

danger: demonstrates an error or problem





For now, I'm just going to add the table-striped class, but it's up to you if you want to experiment with some of the other included classes.

Our table is beginning to look great. You'll notice, however, that on smaller screen sizes the table is cut off horizontally. To combat this, we need to wrap our table in another element that will allow it to scroll at smaller sizes:

<div class="table-responsive"> … </div>



That's much better, as our content is no longer getting cut off or breaking our responsive layout. The last thing I want to do to our table is turn that view link into a button. Bootstrap comes with a plethora of button styles that we can take advantage of.

All buttons are combinations of the following classes:

Default button class

Context class

Size class





Together, these give us all the control we need to pick the right button for the right occasion. Let's put these together to create a button that works within our table:

<a href="#" class="btn btn-default btn-xs">View</a>



Our first class here provides some default button styles; the second gives it color (in this case, the default is white), and the final class defines the size of the button. Alternatively, we could have used one of the following classes to change the color of our button:


Class Name

Description



btn-default

White button with a grey border



btn-primary

Blue button



btn-success

Green button



btn-info

Light blue button



btn-warning

Orange button



btn-danger

Red button



btn-link

Styled to look like a link





Apart from providing the default size, there are also three classes we can utilize to change the size of our buttons. In the preceding code, we've already used btn-xs to make our button really small, but we could have also used btn-sm to make it a little smaller than the default or btn-lg to make it larger.

Our Index view is looking pretty complete to me now, and it's ready to be populated when we're ready. Let's take a look at the finished product, as seen in the following image:





Populating the Add Contact view





It's quite clear what we're going to need in our Add Contact view—a form to allow us to enter the required information. Thankfully, Bootstrap provides us with a lot of control when arranging our fields. We've already worked out what data we're going to be storing, so it's just a case of working out what type of field is best:

Name: Text field

Email address: Email field

Phone number: Tel field

Address: Textarea

Website: Text field

Notes: Textarea

Gravatar: N/A





As Gravatar uses an email address to serve images, we don't need to request any additional information here. We've got a total of six fields, so I think two columns would be great here.

The first thing we need to do is open up our form. Once we've done that, we can add our columns inside. We've already got our container class, so we just need to open up a new row and add our two columns. As we learnt in Chapter 2, Let's Build with AngularJS and Bootstrap, Bootstrap's grid-system is 12-columns wide, so we need to keep that in mind when creating our layout:

<form> <div class="row"> <div class="col-sm-6"> </div> <div class="col-sm-6"> </div> </div> </form>



Just as before, we're using the col-sm prefix to allow our columns to collapse down on smaller tablets and mobile devices.

We could just pop our labels and inputs directly within our columns, but for optimum spacing, we need to wrap our elements in a div tag with the form-group class:

<div class="form-group"> <label for="name">Name</label> <input type="text" id="name"> </div>



To take advantage of Bootstrap's styles in our inputs, we do to add the form-control class to them. If we add the control-label class to our label, it will also give us a bit of extra padding:

<div class="form-group"> <label for="name" class="control-label">Name</label> <input type="text" id="name" class="form-control"> </div>



Let's quickly add the rest of our elements in. I'm going to add name, phone number, and address to the left column and email address, website, and notes to the right.





Horizontal forms





Okay, that's looking great. If you're not a fan of the labels up top, we can position them on the left with a bit of tweaking. By adding the form-horizontal class to our opening form tag, our form-group classes behave as grid rows, meaning we can use column classes in the elements within them. Let me show you what all this means:

<div class="form-group"> <label for="name" class="col-sm-4 control-label">Name</label> <div class="col-sm-8"> <input type="text" id="name" class="form-control"> </div> </div>



After including form-horizontal, you'll notice we can now add Bootstrap's column classes to our label. As form-control sets the width to 100%, it matches the parent we need to wrap it in an additional element. As we've also included the control-label class, the label is centered vertically.

Our form looks a lot less cluttered using the form-horizontal class, so let's go ahead and wrap all of our inputs in that form-control element.

There might be times where you need to give the user a bit more information about what's required. This can be included underneath the related input by using a span tag with the help-block class:

<div class="form-group"> <label for="notes" class="col-sm-4 control-label">Notes</label> <div class="col-sm-8"> <textarea id="notes" class="form-control"></textarea> <span class="help-block">Any additional information about the contact.</span> </div> </div>



The last thing we need to do is add a submit button. In previous versions of Bootstrap, this would usually have been wrapped in an element with the form-actions class. However, in Bootstrap 3, we just need to use the same form-group we've been using all along. If you're using the form-horizontal style, you'll need to offset your columns:

<div class="form-group"> <div class="col-sm-offset-4 col-sm-8"> <button class="btn btn-primary">Add Contact</button> </div> </div>



As our label spans four columns, we need to offset our button the same amount here so it doesn't look misaligned.

I quite liked the contrast the old form-actions class used to provide. Thankfully, we can achieve a similar result using Bootstrap's well component. Here, I've moved our form-group class containing the submit button to be directly below our existing row (remember, form-horizontal makes all groups act like rows) and have also added a well class:

<div class="form-group well"> <div class="col-sm-offset-2 col-sm-10"> <input type="submit" class="btn btn-primary" value="Add Contact"> </div> </div>



Finally, to complete the page, I'm going to give it the same page-header element we added to our Index view and position it at the very top of our markup:

<div class="page-header"> <h1>Add Contact</h1> </div>



The end result will look as seen in the following screenshot:





Populating the View Contact view





The final partial we need to populate is the screen where we can view our contact. I was tempted to just add this in as a form but I quite like the idea of having static text, which we can choose to edit certain sections of.

We're going to need to display all the same information we entered in the Add Contact view, as well as our Gravatar.





Title and Gravatar





To begin, we're going to include a page-header class. This is going to house an h1 tag with our contact's name within:

<div class="page-header"> <h1>Declan Proud</h1> </div>



I also want to include our Gravatar here, so let's take a look at how we can achieve that. For now, we're just going to use some placeholder images from http://placehold.it. If you've not used this website before, it just serves up placeholders of any size. I think we just need a 50px x 50px image, and we can pull that in with the following:

<img src="http://placehold.it/50x50">



Feel free to tweak the size to something of your liking. We can slot this directly before the name of our contact within the h1 tag. I'm also opting to add the img-circle class:

<div class="page-header row"> <h1><img src="http://placehold.it/50x50" class="img-circle"> Declan Proud</h1> </div>



The class is one of three available to give some style to images and adds a 50% border radius to the image to create a circle. Also available is img-rounded, which rounds off the corners a little, as well as img-thumbnail, which adds a nice double border.





The form-horizontal class





We're quite lucky in that we can recycle quite a lot of what we did in the Add Contact view. The form-horizontal class will work just as well here with static content instead of fields. This page will later become our editing screen as well as our contact card, so it's handy that we can use the class for both views.

This time, however, we're going to use a d