The first thing we need to think of is how we’ll be gathering the information about users. It’s quite easy - we just need to get a request from a visitor. Of any kind - it may be a request to get an image, a file, a stylesheet or a script.
Then we’ll just parse headers from that request and save the extracted data in the database. The only problem here is: how to get unique key from each request?. We may use visitor’s IP address. It’s the easiest way.
Now, let’s decide what pages will our application have and what will they look like:
Let’s create a set of pages, we described above. They will be used later to display real data, but for now we’ll stub the real data with constants.
To make our prototyping smooth and fast, we’ll use Twitter Bootstrap. We’ve installed it already with Bower, so we’ll be just using it. We need to implement these pages only:
- landing page
- sign up / sign in page
- account settings page
- apps list page
- application page
- application create / edit page
Six pages, huh? Let’s do it quick:
And to make Bower-managed libraries available in our views, we need to add one more path to the server configuration:
As you can see, we used Jade’s block extending and split our templates into one layout and many partials, so our file tree is clean and changing any of the pages will not be a hard task.
In our frontend application we’ll use something called MVVM. That is a design pattern, kindly provided by Angular. So our views will be displaying data and transferring it to controllers (or ViewModels), and all the logic, handling that data will be defined in controllers and services, representing Models. Actually, our models will be handled on a server-side, and services will only provide an interface to them. But that is totally another story and will be described later.
For now let’s integrate Angular in our application. And we’ll start, deciding how we’ll split our application on the Angular layer. Pages we described above are used for these actions:
And so we can define corresponding Angular controllers:
Basically, here’s how SPA works:
- user enters a website
- user, say, clicks on a link to change the page
- if application needs partial, it requests server for a HTML partial
- application renders a retrieved (or cached) partial in a browser
Let’s start and you will see how easy it is!
We’ll start, adding Angular to our
Now when we have Angular inside the
bower_components directory, let’s add its references to
Now, the only two things we need to start using Angular right away are:
- Angular controller, which will contain scope with variables and functions for the page’ portion
Yeah, we definitely need some clarifications about those two new words.
Angular defines a few kind of bricks, you may use to build an entire application:
- templates - dynamically changed HTML files
- factories (or services) - helper objects, containing logic which is not directly related to views (displaying and retrieving data)
- directive - is a place, where HTML-related code is placed
On the other hand, in MVVM architecture we have three layers:
- Model - layer, performing manipulations on data; kind of database layer
- View - user interface layer
- ViewModel - stores data to be shown on UI or which was retrieved from UI
Accordingly to this scheme, in Angular we have the next logic structure:
- Model - Services
- Views - Templates, Directives
- ViewModel - Controllers
Here I did not mention two-way data binding and scopes, because first one is a part of templates and the second one is a part of controllers. And they may not be separated one from another.
Kickstarting. For real
Let’s add some Angular logic into our project. We’ll split our app into all those
controllers and services very soon. But first, we need our application in Angular to be
registered. Create a file
config.js inside the
with the following content:
And point the whole layout to use that application with the
ng-app directive on
Now when we have our app accessible by all our pages, let’s create our first controller,
which will handle exactly one page, the landing page. To expose our controllers,
we need to make a module, consisting of controllers and add it as a dependency
to our application. If you take a look at the application definition, it is also
a module. The first parameter to
angular.module() function is a module name,
the second one is a list of dependencies.
Controllers are defined in a same way, but the dependency list for them contains
module definition as the last element as well. Let’s define a
Here we defined a module
ourStatsControllers, which will contain all the controllers
LandingPageCtrl controller, which is a module of the same name, with a single
$scope and its implementation. As you can see, the
here twice - first time as a dependency name and second time as an argument
to controller’ definition function. That’s exactly how dependency injection is made
with Angular. Here we defined a scope variable for our controller too, called
We’ll use it in just a moment.
Instead, when minified, this controller will be transformed into something like this:
And Angular will try to find a controller, factory or a service, named
b. And will probably fail.
But if you use “explicit” dependency injection, like this:
then this code will be minified to something like this:
And then Angular will try to inject the
$scope service and will succeed.
Just keep this small trick in mind, when developing a real-world Angular applications.
Now we need to inject our controllers module into our application. So we just add a dependency entry inside our application declaration:
As we already defined the
ourStatsControllers module, we don’t need to provide
an implementation for it.
But to make our controller work properly, we need either to declare it on frontend, making it handle static piece of a page, or make our application use different controller and view, depending on a route in a browser’s address line.
The first approach is good when you use Angular for complex widgets on a page. But we need something more from our application, so we’ll set up router. It’ll parse URL from browser and run the corresponding controller.
Router we’ll be using is
ngRoute. This is a third-party plugin for Angular.
And in order to use it, we need to add a Bower dependency to our project:
Now we only need to reference it in our app definition:
The next step is configuring our application routes, making application to “understand”, which controller and which template it should run. This may be done in the same file, as the application definition:
This configuration makes our application run
LandingPageCtrl when the
/home page is
entered. And if the URL entered is not known, application will redirect user to the
/home page. All Angular routes are passed as an anchor, so saying the
we actually mean
http://your.host/#/home. This makes browser not send requests
to the server, requesting non-existent pages.
Also, note we point our application to use the
But we did not tell the Angular where to render it. To fix that, we’ll
ng-view directive to the
.container-fluid element of our
right in our layout:
Currently, when the project is built with the Gulp’
we have a
public/home.html page, but it is not a partial - it is a standalone
page, extending the layout. But what we need is to compile all our Jade templates
public/partials directory, making them available through the
/partials/partial_name.html URL. And they should not extend the layout, because
then we’ll end up with an incorrect HTML code.
To make the magic happen, we only need to change our
build task for Gulp
to build views directly to the
public directory. So the
file will be the main HTML page of our application, available through
/index.html URL. And all the partials would be our Angular controllers’ templates,
Now the views part of Gulp
build task should look like this:
And let’s just extract the content of
index.jade partial as it is now, all the
content into a separate Angular partial,
And let’s add only one small change - add only one line there:
Remember, we have the
$scope parameter for our
And that there we set the
apps variable? That is it, displaying on our home page.
All the other parameters, added to the
$scope are available on the corresponding
template, by default. You may display them with some formatting, looping through the list
ng-repeat and using them as conditionals with
But let’s go step-by-step.
Populating our app with stuff
For now, let’s add all the other controllers to our app. Just create empty controllers according to the table from part one, using the same approach as described right above.
We’ll define each controller in its own file, but all of them will be concatenated
into one big application javacript file. We’ll use Gulp for this purpose with
Let’s install it:
Its usage is super-easy: you just redirect the output of our CSS and JS files compiler
concat(filename) function, providing it with a resulting filename. Like this:
Now, the controllers. First we need to set the routes. The router configuration for our application will look just like this:
And we currently have a lot of pages with broken links. We should fix that, providing URLs to our (empty for now) controllers. Like this one:
Fix those links for all the views by yourself - that is not a complex task. All the routes are described both in the app configuration and in a table above.