Mean
Learn to Build Modern Web Apps with MEAN

Adding Some CSS Styles

PRO
Outline

At this point, we have the basics of an application - a user can add new posts which are automatically ordered based on the number of upvotes. Up until now, however, our interface has been lacking in the looks department. We can spruce it up a bit using some basic Bootstrap styling.

Change your index.html file to include Twitter Bootstrap along with a new layout:
<html>
<head>
  <title>Flapper News</title>
  <link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
  <script src="app.js"></script>
  <style> .glyphicon-thumbs-up { cursor:pointer } </style>
</head>
<body ng-app="flapperNews" ng-controller="MainCtrl">
  <div class="row">
    <div class="col-md-6 col-md-offset-3">

      <div class="page-header">
        <h1>Flapper News</h1>
      </div>

      <div ng-repeat="post in posts | orderBy:'-upvotes'">
        <span class="glyphicon glyphicon-thumbs-up"
          ng-click="incrementUpvotes(post)"></span>
        {{post.upvotes}}
        <span style="font-size:20px; margin-left:10px;">
          <a ng-show="post.link" href="{{post.link}}">
            {{post.title}}
          </a>
          <span ng-hide="post.link">
            {{post.title}}
          </span>
        </span>
      </div>

      <form ng-submit="addPost()"
        style="margin-top:30px;">
        <h3>Add a new post</h3>

        <div class="form-group">
          <input type="text"
            class="form-control"
            placeholder="Title"
            ng-model="title"></input>
        </div>
        <div class="form-group">
          <input type="text"
          class="form-control"
          placeholder="Link"
          ng-model="link"></input>
        </div>
        <button type="submit" class="btn btn-primary">Post</button>
      </form>

    </div>
  </div>
</body>
</html>

At the top we've included Bootstrap from a CDN. In the body tag, we've made use of Bootstrap's grid system to align our content in the middle of the screen. We've also stylized the posts list and "Add a new post" form to make things a little easier to read. There's a lot more that could be done on this front so feel free to mess around with more styling before (or after) you continue.

(optional) Add your own styles!

Angular Services

Up to this point, we've been storing important data directly in the controller. While this works, it has some disadvantages:

  • when the controller goes out of scope, we lose the data
  • that data cannot be easily accessed from other controllers or directives
  • the data is difficult to mock, which is important when writing automated tests

To rectify this problem, we're going to refactor our $scope.posts variable into a service.

My First Service... Is Really a Factory

In Angular, services are declared much like controllers. Inside app.js, we're going to attach a new service to our flapperNews module.

Create a factory for posts in app.js (our MainCtrl controller should appear below this):
app.factory('posts', [function(){
  // service body
}])

By Angular conventions, lowerCamelCase is used for factory names that won't be new'ed.

You may be wondering why we're using the keyword factory instead of service. In angular, factory and service are related in that they are both instances of a third entity called provider.

The difference between them are nuanced but if you'd like to learn more check out Angular Factory vs Service vs Provider.
Now that we've created our service, lets go ahead and move our posts variable to it:
app.factory('posts', [function(){
  var o = {
    posts: []
  };
  return o;
}]);

What we're doing here is creating a new object that has an array property called posts. We then return that variable so that our o object essentially becomes exposed to any other Angular module that cares to inject it. You'll note that we could have simply exported the posts array directly, however, by exporting an object that contains the posts array we can add new objects and methods to our services in the future.

Injecting the Service

Our next step is to inject the service into our controller so we can access its data. Simply add the name of the service as a parameter to the controller we wish to access it from:

Inject posts service into MainCtrl
app.controller('MainCtrl', [
'$scope',
'posts',
function($scope, posts){
  ...

)};

As you'll recall, two-way data-binding only applies to variables bound to $scope. To display our array of posts that exist in the posts factory (posts.posts), we'll need to set a scope variable in our controller to mirror the array returned from the service.

Bind the $scope.posts variable in our controller to the posts array in our service:
$scope.posts = posts.posts;

Now any change or modification made to $scope.posts will be stored in the service and immediately accessible by any other module that injects the posts service.

Check your work

You can view the completed & working code for this tutorial here:

Now our application should behave the same way as the previous chapter, but we've added some styles with bootstrap and moved some of our logic for Posts to an AngularJS service.

 

I finished! On to the next chapter