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:
- Latest version of Angular and angular-route: https://angularjs.org/.
- Latest version of Firebase: https://www.firebase.com/docs/web/changelog.html.
- Latest version of angularfire: https://github.com/firebase/angularfire/releases.
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.