This is the first part of Building SPA using AngularJS Series. The topics we’ll cover are:
- Building SPA using AngularJS – Part 1 (This Post)
- Building SPA using AngularJS – Part 2
- Building SPA using AngularJS – Part 3
Update (2014-May-5) New post which covers adding GulpJS as Task Runner for our AngularJS application:
Update (2014-June-1) New post which covers Implementing AngularJS Token Authentication using ASP.NET Web API 2 and Owin middleware:
Building SPA using AngularJS – Part 1
In this post we’ll take baby steps to know what we are doing and understand some basic steps which are fundamentals for completing our application.
Step 1: Downloading third party libraries
To get started we need to download all libraries needed in our application, we can use Nuget package manager but I prefer to download them manually to know what are the files really needed.
- AngularJS: to get AngularJS let’s visit http://angularJS.org then download the zip build, we’ll use version (1.2.6)
- Angular Boostrap UI: we need to download the zip build for UI Boostrap from http://angular-ui.github.io/bootstrap/. We’ll use version (0.9.0)
- Toaster: It is non blocking notification JavaScript library where will use it to display notification for users, we need to download it from here.
- Loading Bar: We’ll use the loading bar as UI indication for every XHR request we’ll made, to get this plugin we need to download it from here.
- UI Bootstrap theme: to style our application, we need to download a free bootstrap ready made theme from http://bootswatch.com/ I’ve used a theme named “Amelia”
Step 2: Organizing your project structure
If you are using VS 2012 then create new Basic MVC 4 Web Application and name it “FoursquareAngularJS.Web”, we’ll not use any features of MVC in our application but I prefer to use this template because we can use Web API directly, you can do the same using Web Forms template if you wish. If you are using VS 2013 you can choose the empty template but do not forget to check “Web API” as a core reference. Having organized project structure from the beginning is vital for your project success, because we’ll deal with many small JavaScript files, you will notice how important it is once the project grows in size. There are different schools to organize your project all come with pros and cons, but I prefer to organize my files based on architecture types (Services, controllers, directives, filters, views, etc..). So in our case all our Angular code will be contained in Parent folder named “app” as the image below:
For the third part libraries (AngularJS & Bootstrap UI) or any ready made JavaScript libraries we’ll add them to root folder named “scripts”, you can delete any prior JS files in this folder and add the new extracted files from step 1.
Step 3: Adding the shell page:
Now we’ll add the “Single Page” which is a container for our application, it will contain the navigation menu and Angular directive for rendering different application views “pages”. Those views can be rendered in JavaScript without the need to download new page from the server, we’ll gain performance here because we are not downloading new HTML page each time the user navigating to a new page. After adding the “index.html” page we need to reference the 3rd party JavaScript and CSS files needed as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
<html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" /> <title>Explore Foursquare</title> <link href="content/css/bootstrap.min.css" rel="stylesheet" /> <link href="content/css/amelia-bootstrap-theme.min.css" rel="stylesheet" /> <link href="Content/css/toaster.css" rel="stylesheet" /> <link href="content/css/loading-bar.min.css" rel="stylesheet" /> <link href="content/css/site.css" rel="stylesheet" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <link href="favicon.ico" rel="shortcut icon" /> </head> <body> <div id="page"> <header class="container"> <div class="navbar navbar-default" id="menu"> <div class="navbar-header"> <button class="btn btn-success navbar-toggle"></button> <div id="logo"> <h4>Explore Foursquare</h4> </div> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="#/explore">Explore</a></li> <li><a href="#/places">My Places</a></li> <li><a href="#/about">About</a></li> </ul> </div> </div> </header> <section class="container" id="body"> <div></div> </section> <hr /> <footer class="container"> Created by Taiseer Joudeh. Twitter: <a href="http://twitter.com/tjoudeh" target="_blank">@tjoudeh</a> Taiseer Joudeh Blog: <a href="http://www.bitoftech.net" target="_blank">bitoftech.net</a> </footer> </div> </body> </html> <!-- 3rd party libraries --> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular-route.min.js"></script> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular-resource.min.js"></script> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular-animate.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.9.0/ui-bootstrap.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.9.0/ui-bootstrap-tpls.min.js"></script> <script type="text/javascript" src="scripts/toaster.js"></script> <script type="text/javascript" src="scripts/loading-bar.min.js"></script> |
Till now we have created the container for our application, we will visit this page frequently to reference the new JS files we’ll add during this tutorial.
Step 4: “Booting up” our application
We’ll add file named “app.js” in the root of folder “app”, this file is responsible to create modules in applications, in our case we’ll have a single module called “FoursquareApp”, we can consider module as collection of services, directives, filters which is used in the application. Each module has configuration block where it gets applied to the application during the bootstrap process.
Step 5: Configuring AngularJS Routes
Our application will have three different views: (For now you can think about them as pages),
- View for displaying search results mapped to route (URL) “/explore“.
- View for displaying user bookmarked places mapped to route (URL) “/places“.
- View for about page mapped to route (URL) “/about“.
We need to use Angular Routing to help us loading the correct view when user request it, thanks for the built in Angular service named “$routeProvider”, this service mainly responsible to wire the controllers, view templates, and the current URL location in the browser, usually every view template is mapped to a unique controller. To use this service we need to inject “ngRoute” module in our application. “ngResource” and “ui.bootstrap” are injected for further use. The code snippet below will show us how we can manage routing information in Angular app, so open file “app.js” and paste the code below:
1 2 3 4 5 6 7 8 9 10 |
var app = angular.module('FoursquareApp', ['ngRoute', 'ngResource', 'ui.bootstrap']); app.config(function ($routeProvider) { $routeProvider.when("/explore", { controller: "placesExplorerController", templateUrl: "/app/views/placesresults.html" }); $routeProvider.otherwise({ redirectTo: "/explore" }); }); |
In the code above I’ve defined for now a single URL “/explore” and mapped it to the view “placesresults.html”, so when the user requests http://explore4sq.azurewebsites.net/index.html#/explore, Angular will match it with the route we configured and load the correct view “placesresults.html”, then it invokes the controller “placesExplorerController” where we can write our business logic for this view. As well we’ve configured the route provider to return to URL “/explore” when ever it didn’t find any matching URL by using the “otherwise” function.
Step 6: Adding our first controller and view
It’s time to add our first controller “placesExplorerController”, so add new JS file to the folder “app–>controllers”, name this file “placesExplorerController.js”, you can name the file anything you want but I usually use the same name for file and controller name, open the file and paste the code snippet below:
1 2 3 |
app.controller('placesExplorerController', function ($scope) { $scope.exploreNearby = "New York"; }); |
As we mentioned before, controllers are just normal JavaScript functions responsible to implement our business logic and validation, if you look at the snippet above, you’ll notice that we’ve injected an object named “$scope”, it is really a glue between the controller and the view, you can think of it as container holding the data we want to project on the view. Here we’ve added new model to the “$scope” object named “exploreNearby”, this model will be used on the view we’ll add now. So let’s add new view to the folder “app–>views”, name this file “placesresults.html”. then open the file and clear it’s content from any html tags such as html body or head, then paste the code snippet below:
1 2 3 4 5 6 7 8 |
<div> <div> <input type="text" data-ng-model="exploreNearby" /> </div> <div> <h5>Places found Near by ({{exploreNearby}})</h5> </div> </div> |
I’m using Bootstrap to style the application, Source code for application on GitHub has a lot extra html elements and CSS classes to make it responsive. Those have been removed from code snippets for clarity. By looking at the code snippet above you will notice that we used directive called “ng-model”, this directive is responsible for the two-way data binding, in simple words; we’ve bind the text input to the model “exploreNearby” so if the value of the text input has changed, thne the value of the model will be changed and vise verse. Notice how we do not use “$scope” object when we reference the model in the html, using the double curly brackets {{}} is the right way to reference model in the DOM elements i.e. {{exploreNearBy}}.
Step 7: Wiring “FourSquareApp” module to our App
This is the most forgotten step that usually devs fall into, we need to tell Angular in which part of our application should be active, we can have Mini-SPA i.e. part of the application is SPA and other parts are traditional client/server request. But in our case we want the entire application to use Angular, so the right place to add the directive “ng-app” is on html tag of the shell page “index.html”, it will look at the code snippet below:
1 2 3 4 5 6 7 8 9 10 |
<html data-ng-app="FoursquareApp"> <head> <!--Rest of html is here, removed for clarity--> </head> <body> <!--Rest of html is here, removed for clarity--> <div data-ng-view=""></div> </body> </html> <!--Rest of html is here, removed for clarity--> |
By looking at the code snippet above we’ll notice that we added new directive called “ng-view” inside html body, this directive will act as a placeholder for our views, so each view configured in the routes will be loaded in this section of out shell page. In other words this directive will get replaced by the html content of our views, so in our case the html fragments in file “placesreuslts.html” will replace this directive. It is important to specify the module name “FoursquareApp” once we use ng-app attribute because in our case we are using modules. We can run the application now and test the routing configuration and the two way binding feature. Don’t forget to reference the 2 newly added JS files to the bottom of Index.html page as the code snippet below:
1 2 3 4 5 6 7 |
<body> <!--Rest of html is here, removed for clarity--> </body> <!-- Load app main script --> <script type="text/javascript" src="app/app.js"></script> <!-- Load controllers --> <script type="text/javascript" src="app/controllers/placesExplorerController.js"></script> |
In the next post we’ll start implementing the real business logic in this controller, make RESTful calls to foursquare API using angular services, and project the data in the view in a neat way.
why following code should be in app.js
Places found Near by ({{exploreNearby}})
Arya, thanks for your comment, I apologize about this, This has been fixed now.
WordPress code snippet plugin didn’t like putting html in the body of the post and keeps removing html tags, I had to do this again.
So… why do you say to download a bunch of libraries and then use CDNs for most of them? It’s a bit confusing to download angular.js, then have a bunch of cdn urls for angular-route, angular-resource, etc. Ditto for angular-ui-bootstrap. If I don’t want to rely on CDNs, do I need to download angular-route, angular-resource, angular-animate? In particular, it seems odd that I would need both ui-bootstrap.min.js and ui-bootstrap-tpls.js.
Hello Joe, Thanks for your comment, I’ve mentioned that we have to download the angular files needed so post readers will know what files to use in this case, I’ve used CDN because I do not want to serve those angular files from my hosting server, I want them to get served from CDN, not a big deal.
As well you need ui-bootstrap-tpls.js so you can use modal popup, otherwise there is no template for the modal so it wont open. try removing this file.
Now we’ll add the “Single Page” which is a container for our application…………….. After adding the “index.html” page
Unfortunately, not quite enough detailed information for a novice.
How/Where should I add this “Single Page” ?
Should I create it in the Views Folder ?
Hello Adam,
You have to add the shell page (index.html) to the root of your project, i recommend you to fork the working project from Github and you can do step by step based on the blog posts, if you felt lost get back to working version.
Great work Taiseer, thank you for putting this series up.
Hello Bernard, I’m really glad you like it. thanks for your comment 🙂
Awesome Job
Thanks Tarun! Glad you like it!
Great Stuff ! Spelling – thne the value…
Hi Taiseer,
Why do you use data-ng-xxx ? shouldn’t ng-xxx be enough?
Hello Shai,
It is a good practice to keep your HTML valid, there is nothing wrong if you used ng-xxx but I prefer to prefix it with data-ng-xxx always.
I understand. Thanks.
I have completed “Building SPA using AngularJS – Part 1” as your instruction……. But unfortunately i can’t run it…… there was an error message …….
“Server Error in ‘/’ Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.18408 ”
please help me!
Thank you.
Hello Zahir,
You need to double check your app.js file and make sure that you are setting the routeProvider.otherwise is configured as the below:
$routeProvider.otherwise({ redirectTo: “/explore” });
The same issue happens if you requested /index.html#/explore?
Hi. A have the following error: Error: [$injector:modulerr] …
I did it like in tutorial. Why I get this error?
Hello Max,
This is an issue with the dependencies injected, please check the app.js file and make sure that you are injecting the services required, as well make sure you are referencing the JS file in index.html page in correct order.
Hope this helps.
Thanks for the answer. I have code like your.
this is scripts in html:
this is app.js:
var app = angular.module(‘FoursquareApp’, [‘ngRoute’, ‘ngResource’, ‘ui.bootstrap’]);
app.config(function ($routeProvider) {
$routeProvider.when(“/explore”, {
controller: “placesExplorerController”,
templateUrl: “/app/views/placesresults.html”
});
$routeProvider.otherwise({ redirectTo: “/explore” });
});
All scripts loaded correctly. I checked it.
Can yoг check my solution? http://www.filedropper.com/foursquareangularjsweb
Thanks.
Hello,
I’ve checked it, when you referenced the JS files there is “mce-text/javascript” in the type for app.js and forplacesExplorerController.js. You should reference them as the below.
As well double check the spelling for $scope.explorerNearby = “Astana”; You typed it different than the view {{exploreNearby}}
Thank you very much. There are some utils or plugin which can catch this type of errors or return valuable error?
hmm, always use Chrome and keep the console tab opened so you will notice the errors when they happen directly. Your error was like you forgot to reference the file, most probably you will not forget to reference a file 🙂 Good luck in learning AngualrJS!
Very nice sample. Great looking app too!
Thanks Trevor, glad you like it. Keep tuned as I’ll be posting another angularjs tutorial which focus on authentication using tokens.
Hi nice tutorial,But i don’t understand why we include all the custom directives, controllers in the index.html.
what if my application have more number of files, is this is the right way to do?
Hi Anil,
The idea of single page applications is to load all the necessary resources up front and do a full page load only when the application is requested for the first time. That’s why you will find all the resources in Index.html page. By doing this you will reduce the server trips between client and server as all the JS files are loaded earlier. For larger SPAs the number of controllers, service, etc… will increase and you will end up having lot of JS files; to solve this issue you need to concatenate and minify those small JS files into larger JS file. You can achieve this by using a task runner such as GulpJS or Grunt, check my other post where I used GulpJS for minifying and concatenating JS files http://wp.me/p4nPYq-7j hope this clarifies your concerns.
Great One Tayseer 🙂
Thanks Yaser, glad you liked it 🙂
Wonderful step by step article
Glad it was useful to get started with AngularJS 🙂
First of all thanks for this step by step article. I am a newbie in this. I got an error while running the Part 1 example code.
Error says “Access to restricted URI denied”, in browser console and the input field is also not showing.
I fixed this by changing [templateUrl: “app/views/placesresults.html”].
Did I miss anything while using ‘/app/views/placesresults.html’.
Hi,
You are welcome, but this has been for a while, I do not know what is the exact issue is but I’m suspecting that it is generated from Foursquare API, double check that app_id and key for 4SQ is correct.
Hi I tried doing part 1. But it does not seem to want to load the partial view. I’m using VS2013 and selected ASP.NET Web Application (.NET Framework 4.5.1) >> Empty (with Web API checked).
Hi Lyle,
This has been for a while, are you facing specific issue or receiving certain error? It should work even if you on VS 2013.
Are you supposed to select MVC and Web API as core references? Issue comes when trying to do things like bundling.
Great Sample. Really helpful in clearing out the doubts that users have at early stages of AngularJS.
You are welcome, happy to help!
love the majority of your articles , explain and communicate your thoughts with clarity and concise english !
You are welcome, thanks for your message.