Responsive website using AngularJS

Tutorials

In today’s tutorial, I’m going to show you the process of creating almost an entire website with a new library – AngularJS. However, firstly, I would like to introduce to you the AngularJS. AngularJS is a magnificent framework for creating Web applications. This framework lets you extend HTML’s syntax to express your application’s components clearly and succinctly, and lets use standard HTML as your main template language. Plus, it automatically synchronizes data from your UI with your javascript objects through 2-way data binding. If you’ve ever worked with jQuery, the first thing to understand about Angular is that this is a completely different instrument. jQuery – is a library, but AngularJS – is framework. When your code works with the library, it decides when to call a particular function or operator. In the case of the framework, you implement event handlers, and the framework decides at what moment it needs to invoke them.

Using this framework allows us to clearly distinguish between website templates (DOM), models and functionality (in controllers). Let’s come back to our template, take a look at our result:

template preview


Live Demo

[sociallocker]

download in package

[/sociallocker]


Description

This template is perfect for business sites. It consists of several static pages: the list of projects, privacy and about pages. Each product has its own page. There is also a contact form for communication. That is – all that is necessary for any small website. Moreover, it is also responsive template, thus it looks good on any device.

I hope you liked the demo, so if you’re ready – let’s start making this application. Please prepare a new folder for our project, and then, create next folders in this directory:

  • css – for stylesheet files
  • images – for image files
  • js – for javascript files (libraries, models and controllers)
  • pages – for internal pages

Stage 1. HTML

The main layout consists of four main sections: header with navigation, hidden ‘contact us’ form, main content section and footer. First at all we have to prepare a proper header:

index.html

01 <head>
02     <meta charset="utf-8" />
03     <meta name="author" content="Script Tutorials" />
04     <title>Responsive website using AngularJS | Script Tutorials</title>
05     <meta name="description" content="Responsive website using AngularJS - demo page">
06     <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
07     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
08     <!-- add styles -->
09     <link href="css/style.css" rel="stylesheet" type="text/css" />
10     <!-- add javascripts -->
11     <script src="js/jquery-2.0.3.min.js"></script>
12     <script src="js/angular.min.js"></script>
13     <script src="js/app.js"></script>
14     <script src="js/controllers.js"></script>
15 </head>

As you can see, it’s quite ordinary header. Now – the header with the navigation:

01 <header>
02     <div class="wrap">
03         <!-- logo -->
04         <a href="#!"><img class="logo" src="images/logo.png" /></a>
05         <!-- navigation menu -->
06         <nav>
07             <ul>
08                 <li><a id="workBtn" href="#!/" ng-class="{activeSmall:part == 'projects'}" >Our Projects</a></li>
09                 <li><a id="privacyBtn" href="#!/privacy" ng-class="{activeSmall:part == 'privacy'}">Privacy &amp; Terms</a></li>
10                 <li><a id="aboutBtn" href="#!/about" ng-class="{activeSmall:part == 'about'}">About</a></li>
11                 <li style="margin-right:0px"><a id="contactBtn" class="active" href="javascript: void(0)" ng-click="showForm()">Contact Us</a></li>
12             </ul>
13         </nav>
14     </div>
15 </header>

The ordinary logo, the menu is the usual UL-LI menu. Next section is more interesting – ‘Contact Us’ form:

01 <!-- contact us form -->
02 <div class="paddRow contactRow">
03     <div class="wrap">
04         <div class="head">Contact Us</div>
05         <img class="close" src="images/close.png" ng-click="closeForm()" />
06         <form ng-submit="save()" class="contactForm" name="form" ng-hide="loaded">
07             <input class="input" required="required" type="text" name="name" placeholder="your name" ng-model="message.name" />
08             <input class="input email" required="required" type="email" name="email" value="" placeholder="your email" ng-model="message.email" /><br />
09             <textarea class="textarea" rows="5" required="required" placeholder="your message" ng-model="message.text" ></textarea>
10             <button class="btn green">send message</button>
11         </form>
12         <!-- contact us form response messages -->
13         <div ng-show="process" style="text-align:center">
14             <img class="loader" src="images/loader.png" />
15         </div>
16         <div ng-show="success"><p>Your message has been sent, thank you.</p></div>
17     </div>
18 </div>

Finally, the last key element: the main content section:

1 <!-- main content -->
2 <div style="position:relative">
3     <div style="width:100%" ng-view ng-animate="{enter: 'view-enter', leave: 'view-leave'}"></div>
4 </div>

Have you noticed the numerous ‘ng-‘ directives? All these directives allow us to do various actions directly in the DOM, for example:

  • ng-class – the ngClass allows you to set CSS classes on HTML an element, dynamically, by databinding an expression that represents all classes to be added.
  • ng-click – the ngClick allows you to specify custom behavior when element is clicked.
  • ng-hide – the ngHide directive shows and hides the given HTML element conditionally based on the expression provided to the ngHide attribute.
  • ng-include – fetches, compiles and includes an external HTML fragment.
  • ng-model – is a directive that tells Angular to do two-way data binding.
  • ng-show – the ngShow directive shows and hides the given HTML element conditionally based on the expression provided to the ngShow attribute.
  • ng-submit – enables binding angular expressions to onsubmit events.

Stage 2. CSS

In this rather large section you can find all the styles used

css/style.css

001 /* general settings */
002 html {
003   min-height:100%;
004   overflow-x:hidden;
005   overflow-y:scroll;
006   position:relative;
007   width:100%;
008 }
009 body {
010   background-color:#e6e6e6;
011   color:#FFF;
012   font-weight:100;
013   margin:0;
014   min-height:100%;
015   width:100%;
016 }
017 a {
018   text-decoration:none;
019 }
020 a img {
021   border:none;
022 }
023 h1 {
024   font-size:3.5em;
025   font-weight:100;
026 }
027 p {
028   font-size:1.5em;
029 }
030 input,textarea {
031   -webkit-appearance:none;
032   background-color:#f7f7f7;
033   border:none;
034   border-radius:3px;
035   font-size:1em;
036   font-weight:100;
037 }
038 input:focus,textarea:focus {
039   border:none;
040   outline:2px solid #7ed7b9;
041 }
042 .left {
043   float:left;
044 }
045 .right {
046   float:right;
047 }
048 .btn {
049   background-color:#fff;
050   border-radius:24px;
051   color:#595959;
052   display:inline-block;
053   font-size:1.4em;
054   font-weight:400;
055   margin:30px 0;
056   padding:10px 30px;
057   text-decoration:none;
058 }
059 .btn:hover {
060   opacity:0.8;
061 }
062 .wrap {
063   -moz-box-sizing:border-box;
064   -webkit-box-sizing:border-box;
065   box-sizing:border-box;
066   margin:0 auto;
067   max-width:1420px;
068   overflow:hidden;
069   padding:0 50px;
070   position:relative;
071   width:100%;
072 }
073 .wrap:before {
074   content:'';
075   display:inline-block;
076   height:100%;
077   margin-right:-0.25em;
078   vertical-align:middle;
079 }
080 /* header section */
081 header {
082   height:110px;
083 }
084 header .wrap {
085   height:100%;
086 }
087 header .logo {
088   margin-top:1px;
089 }
090 header nav {
091   float:right;
092   margin-top:17px;
093 }
094 header nav ul {
095   margin:1em 0;
096   padding:0;
097 }
098 header nav ul li {
099   display:block;
100   float:left;
101   margin-right:20px;
102 }
103 header nav ul li a {
104   border-radius:24px;
105   color:#aaa;
106   font-size:1.4em;
107   font-weight:400;
108   padding:10px 27px;
109   text-decoration:none;
110 }
111 header nav ul li a.active {
112   background-color:#c33c3a;
113   color:#fff;
114 }
115 header nav ul li a.active:hover {
116   background-color:#d2413f;
117   color:#fff;
118 }
119 header nav ul li a:hover,header nav ul li a.activeSmall {
120   color:#c33c3a;
121 }
122 /* footer section */
123 footer .copyright {
124   color:#adadad;
125   margin-bottom:50px;
126   margin-top:50px;
127   text-align:center;
128 }
129 /* other objects */
130 .projectObj {
131   color:#fff;
132   display:block;
133 }
134 .projectObj .name {
135   float:left;
136   font-size:4em;
137   font-weight:100;
138   position:absolute;
139   width:42%;
140 }
141 .projectObj .img {
142   float:right;
143   margin-bottom:5%;
144   margin-top:5%;
145   width:30%;
146 }
147 .paddRow {
148   background-color:#dadada;
149   color:#818181;
150   display:none;
151   padding-bottom:40px;
152 }
153 .paddRow.aboutRow {
154   background-color:#78c2d4;
155   color:#FFF !important;
156   display:block;
157 }
158 .paddRow .head {
159   font-size:4em;
160   font-weight:100;
161   margin:40px 0;
162 }
163 .paddRow .close {
164   cursor:pointer;
165   position:absolute;
166   right:50px;
167   top:80px;
168   width:38px;
169 }
170 .about {
171   color:#818181;
172 }
173 .about section {
174   margin:0 0 10%;
175 }
176 .about .head {
177   font-size:4em;
178   font-weight:100;
179   margin:3% 0;
180 }
181 .about .subHead {
182   font-size:2.5em;
183   font-weight:100;
184   margin:0 0 3%;
185 }
186 .about .txt {
187   width:60%;
188 }
189 .about .image {
190   width:26%;
191 }
192 .about .flLeft {
193   float:left;
194 }
195 .about .flRight {
196   float:right;
197 }
198 .projectHead.product {
199   background-color:#87b822;
200 }
201 .projectHead .picture {
202   margin-bottom:6%;
203   margin-top:6%;
204 }
205 .projectHead .picture.right {
206   margin-right:-3.5%;
207 }
208 .projectHead .text {
209   position:absolute;
210   width:49%;
211 }
212 .projectHead .centerText {
213   margin:0 auto;
214   padding-bottom:24%;
215   padding-top:6%;
216   text-align:center;
217   width:55%;
218 }
219 .image {
220   text-align:center;
221 }
222 .image img {
223   vertical-align:top;
224   width:100%;
225 }
226 .contactForm {
227   width:50%;
228 }
229 .input {
230   -moz-box-sizing:border-box;
231   -webkit-box-sizing:border-box;
232   box-sizing:border-box;
233   margin:1% 0;
234   padding:12px 14px;
235   width:47%;
236 }
237 .input.email {
238   float:right;
239 }
240 button {
241   border:none;
242   cursor:pointer;
243 }
244 .textarea {
245   -moz-box-sizing:border-box;
246   -webkit-box-sizing:border-box;
247   box-sizing:border-box;
248   height:200px;
249   margin:1% 0;
250   overflow:auto;
251   padding:12px 14px;
252   resize:none;
253   width:100%;
254 }
255 ::-webkit-input-placeholder {
256   color:#a7a7a7;
257 }
258 :-moz-placeholder {
259   color:#a7a7a7;
260 }
261 ::-moz-placeholder { /* FF18+ */
262   color:#a7a7a7;
263 }
264 :-ms-input-placeholder {
265   color:#a7a7a7;
266 }
267 .loader {
268   -moz-animation:loader_rot 1.3s linear infinite;
269   -o-animation:loader_rot 1.3s linear infinite;
270   -webkit-animation:loader_rot 1.3s linear infinite;
271   animation:loader_rot 1.3s linear infinite;
272   height:80px;
273   width:80px;
274 }
275 @-moz-keyframes loader_rot {
276   from {
277     -moz-transform:rotate(0deg);
278   }
279   to {
280     -moz-transform:rotate(360deg);
281   }
282 }
283 @-webkit-keyframes loader_rot {
284   from {
285     -webkit-transform:rotate(0deg);
286   }
287   to {
288     -webkit-transform:rotate(360deg);
289   }
290 }
291 @keyframes loader_rot {
292   from {
293     transform:rotate(0deg);
294   }
295   to {
296     transform:rotate(360deg);
297   }
298 }
299 .view-enter,.view-leave {
300   -moz-transition:all .5s;
301   -o-transition:all .5s;
302   -webkit-transition:all .5s;
303   transition:all .5s;
304 }
305 .view-enter {
306   left:20px;
307   opacity:0;
308   position:absolute;
309   top:0;
310 }
311 .view-enter.view-enter-active {
312   left:0;
313   opacity:1;
314 }
315 .view-leave {
316   left:0;
317   opacity:1;
318   position:absolute;
319   top:0;
320 }
321 .view-leave.view-leave-active {
322   left:-20px;
323   opacity:0;
324 }

Please note, that CSS3 transitions are used, it means that our demonstration will only work in most modern browsers (FF, Chrome, IE10+ etc)

Stage 3. JavaScript

As I mentioned before, our main controller and the model are separated. The navigation menu can be handled here, and we also can operate with the contact form.

js/app.js

01 'use strict';
02 // angular.js main app initialization
03 var app = angular.module('example359', []).
04     config(['$routeProvider'function ($routeProvider) {
05       $routeProvider.
06         when('/', { templateUrl: 'pages/index.html', activetab: 'projects', controller: HomeCtrl }).
07         when('/project/:projectId', {
08           templateUrl: function (params) { return 'pages/' + params.projectId + '.html'; },
09           controller: ProjectCtrl,
10           activetab: 'projects'
11         }).
12         when('/privacy', {
13           templateUrl: 'pages/privacy.html',
14           controller: PrivacyCtrl,
15           activetab: 'privacy'
16         }).
17         when('/about', {
18           templateUrl: 'pages/about.html',
19           controller: AboutCtrl,
20           activetab: 'about'
21         }).
22         otherwise({ redirectTo: '/' });
23     }]).run(['$rootScope''$http''$browser''$timeout'"$route"function ($scope, $http, $browser, $timeout, $route) {
24         $scope.$on("$routeChangeSuccess"function (scope, next, current) {
25           $scope.part = $route.current.activetab;
26         });
27         // onclick event handlers
28         $scope.showForm = function () {
29           $('.contactRow').slideToggle();
30         };
31         $scope.closeForm = function () {
32           $('.contactRow').slideUp();
33         };
34         // save the 'Contact Us' form
35         $scope.save = function () {
36           $scope.loaded = true;
37           $scope.process = true;
38           $http.post('sendemail.php', $scope.message).success(function () {
39               $scope.success = true;
40               $scope.process = false;
41           });
42         };
43   }]);
44 app.config(['$locationProvider'function($location) {
45     $location.hashPrefix('!');
46 }]);

Pay attention, when we request a page, it loads an appropriate page from the ‘pages’ folder: about.html, privacy.html, index.html. Depending on selected product, it opens one of product pages: product1.html, product2.html, product3.html or product4.html

In the second half there are functions to slide the contact form and to handle with it’s submit process (to the sendemail.php page). Next is the controller file:

js/controllers.js

01 'use strict';
02 // optional controllers
03 function HomeCtrl($scope, $http) {
04 }
05 function ProjectCtrl($scope, $http) {
06 }
07 function PrivacyCtrl($scope, $http, $timeout) {
08 }
09 function AboutCtrl($scope, $http, $timeout) {
10 }

It is empty, because we have nothing to use here at the moment

Stage 4. Additional pages

AngularJS loads pages asynchronous, thereby increasing the speed. Here are templates of all additional pages used in our project:

pages/about.html

01 <div style="width:100%">
02     <div class="paddRow aboutRow">
03         <div class="wrap">
04             <div class="head">About Us</div>
05             <p>Script Tutorials is one of the largest web development communities. We provide high quality content (articles and tutorials) which covers all the web development technologies including HTML5, CSS3, Javascript (and jQuery), PHP and so on. Our audience are web designers and web developers who work with web technologies.</p>
06         </div>
07     </div>
08     <div style="background-color:#f5f5f5">
09         <div class="wrap about">
10             <div class="head">Additional information</div>
11             <section>
12                 <div class="image flRight">
13                     <img src="images/ang.png" class="abIcon">
14                 </div>
15                 <div class="txt flLeft">
16                     <h2 class="subHead">Promo 1</h2>
17                     <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et ligula accumsan, pharetra nibh nec, facilisis nulla. In pretium semper venenatis. In adipiscing augue elit, at venenatis enim suscipit a. Fusce vitae justo tristique, ultrices mi metus.</p>
18                 </div>
19                 <div style="clear:both"></div>
20             </section>
21             .....
22         </div>
23     </div>
24     <ng-include src="'pages/footer.html'"></ng-include>
25 </div>

pages/privacy.html

01 <div style="width:100%">
02     <div class="paddRow aboutRow">
03         <div class="wrap">
04             <div class="head">Privacy &amp; Terms</div>
05             <p> By accessing this web site, you are agreeing to be bound by these web site Terms and Conditions of Use, all applicable laws and regulations, and agree that you are responsible for compliance with any applicable local laws. If you do not agree with any of these terms, you are prohibited from using or accessing this site. The materials contained in this web site are protected by applicable copyright and trade mark law.</p>
06         </div>
07     </div>
08     <div style="background-color:#f5f5f5">
09         <div class="wrap about">
10             <div class="head">Other information</div>
11             <section>
12                 <div class="image flLeft">
13                     <img src="images/ang.png" class="abIcon">
14                 </div>
15                 <div class="txt flRight">
16                     <h2 class="subHead">Header 1</h2>
17                     <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et ligula accumsan, pharetra nibh nec, facilisis nulla. In pretium semper venenatis. In adipiscing augue elit, at venenatis enim suscipit a. Fusce vitae justo tristique, ultrices mi metus.</p>
18                 </div>
19                 <div style="clear:both"></div>
20             </section>
21             .....
22         </div>
23     </div>
24     <ng-include src="'pages/footer.html'"></ng-include>
25 </div>
1 <footer>
2     <div class="copyright"><a href="https://www.script-tutorials.com/" target="_blank">Copyright © 2013 Script Tutorials</a></div>
3 </footer>

pages/index.html

01 <div style="width:100%">
02     <a class="projectObj" href="#!/project/product1" style="background-color:#87b822">
03         <div class="wrap">
04             <div class="name">Product #1</div>
05             <img class="img" src="images/element.png" />
06         </div>
07     </a>
08     <a class="projectObj" href="#!/project/product2" style="background-color:#3f91d2">
09         <div class="wrap">
10             <div class="name">Product #2</div>
11             <img class="img" src="images/element.png" />
12         </div>
13     </a>
14     <a class="projectObj" href="#!/project/product3" style="background-color:#f1784d">
15         <div class="wrap">
16             <div class="name">Product #3</div>
17             <img class="img" src="images/element.png" />
18         </div>
19     </a>
20     <a class="projectObj" href="#!/project/product4" style="background-color:#f0c42c">
21         <div class="wrap">
22             <div class="name">Product #4</div>
23             <img class="img" src="images/element.png" />
24         </div>
25     </a>
26     <ng-include src="'pages/footer.html'"></ng-include>
27 </div>
28 <script>
29 $('.projectObj').bind('click', function (e) {
30     e.preventDefault();
31     var me = this;
32     var width = $(me).width() / 1.5;
33     $(me).find('.wrap').width($(me).find('.wrap').width());
34     $(me).animate({
35         opacity: 0,
36         marginLeft: -width
37     }, 500);
38     var delayN = 150;
39     var delayP = 150;
40     var nextEl = $(me).nextAll('.projectObj');
41     var prevEl = $(me).prevAll('.projectObj');
42     nextEl.each(function (index, elem) {
43         setTimeout(function () {
44             $(elem).find('.wrap').width($(elem).find('.wrap').width());
45             $(elem).animate({
46                 opacity: 0,
47                 marginLeft: -width
48               }, 500, function () {
49             });
50         }, delayN);
51         delayN += 100;
52     });
53     prevEl.each(function (index, elem) {
54         setTimeout(function () {
55             $(elem).find('.wrap').width($(elem).find('.wrap').width());
56             $(elem).animate({
57                 opacity: 0,
58                 marginLeft: -width
59               }, 500, function () {
60             });
61         }, delayP);
62         delayP += 100;
63     });
64     setTimeout(function () {
65         document.location = $(me).attr('href');
66     },1000)
67     return false;
68 });
69 </script>

Finally, the product pages. All of them are prototype, so I decided to publish only one of them.

pages/index.html

01 <div style="width:100%">
02     <div class="projectHead product">
03         <div class="wrap">
04             <div class="text left">
05                 <h1>Product 1 page</h1>
06                 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et ligula accumsan, pharetra nibh nec, facilisis nulla. In pretium semper venenatis. In adipiscing augue elit, at venenatis enim suscipit a. Fusce vitae justo tristique, ultrices mi metus.</p>
07                 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et ligula accumsan, pharetra nibh nec, facilisis nulla. In pretium semper venenatis. In adipiscing augue elit, at venenatis enim suscipit a. Fusce vitae justo tristique, ultrices mi metus.</p>
08                 <a class="btn" href="javascript: void(0)">download the app</a>
09             </div>
10             <img class="picture right" src="images/element.png" />
11             <div style="clear:both"></div>
12         </div>
13     </div>
14     <ng-include src="'pages/footer.html'"></ng-include>
15 </div>

Finishing touches – responsive styles

All of these styles are needed to make our results look equally well on all possible mobile devices and monitors:

001 @media (max-width1200px) {
002     body {
003       font-size:90%;
004     }
005     h1 {
006       font-size:4.3em;
007     }
008     p {
009       font-size:1.3em;
010     }
011     header {
012       height:80px;
013     }
014     header .logo {
015       margin-top:12px;
016       width:200px;
017     }
018     header nav {
019       margin-top:11px;
020     }
021     header nav ul li {
022       margin-right:12px;
023     }
024     header nav ul li a {
025       border-radius:23px;
026       font-size1.3em;
027       padding:10px 12px;
028     }
029     .wrap {
030       padding:0 30px;
031     }
032     .paddRow .close {
033       right:30px;
034     }
035 }
036 @media (max-width900px) {
037     .contactForm {
038       width:100%;
039     }
040 }
041 @media (max-width768px) {
042     body {
043       font-size:80%;
044       margin:0;
045     }
046     h1 {
047       font-size:4em;
048     }
049     header {
050       height:70px;
051     }
052     header .logo {
053       margin-top:20px;
054       width:70px;
055     }
056     header nav {
057       margin-top:8px;
058     }
059     header nav ul li {
060       margin-right:5px;
061     }
062     header nav ul li a {
063       border-radius:20px;
064       font-size:1.1em;
065       padding:8px;
066     }
067     .wrap {
068       padding:0 15px;
069     }
070     .projectObj .name {
071       font-size:3em;
072     }
073     .paddRow {
074       padding-bottom:30px;
075     }
076     .paddRow .head {
077       font-size:3em;
078       margin:30px 0;
079     }
080     .paddRow .close {
081       right:20px;
082       top:60px;
083       width:30px;
084     }
085     .projectHead .picture {
086       width:67%;
087     }
088     .projectHead .picture.right {
089       margin-right:16.5%;
090     }
091     .projectHead .text {
092       position:static;
093       width:100%;
094     }
095     .projectHead .centerText {
096       width:70%;
097     }
098     .view-enter,.view-leave {
099       -webkit-transform:translate3d(0,0,0);
100       transform:translate3d(0,0,0);
101     }
102 }
103 @media (max-width480px) {
104     body {
105       font-size:70%;
106       margin:0;
107     }
108     header {
109       height:50px;
110     }
111     header .logo {
112       display:none;
113     }
114     header nav {
115       margin-top:3px;
116     }
117     header nav ul li {
118       margin-right:3px;
119     }
120     header nav ul li a {
121       border-radius:20px;
122       font-size:1.3em;
123       padding:5px 14px;
124     }
125     #contactBtn {
126       display:none;
127     }
128     .wrap {
129       padding:0 10px;
130     }
131     .paddRow {
132       padding-bottom:20px;
133     }
134     .paddRow .head {
135       margin:20px 0;
136     }
137     .paddRow .close {
138       right:10px;
139       top:45px;
140       width:20px;
141     }
142     .about .image {
143       margin:10% auto;
144       width:60%;
145     }
146     .about .abIcon {
147       display:inline;
148     }
149     .projectHead .centerText {
150       width:90%;
151     }
152     .about .txt,.input {
153       width:100%;
154     }
155     .about .flLeft,.about .flRight,.input.email {
156       float:none;
157     }
158 }

Live Demo

Conclusion

That’s all for today, thanks for your patient attention, and if you really like what we did today – share it with all your friends in your social networks using the form below.

Rate article