Skip to content

Latest commit

 

History

History

module-5

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

###Tying things up in Angular###

Because we're using EJS as our templating engine and it supports plain HTML, we can use our index.html as the default view -- changing the extension to index.ejs of course. We're going to move the rest of our files to the /public/ folder yet again, to be served as static assets.

###Authentication###

When we last left our authController, we left the authentication functions quite blank. Now, let's go through and actually implement them.

####Creating authentication variables

First off, we'll need some place to store our authentication state that all of our controllers can access. Each controller only has access to its own $scope, but they all have access to the $rootScope of the module. Let's go ahead and initialize some some authentication variables there. Note that we're also specifying $rootScope as a dependency.

//chirpApp.js
var app = angular.module('chirpApp', ['ngRoute']).run(function($rootScope) {
  $rootScope.authenticated = false;
  $rootScope.current_user = '';
  };
});
...

####Handling authentication responses

When users enter their credentials and log in, we should make request to the /auth/login endpoint we created previously. If it's successful, we should set our authentication status in the $rootScope appropriately (note the $rootScope dependency)and redirect them to our posts stream. If it's not successful, we'll display an error message similar to what we did before. The same should happen for our registration page with /auth/register.

In order to redirect our users on successful authentications, we'll need to use the $location service and direct our app to the path we'd like to go to. In this case, it's just our main view at /.

//chirpApp.js
app.controller('authController', function($scope, $http, $rootScope, $location){
  $scope.user = {username: '', password: ''};
  $scope.error_message = '';

  $scope.login = function(){
    $http.post('/auth/login', $scope.user).success(function(data){
      if(data.state == 'success'){
        $rootScope.authenticated = true;
        $rootScope.current_user = data.user.username;
        $location.path('/');
      }
      else{
        $scope.error_message = data.message;
      }
    });
  };

  $scope.register = function(){
    $http.post('/auth/signup', $scope.user).success(function(data){
      if(data.state == 'success'){
        $rootScope.authenticated = true;
        $rootScope.current_user = data.user.username;
        $location.path('/');
      }
      else{
        $scope.error_message = data.message;
      }
    });
  };
});

####Signing out

If we're signing users in, we'll also need a way to sign users out. I'd like for users to be able to do this straight from the navigation, so we'll need to put this function in the rootScope as well.

//chirpApp.js
var app = angular.module('chirpApp', ['ngRoute', 'ngResource']).run(function($http, $rootScope) {
  $rootScope.authenticated = false;
  $rootScope.current_user = '';

  $rootScope.signout = function(){
    $http.get('auth/signout');
    $rootScope.authenticated = false;
    $rootScope.current_user = '';
  };
});

####Creating authentication-sensitive elements

Now, let's link to this in the navigation header! We'll use the ng-click directive to call the signout function if Logout is clicked. While we're at it, we can use the ng-show directive to check out authenticated status to only display it if a user is authenticated. We can also display the authenticated user with ng-show and {{current_user}}. Last but not least, we'll use ng-hide='authenticated' to hide the login and registration links if the user is already logged in.

<!--index.html-->
...
<nav class="navbar-fluid navbar-default navbar-fixed-top">
  <div class="container">
    <a class="navbar-brand" href="#"> Chirp! </a>
    <p class="navbar-text"> Learn the MEAN stack by building this tiny app</p>
    <p class="navbar-right navbar-text" ng-hide="authenticated"><a href="#/login">Login</a> or <a href="#/signup">Register</a></p>
    <p class="navbar-right navbar-text" ng-show="authenticated"><a href="#" ng-click="signout()">Logout</a></p>
    <p class="navbar-right navbar-text" ng-show="authenticated">Signed in as {{current_user}}</p>
  </div>
</nav>
...

###Services###

####Calling our APIs through a Factory Service We were using just an empty array for posts, but now we have a real backend that we can call out to! Let's start by replacing our empty array with the our actual feed of chirps! We'll create a simple postService factory with a getAll function that calls out to our API to get all the chirps we already have using the $http service. Note that since we're going to use postService in our mainController, we'll need to add it as a dependency.

//chirpApp.js
var app = angular.module('chirpApp', []);

app.controller('mainController', function($scope, postService){
  $scope.posts = [];
  $scope.newPost = {created_by: '', text: '', created_at: ''};
  
  postService.getAll().success(function(data){
    $scope.posts = data;
  });

  $scope.post = function(){
    $scope.newPost.created_at = Date.now();
    $scope.posts.push($scope.newPost);
    $scope.newPost = {created_by: '', text: '', created_at: ''};
  };
});

app.factory('postService', function($http){
  var baseUrl = "/api/posts";
  var factory = {};
  factory.getAll = function(){
    return $http.get(baseUrl);
  };
  return factory;
});

####Using ngResource

Since our API is a fully RESTful one, Angular has a service that we can use instead of having to manually call out to our endpoint with each type of request, called ngResource. This isn't packaged with Angular, so we'll have to go through and include it in our project.

We can then simply use the $resource in our postService factory, pass our endpoint to $resource, and start performing CRUD operations. We can now use the query method to GET all of our posts instead.

<!--index.html-->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular-resource.js"></script>
//chirpApp.js
var app = angular.module('chirpApp', ['ngRoute', 'ngResource']);

...

app.controller('mainController', function($scope, postService){
  $scope.posts = postService.query();

  ...

});

app.factory('postService', function($resource){
  return $resource('/api/posts/:id');
});

####Limiting posts

Now that we have authenticated users, we should only let them post chirps. Let's use our ng-show directive like before to only display the chirp form if a user is logged in. While we're at it, we can also take out the input field for a username, and display the handle of the logged in user, current_user, instead.

<!--main.html-->
<div class="clearfix">
  <form ng-Submit="post()" ng-show="authenticated">
    <h4>{{current_user}} says</h4>
    <textarea required class="form-control" maxlength="200" rows="3" placeholder="Say something" ng-model="newPost.text"></textarea>
    <input class="btn submit-btn pull-right" type="submit" value="Chirp!" />
  </form>
</div>

...

We'll then add current_user to newPost before we send it to the backend. We'll use the save function to POST our new chirp to our API. Since we want it to show up in our feed, we'll fetch the updated feed again in its callback function. We'll also reset the current post to be blank again.

//chirpApp.js
...
$scope.post = function() {
  $scope.newPost.created_by = $rootScope.current_user;
  $scope.newPost.created_at = Date.now();
  postService.save($scope.newPost, function(){
    $scope.posts = postService.query();
    $scope.newPost = {created_by: '', text: '', created_at: ''};
  });
};
...

We can use postFactory for so much more, whether it's deleting posts or finding a post by its ID. That won't be covered in the scope of this module, but it does show the power of ngResource. You can find out more about it in the AngularJS Docs