Firebase - tdkehoe/blog GitHub Wiki

#Learn Firebase By Breaking Things #Make an AngularFire CRUD Blog App with Firebase and Angular

#What's a CRUD app?

A CRUD app uses a database to persist data. It includes four operations:

  • CREATE: You can make a new record. This route is a called NEW.
  • READ: You can view one record (the SHOW route) or all records (the INDEX route).
  • UPDATE: You can view, edit, and update a record. This route is called EDIT.
  • DESTROY: You can delete a record. This route is called DESTROY.

A CRUD app is a simple exercise for learning many aspects of databases.

We'll make a blog app.

#Why Firebase and AngularFire?

In another tutorial I explained how to make a CRUD app using the MEAN stack: MongoDB, Express, Angular, and Node.

The advantages of Firebase and AngularFire over MongoDB, Express, and Node are:

  • No back end. AngularFire connects Angular directly to the database, without HTTP requests to a back end server.
  • Three-way data binding. Angular includes two-way data binding, keeping the view and $scope in sync. In other words, when a user makes changes in the view, the local variables in the $scope stay in sync. Conversely, when your JavaScript updates a local variable the view stays in sync. AngularFire keeps the remote database in sync with the view and $scope.
  • Five-way data binding, and seven-way, nine-way, etc. I made a tic-tac-toe game using AngularFire. Two players can play together, with the two views updating the two $scopes and the cloud database. You could make a game with three or more players, all sharing one Firebase database.
  • AngularFire's OAuth2 library makes authorization easy with Facebook, GitHub, Google, Twitter, tokens, and passwords.
  • Cloud-based. Firebase is a cloud database, accessible anywhere on the Internet. Firebase can also host your app.

#Set Up the Directory Structure

Set up these directories:

├── app.js
├── css
│   └── style.css
├── index.html
├── .gitignore
└── javascript
    ├── controllers
    │   ├── EditController.js
    │   ├── HomeController.js
    │   ├── NewController.js
    │   └── ShowController.js
    ├── routes
    │   └── routes.js
    ├── services
    └── templates
        ├── edit.html
        ├── home.html
        ├── new.html
        └── show.html
touch app.js
mkdir css
cd css
touch style.css
cd ..
touch index.html
touch .gitignore
mkdir javascript
cd javascript
mkdir controllers
cd controllers
touch EditController.js
touch HomeController.js
touch NewController.js
touch ShowController.js
cd ..
mkdir routes
cd routes
touch routes.js
cd ..
mkdir services
mkdir templates
cd templates
touch edit.html
touch home.html
touch new.html
touch show.html
cd ..
tree

#.gitignore

.DS_Store
angular.min.js
bower_components
angular
angular-route
angularfire
firebase

#INDEX

The INDEX route displays all the posts.

#index.html

Set up your index.html file with Angular:

<!DOCTYPE html>
<html lang="en" ng-app="AngularFireBlogApp">
<head>
  <meta charset="UTF-8">
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular-route.js"></script>
  <script src="https://cdn.firebase.com/js/client/2.3.2/firebase.js"></script>
  <script src="https://cdn.firebase.com/libs/angularfire/1.1.3/angularfire.min.js"></script>
  <link rel="stylesheet" href="css/style.css">
  <title ng-bind="title">My AngularFire Blog</title>
</head>
<body>

  <h1>My AngularFire Blog</h1>

  <ng-view>

  <script type="text/javascript" src="app.js"></script>
  <script type="text/javascript" src="javascript/routes/routes.js"></script>
  <script type="text/javascript" src="javascript/controllers/EditController.js"></script>
  <script type="text/javascript" src="javascript/controllers/HomeController.js"></script>
  <script type="text/javascript" src="javascript/controllers/NewController.js"></script>
  <script type="text/javascript" src="javascript/controllers/ShowController.js"></script>
</body>
</html>

You see four libraries:

  • Angular
  • Angular Route
  • Firebase
  • AngularFire

The CDNs can go in either the head or the body.

Find the latest version CDNs at

https://angularjs.org/
https://www.firebase.com/
https://www.firebase.com/docs/web/libraries/angular/

Or just find the latest version numbers:

The angular-route library version should match the angular version.

I suggest also installing the libraries in case you lose Internet connection. Install the libraries using bower from the command line.

#app.js

var app = angular.module('AngularFireBlogApp', ['ngRoute', 'firebase']);

app.run(['$location', '$rootScope', function($location, $rootScope) {
  $rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
    // test for current route
    if(current.$$route) {
      // Set current page title
      $rootScope.title = current.$$route.title;
    }
  });
}]);

The app.run code changes the title when the user changes views.

#routes.js

app.config(function($routeProvider) {

  $routeProvider
  .when('/', { // INDEX
    templateUrl: 'javascript/templates/home.html',
    controller: 'HomeController',
    title: 'My AngularFire Blog'
  })
  .when('/new', { // must be above '/:id' otherwise it'll think that the ID is 'new'
    templateUrl: 'javascript/templates/new.html', // NEW
    controller: 'NewController',
    title: 'Add New Post'
  })
  .when('/:id/edit', { // UPDATE
    templateUrl: 'javascript/templates/edit.html',
    controller: 'EditController',
    title: 'Edit Post'
  })
  .when('/:id', { // SHOW
    templateUrl: 'javascript/templates/show.html',
    controller: 'ShowController',
    title: 'Show Post'
  })
  .otherwise({ redirectTo: '/' });
});

#INDEX Route

The INDEX route displays all the blog posts. It also has the DELETE button.

##home.html

<div ng-repeat="post in posts">
  <a ng-href="#/{{post.$id}}"><span>{{post.postTitle}}</span></a>
  <span>{{post.authorName}}</span>
  <span>{{post.content}}</span>
  <span>{{post.date}}</span>
  <span>{{post.pictureURL}}</span>
  <button ng-click="deletePost(post)">Delete Post</button>
  <br />
</div>

<a href="/#/new"><button>Add a new post</button></a>

post is an object or record in the array or collection posts. ng-repeat displays all the objects in the array.

The anchor link goes to #/{{post.$id}}. This puts the post's key into the URL query string. In MongoDB the key is _id. In Firebase the key is $id.

The post will have titles, authors, text content, a picture, and dates.

The Delete button with each post runs the method $deletePost() when the user clicks the button.

##HomeController.js

app.controller('HomeController', ['$scope', '$firebaseArray',
function($scope, $firebaseArray) {
  console.log("HomeController.");
  // set up Firebase
  var ref = new Firebase("https://my-angularfire-blog.firebaseio.com/");
  $scope.posts = $firebaseArray(ref);

  $scope.deletePost = function(post) { // DESTROY
    console.log("Deleting post.");
    $scope.posts.$remove(post).then(function() {
      console.log("Post deleted.")
    });
  };

}]);

We inject the dependency $firebaseArray because we will remove posts to the array.

deletePost() passes in the selected post, then $remove(post) finds the object in the array and deletes it.

#NEW

##new.html

##NewController.js

app.controller('HomeController', ['$scope', '$firebaseArray', '$location',
function($scope, $firebaseArray, $location) {
  console.log("HomeController.");
  // set up Firebase
  var ref = new Firebase("https://my-angularfire-blog.firebaseio.com/");
  $scope.posts = $firebaseArray(ref);

  $scope.addPost = function(){ // NEW
    console.log("Adding new post.");
    var post = {
      postTitle: $scope.post.postTitle,
      postAuthor:  $scope.post.postAuthor,
      postContent: $scope.post.postContent,
      postPictureURL: $scope.post.postPictureURL,
      postTimestamp:  Date.now(),
      likes: 0,
      comments: [null]
    };
    $scope.posts.$add(post).then(function(){
      console.log("New post added");
      $location.path( "/" );
    });
  };
}]);

We inject the dependency $firebaseArray because we will add posts to the array.

We inject the dependency _$location because we will change routes after adding the new post, to return to the home page.

⚠️ **GitHub.com Fallback** ⚠️