+ All Categories
Home > Documents > Thinkster Io Angularfire Slack Tutorial

Thinkster Io Angularfire Slack Tutorial

Date post: 06-Jul-2018
Category:
Upload: tafhim-ul-islam
View: 223 times
Download: 0 times
Share this document with a friend

of 49

Transcript
  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    1/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Learn to build a Real-Time

    Slack clone with AngularFire

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://thinkster.io/

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    2/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Stay on the bleeding edge of your favorite technologies. 

    AngularJS  Firebase

     Albert Pai

    IntroductionThe goal of this tutorial is to guide you through the creation of a Slack clone called fireSlack. Upon

    completion, you will learn how to build a real time collaborative chat application using angularFire to

    integrate Firebase with AngularJS. Your application will be able to provide the following features:

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://thinkster.io/topics/angularhttps://thinkster.io/topics/firebasehttps://twitter.com/iamalbertpaihttps://slack.com/https://www.firebase.com/docs/web/libraries/angular/index.htmlhttps://firebase.com/https://angularjs.org/

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    3/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Sign up for an account

    Join/create channels to chat in

    Have a user profile

    Direct message other users

    See who's online

    Prerequisites

    This course assumes knowledge of programming and at least basic knowledge of JavaScript andAngularJS. We recommend going through A Better Way to Learn AngularJS if you're not familiar with

    AngularJS. We've created a seed repo based off of the Yeoman AngularJS Generator to help you get

    started faster. Before you begin, you will need to have Node.js, npm, and Git installed. We'll need

    Node.js and npm in order to install Grunt and Bower for managing dependencies. Follow these

    instructions for installing Node.js and npm, and these instructions for installing Git. Additionally,

    you'll need to have a free Firebase account and create a Firebase for this tutorial.

    Final Notes About the Tutorial

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://thinkster.io/a-better-way-to-learn-angularjs/https://github.com/apai4/fireslack-seedhttps://github.com/yeoman/generator-angularhttps://nodejs.org/https://www.npmjs.org/https://git-scm.com/http://gruntjs.com/http://bower.io/https://github.com/joyent/node/wiki/Installing-Node.js-via-package-managerhttps://git-scm.com/book/en/v2/Getting-Started-Installing-Githttps://www.firebase.com/signup/?utm_medium=tutorial&utm_source=thinkster&campaign=angular_article

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    4/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    You should never  copy and paste code from this text unless we tell you to, as we've found that the

    skills being taught will stick better if you write out all of the code yourself. If you need more

    clarification on a certain part of the tutorial, we recommend that viewing the supplementary

    screencast series as we go into far more detail in each of the videos. It's also significantly easier to

    learn from the screencast series than the text, as you can actually see how a skilled developer

    manipulates the concepts in AngularJS to build a working application.

    Getting StartedOnce the initial codebase is cloned locally, we'll need to run a few commands to install dependencies

    and get our application up and running. Within our codebase, run the following commands:

    After running grunt serve , open up http://localhost:4000  and you should see a splash page for

    our application, with a non-functional login and register page ready for us to build off of. In this

    tutorial, our directory structure will be grouped by feature (see #1 in this list) and we will be using ui-

    router as our router. We'll also be using the "controller as" syntax for referencing our controllers.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://www.airpair.com/angularjs/posts/top-10-mistakes-angularjs-developers-makehttps://github.com/angular-ui/ui-router

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    5/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Authenticating Users usingangularFireCreating a registration and login system for your app can be tedious, but is often one of the most

    important and required features, and usually requires you to create your own backend. Thankfully,

    Firebase makes this really easy for us by providing us with a hosted solution.

    While you have your Firebase pulled up, keep your Firebase URL handy. It should look something like:

    https://firebase-name-here.firebaseio.com/

      .constant('FirebaseUrl', 'https://firebase-name-here.firebaseio.com/' );

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    6/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Creating the Auth Service

    angular.module('angularfireSlackApp')

      .factory('Auth', function(){

      });

    Here we'll inject $firebaseAuth , which is a service that AngularFire provides us with, along with our

    constant, FirebaseUrl . Then we'll be able to create a reference to Firebase using the Firebase

    constructor and our FirebaseUrl , which we'll be passing to the $firebaseAuth  service. See the

    angularFire API docs for a list of available methods $firebaseAuth  provides. Our factory will return

    the $firebaseAuth  service associated with our Firebase.

    The resulting factory should look like this:

    angular.module('angularfireSlackApp')

      .factory('Auth', function($firebaseAuth, FirebaseUrl){

      var ref = new Firebase(FirebaseUrl);

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-users-and-authentication

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    7/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      var auth = $firebaseAuth(ref);

      return auth;

      });

    Now that we have an Auth  service ready for our application to use, let's create a controller to use

    with our login and registration forms.

    Creating the Auth Controller

    angular.module('angularfireSlackApp')

      .controller('AuthCtrl', function(Auth, $state){

      var authCtrl = this;

      });

    The $state  service is provided by ui-router  for us to control the state of our application. We can

    use the go()  function on $state  to redirect our application to a specific state. We also create a

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    8/49

    pdfcrowd comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    reference to the this  keyword within our controller because we're using the controller as

    syntax. For more information about this syntax, see this lesson.

    angular.module('angularfireSlackApp')

      .controller('AuthCtrl', function(Auth, $state){

      var authCtrl = this;

      authCtrl.user = {

      email: '',

      password: ''

      };

      });

    This user object will be used with the ng-model  directive in our form. Next, we'll need two functions

    on our controller, one for registering users and one for logging in users. $firebaseAuth  provides us

    with two functions: $authWithPassword  for logging in users and $createUser  for registering users.

    Both of these functions take a user object like the one we initialized on our controller, and return a

    promise. If you're not familiar with how promises work, read this to learn more about promises.

    authCtrl.login = function (){

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://thinkster.io/a-better-way-to-learn-angularjs/controllershttps://thinkster.io/a-better-way-to-learn-angularjs/promises

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    9/49

    df d mi b PRO i Are you a developer? Try out the HTML to PDF API

      Auth.$authWithPassword(authCtrl.user).then(function (auth){

      $state.go('home');

      }, function (error){

      authCtrl.error = error;

      });

    };

    When authentication is successful, we want to send the user to the home state. When it fails, we want

    to set the error on our controller so we can display the error message to our user.

    authCtrl.register = function (){

      Auth.$createUser(authCtrl.user).then(function (user){

      authCtrl.login();

      }, function (error){

      authCtrl.error = error;

      });

    };

    Our register  function works very similarly to our login  function. We want to set error  on the

    controller if $createUser  fails, however, when $createUser  succeeds, it doesn't automatically log in

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    10/49

    df di b PRO i Are you a developer? Try out the HTML to PDF API

    the user that was just created so we'll need to call the login  function we just created to log the user

    in. Now that we have our authentication service and controller created, let's update our templates

    and put them to use.

    .state('login', {

      url: '/login',

      controller: 'AuthCtrl as authCtrl',

      templateUrl: 'auth/login.html'

    })

    .state('register', {

      url: '/register',

      controller: 'AuthCtrl as authCtrl',

      templateUrl: 'auth/register.html'

    })

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    11/49

    df di b PRO i A d l ? T t th HTML t PDF API

    The resulting form should look like this:

     

     

     

     

     

     

     

      {{ authCtrl.error.message }}

    This div will remain hidden until our authentication controller reaches an error, in which case the error

    message it will get displayed to our user. Next, let's update our login template in a similar fashion.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    12/49df di b PRO i A d l ? T t th HTML t PDF API

     

     

     

     

     

     

     

      {{ authCtrl.error.message }}

    Now we should have a working register and login system, but we have no way of telling if the user is

    logged in or not. The login and registration pages are still accessible if we are authenticated. We can

    resolve this by using the resolve  property on our states. resolve  allows us to create dependencies

    that can be injected into controllers or child states. These dependencies can depend on services in our

    app that return promises, and the promises will get resolved before our controller gets instantiated.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://github.com/angular-ui/ui-router/wiki#resolve

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    13/49df di b PRO i

    Are you a developer? Try out the HTML to PDF API

    Read the ui-router Github Wiki if you're not familiar with how resolve  works with ui-router .

    resolve: {

      requireNoAuth: function($state, Auth){

      return Auth.$requireAuth().then(function(auth){

      $state.go('home');

      }, function(error){

      return;

      });

      }

    }

    The $firebaseAuth  service provides us with a $requireAuth  function which returns a promise. This

    promise will get resolved with an auth  object if the user is logged in. The Firebase Documentation

    provides a table of what information is available to us within auth . If the user is not authenticated,

    the promise gets rejected. In our requireNoAuth  dependency, if the User is logged in we want to send

    them back to the home  state, otherwise, we need to catch the error that gets thrown and handle it

    gracefully by returning nothing, allowing the promise to be resolved instead of rejected. Now, we

    should no longer be able to access the login or register states if we're authenticated.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://github.com/angular-ui/ui-router/wiki#resolvehttps://www.firebase.com/docs/web/guide/login/password.html#section-logging-in

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    14/49df di b PRO iAre you a developer? Try out the HTML to PDF API

    Storing User Profiles in FirebaseNow that we're able to authenticate users, let's create the ability for users to have custom display

    names to use in our app (rather than showing the user's email or uid)

    angular.module('angularfireSlackApp')

      .factory('Users', function($firebaseArray, $firebaseObject, FirebaseUrl){

      var Users = {};

      return Users;

      });

    The purpose of this factory is to provide us with the ability to get either a specific user's data, or to get

    a list of all of our users. Note that while Firebase provides us with a means of authentication, all of the

    authentication data is separate from our Firebase data and can't be queried. It is up to us to store any

    d i hi i b ll

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    15/49df di b PRO iAre you a developer? Try out the HTML to PDF API

    custom user data within Firebase manually.

    angular.module('angularfireSlackApp')

      .factory('Users', function($firebaseArray, $firebaseObject, FirebaseUrl){

      var usersRef = new Firebase(FirebaseUrl+'users');

      var Users = {};

      return Users;

      });

    Data in Firebase is stored in a tree structure and child nodes can be referenced by adding a path to

    our FirebaseUrl , so https://firebase-name-here.firebase.io.com/users  refers to the users  node.

    angular.module('angularfireSlackApp')

      .factory('Users', function($firebaseArray, $firebaseObject, FirebaseUrl){

      var usersRef = new Firebase(FirebaseUrl+'users');

      var users = $firebaseArray(usersRef);

      var Users = {};

      return Users;

    });

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://en.wikipedia.org/wiki/Tree_%28data_structure%29

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    16/49

    Are you a developer? Try out the HTML to PDF API

      });

    It's also good to know that while $firebaseArray  will return pseudo array, meaning it will act a lot like

    an array in javascript, however, methods like splice() , push() , pop()  will only affect data locallyand not on the Firebase. Instead, $firebaseArray  provides methods named $add  and $remove  to

    provide similar functionality while keeping your data in sync. Read the $firebaseArray

    Documentation For a complete understanding of how $firebaseArray  should be used.

    var Users = {

      getProfile: function(uid){

      return $firebaseObject(usersRef.child(uid));

      },

      getDisplayName: function(uid){

      return users.$getRecord(uid).displayName;

      },

      all: users

    };

    getProfile(uid)  allows us to get a $firebaseObject  of a specific user's profile, while all  returns a

    f ll th i h l f ti th t t '

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebasearray

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    17/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    $firebaseArray  of all the users. getDisplayName(uid)  is a helper function that returns a user's

    displayName  when given a uid . We will be keying our data by the uid  that comes back from our

    Firebase auth data, so data in our Firebase will look similar to:

    {

      "users": {

      "simplelogin:1":{

      "displayName": "Blake Jackson"

      }

      }

    }

    Now that our Users  service is created, let's create a controller for updating a user's profile. First we'll

    need to create a new state in app/app.js  to resolve  a couple dependencies. We want to have the

    user's auth  data and their profile available to us before our controller is instantiated.

    .state('profile', {

      url: '/profile',

      resolve: {

      auth: function($state, Users, Auth){

    return Auth.$requireAuth().catch(function(){

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    18/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      return Auth.$requireAuth().catch(function(){

      $state.go('home');

      });

      },

      profile: function(Users, Auth){

      return Auth.$requireAuth().then(function(auth){

      return Users.getProfile(auth.uid).$loaded();

      });  }

      }

    })

    We left the controller  and templateUrl  properties out of the state configuration temporarily

    because we haven't created them yet. The auth  dependency is similar to the requireNoAuth

    dependency we created for login  and register , except it does the inverse, where the user is

    redirected to the home  state if they're not authenticated. The .catch  function is a shorthand for

    handling promises if we don't want to provide a success handler. The profile  dependency also

    ensures authentication, but resolves to the user's profile using the getProfile  function we createdin our Users  service. $loaded  is a function provided by both $firebaseObject  and

    $firebaseArray  that returns a promise that gets resolved when the data from Firebase is available

    locally.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    19/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    angular.module('angularfireSlackApp')

      .controller('ProfileCtrl', function($state, md5, auth, profile){

      var profileCtrl = this;

      });

    We'll be using Gravatar to get profile picture functionality in our application. Gravatar is a service that

    provides us with a user's profile picture when given an email, however the email needs to be md5

    hashed. Luckily, there are many modules available that can do this for us, and angular-md5 was

    already included in our seed codebase.

    profileCtrl.profile = profile;

    profileCtrl.updateProfile = function(){

      profileCtrl.profile.emailHash = md5.createHash(auth.password.email);

      profileCtrl.profile.$save();

    };

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://en.gravatar.com/https://en.wikipedia.org/wiki/MD5https://github.com/gdi2290/angular-md5

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    20/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Here we're getting the current user's email from the auth  data that was resolved from our router,

    hashing it and setting to emailHash  on profile . displayName  will be set from the template we'll be

    creating next using ng-model .

    getGravatar: function(uid){

      return '//www.gravatar.com/avatar/' + users.$getRecord(uid).emailHash;

    },

    url: '/profile',

    controller: 'ProfileCtrl as profileCtrl',

    templateUrl: 'users/profile.html',

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    21/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

     

      Edit Profile

     

     

     

      You'll need a display name before you can start chatting.

     

     

     

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    22/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Creating the Channels SidebarNow that our Users can authenticate and have profiles, we can finally start to create the functionality

    for Channels. We're going to start by creating the left sidebar for listing channels and display the

    current user's profile.

    angular.module('angularfireSlackApp')

      .factory('Channels', function($firebaseArray, FirebaseUrl){

      var ref = new Firebase(FirebaseUrl+'channels');

      var channels = $firebaseArray(ref);

      return channels;

      });

    .state('channels', {

      url: '/channels',

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    23/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      resolve: {

      channels: function (Channels){

      return Channels.$loaded();

      },

      profile: function ($state, Auth, Users){

      return Auth.$requireAuth().then(function(auth){

      return Users.getProfile(auth.uid).$loaded().then(function (profile){

      if(profile.displayName){

      return profile;

      } else {

      $state.go('profile');

      }

      });

      }, function(error){

      $state.go('home');

      });

      }

      }

    })

    templateUrl  and controller  are temporarily omitted since we haven't created them yet. We're

    resolving two dependencies here: channels , which is promising our $firebaseArray  of channels,

    and profile , which is a lot like the profile  dependency in the profile  state, but we're ensuring

    that the user already a displayName set, otherwise they're taken to the profile  state, and if they're

    not authenticated, they get sent to the home  state.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    24/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    , y g

    angular.module('angularfireSlackApp')

      .controller('ChannelsCtrl', function($state, Auth, Users, profile, channels){

      var channelsCtrl = this;

      });

    channelsCtrl.profile = profile;

    channelsCtrl.channels = channels;

    channelsCtrl.getDisplayName = Users.getDisplayName;

    channelsCtrl.getGravatar = Users.getGravatar;

    channelsCtrl.logout = function(){

      Auth.$unauth();

    $ ('h ')

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    25/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      $state.go('home');

    };

     

     

      FireSlack

     

     

      Channels

     

     

     

     

    di l " "

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    26/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

     

      {{ channelsCtrl.profile.displayName }}

     

     

      edit profile

      /  logout

     

     

     

     

    url: '/channels',

    controller: 'ChannelsCtrl as channelsCtrl',

    templateUrl: 'channels/index.html',

    resolve: {

      requireNoAuth: function($state, Auth){

    return Auth $requireAuth() then(function(auth){

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    27/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      return Auth.$requireAuth().then(function(auth){

      $state.go('channels');

      }, function(error){

      return;

      });

      }

    }

    profileCtrl.updateProfile = function(){

      profileCtrl.profile.emailHash = md5.createHash(auth.password.email);

      profileCtrl.profile.$save().then(function(){

      $state.go('channels');

      });

    };

    This requireNoAuth  dependency is a lot like the one on login  and register  but it sends the user to

    the channels  state. If you want, you can change the requireNoAuth  dependency on login  and

    register  to also send the user to the channels  state as well. Now when we're logged in and visit

    http://localhost:4000  we should be sent to the channels  state. In that state, we can see the

    sidebar we just created for our application. There should be the logged in user's name and Gravatar

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    28/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    at the bottom of the sidebar, and an edit profile and logout link next to it. We're using the ui-sref

    directive that comes with ui-router  to specify what state we should navigate to on click. If we click

    on edit profile, we can update the user's displayName  and it should send us back to the channels

    state when we submit the form. The logout link should log us out and send us back to the home  state.

    Now let's create the view to create channels.

    .state('channels.create', {

      url: '/create',

      templateUrl: 'channels/create.html',

      controller: 'ChannelsCtrl as channelsCtrl'})

    This state is a child state of the channels  controller (the dot notation in the state name specifies

    parentState.childState ). This state will also use ChannelsCtrl . We'll want to render our child

    states to the right of the sidebar. We need to declare another ui-view  tag in order for our child

    states to appear. You can read more about ui-router 's nested states at this Github Wiki.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://github.com/angular-ui/ui-router/wiki/Nested-States-and-Nested-Views

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    29/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

     

    channelsCtrl.newChannel = {

      name: ''

    };

    channelsCtrl.createChannel = function(){

      channelsCtrl.channels.$add(channelsCtrl.newChannel).then(function(){

      channelsCtrl.newChannel = {

      name: ''

      };

      });

    };

    The $add  function on the channels $firebaseArray  provides similar functionality to the .push()

    function on a Javascript Array , but keeps our data in sync with Firebase, while returning a promise.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    30/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Once the new channel is created we'll need to clear out the newChannel  object.

      Create a channel

     

     

     

     

      Channels

     

      # {{ channel.name }}

     

    We're using the directive to iterate over our array of h l

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    31/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    We're using the ng-repeat  directive to iterate over our array of channels .

      Channels

     

      # {{ channel.name }}

     

     

      + create channel

     

    We're now able to click on the create channel link and start creating channels!

    Adding Messaging Functionality

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    32/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    angular.module('angularfireSlackApp')

      .factory('Messages', function($firebaseArray, FirebaseUrl){

      var channelMessagesRef = new Firebase(FirebaseUrl+'channelMessages');

      return {

      forChannel: function(channelId){

      return $firebaseArray(channelMessagesRef.child(channelId));

      }

      };

      });

    The forChannel  function on our service returns a $firebaseArray  of messages when provided a

    channelId . Later in this tutorial, we'll create a forUsers  function for retrieving direct messages.

    .state('channels.messages', {

      url: '/{channelId}/messages',

      resolve: {

      messages: function($stateParams, Messages){

      return Messages.forChannel($stateParams.channelId).$loaded();

      },

      channelName: function($stateParams, channels){

      return '#'+channels.$getRecord($stateParams.channelId).name;

      }

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    33/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      }

    })

    This state will again be a child state of channels . Our url will have a channelId  parameter. We can

    access this parameter with $stateParams , provided by ui-router . We're resolving messages ,

    which is using the forChannel  function from our Messages  service, and channelName  which we'll be

    using to display the channel's name in our messages pane. Channel names will be prefixed with a # .

    The channels  dependency we're injecting is coming from the parent state channels  since child

    states inherit their parent's dependencies. We'll come back and add the controller  and

    templateUrl  properties once we create our controller and template.

    angular.module('angularfireSlackApp')

      .controller('MessagesCtrl', function(profile, channelName, messages){

      var messagesCtrl = this;

      });

    Again, the profile  dependency that we're injecting will actually come from the parent state

    th t l t th t ' fil

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    34/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    channels  that resolves to the current user's profile.

    messagesCtrl.messages = messages;

    messagesCtrl.channelName = channelName;

    messagesCtrl.message = '';

    messagesCtrl.sendMessage = function (){

      if(messagesCtrl.message.length > 0){

      messagesCtrl.messages.$add({

      uid: profile.$id,  body: messagesCtrl.message,

      timestamp: Firebase.ServerValue.TIMESTAMP

      }).then(function (){

      messagesCtrl.message = '';

      });

      }

    };

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    35/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    A message object will need to contain uid , which will be how we identify who sent the message.

    body  contains the message our user input, and timestamp  is a constant from Firebase  that tells

    the Firebase servers to use the their clock for the timestamp. When a message sends successfully,

    we'll want to clear out messagesCtrl.message  so the user can type a new message.

      {{ messagesCtrl.channelName }}

     

     

     

      {{ channelsCtrl.getDisplayName(message.uid) }}

      {{ message.timestamp | date:'short' }}

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    36/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

     

     

      {{ message.body }}

     

     

     

     

     

      Send

       

    Here we're creating a header to display the channelName  from our controller. Then we'reng-repeat ing over messages  and using message.uid  and the helper functions from channelsCtrl

    to get the user's display name and Gravatar. We're also using Angular's date  filter on the timestamp

    to display a short timestamp. Finally, at the bottom of our view we have the form for sending

    messages which submits to the sendMessage  function from our controller.

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    37/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    url: '/{channelId}/messages',

    templateUrl: 'channels/messages.html',

    controller: 'MessagesCtrl as messagesCtrl',

    # {{ channel.name }}

    We're specifying the parameters for the channels.messages  state within the ui-sref  directive. The

    ui-sref-active  directive will add the specified class ( selected  in our case) to the element when a

    state specified in a sibling or child ui-sref  directive. Now we should be able to navigate between

    channels and start chatting!

    channelsCtrl.createChannel = function(){

      channelsCtrl.channels.$add(channelsCtrl.newChannel).then(function(ref){

      $state.go('channels.messages', {channelId: ref.key()});

      });

    };

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    38/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Creating Direct MessagesNow that we have working channels with messaging, adding direct messages will be easier since we

    can reuse a lot of the existing functionality we have.

      var userMessagesRef = new Firebase(FirebaseUrl+'userMessages')

    return {

      forChannel: function(channelId){

      return $firebaseArray(channelMessagesRef.child(channelId));

      },

      forUsers: function(uid1, uid2){

      var path = uid1 

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    39/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      return $firebaseArray(userMessagesRef.child(path));

      }

    };

    We'll be storing our direct messages in Firebase like so:

    {

      "userMessages": {

      "simplelogin:1": {

      "simplelogin:2": {

      "messageId1": {

      "uid": "simplelogin:1",

      "body": "Hello!",

      "timestamp": Firebase.ServerValue.TIMESTAMP

      },

      "messageId2": {

      "uid": "simplelogin:2",

      "body": "Hey!",

      "timestamp": Firebase.ServerValue.TIMESTAMP

      }

      }

      }

      }

    }

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    40/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Since we always want to reference the same path in our Firebase regardless of which id was passed

    first, we'll need to sort our ids before referencing the direct messages.

    .state('channels.direct', {

      url: '/{uid}/messages/direct',

      templateUrl: 'channels/messages.html',

      controller: 'MessagesCtrl as messagesCtrl',

      resolve: {

      messages: function($stateParams, Messages, profile){

      return Messages.forUsers($stateParams.uid, profile.$id).$loaded();

      },

      channelName: function($stateParams, Users){

      return Users.all.$loaded().then(function(){

      return '@'+Users.getDisplayName($stateParams.uid);

      });

      }

      }

    });

    This state is almost identical to channels.messages , using the same templateUrl  and controller .

    W ' i diff t d th d d i i th

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    41/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    We're using a different url , and the messages  dependency is using the Messages.forUsers

    function that we just created. The channelName  dependency also looks up the other user's display

    name, and prefixes it with @ .

    channelsCtrl.users = Users.all;

      + create channel

    Direct Messages

     

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    42/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Adding Presence to UsersHaving direct messaging is an important feature to any chat application, but it's also very useful to

    know what users are online. Firebase makes this very easy for us. Read the Firebase Documentation

    to see some example code using presence. While this code is written using the core Firebase library,we're going to replicate the same functionality using AngularFire.

    var usersRef = new Firebase(FirebaseUrl+'users');

    var connectedRef = new Firebase(FirebaseUrl+'.info/connected');

    setOnline: function(uid){

      var connected = $firebaseObject(connectedRef);

      var online = $firebaseArray(usersRef.child(uid+'/online'));

    connected $watch(function (){

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://www.firebase.com/docs/web/guide/offline-capabilities.html#section-sample

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    43/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      connected.$watch(function (){

      if(connected.$value === true){

      online.$add(true).then(function(connectedRef){

      connectedRef.onDisconnect().remove();

      });

      }

      });

    }

    This function watches for changes at the .info/connected  node and will $add  any open

    connections to a $firebaseArray  keyed under online  within the user's profile. This allows us to

    track multiple connections (in case the user has multiple browser windows open), which will get

    removed when the client disconnects.

    Users.setOnline(profile.$id);

    channelsCtrl.logout = function(){

      channelsCtrl.profile.online = null;

      channelsCtrl.profile.$save().then(function(){

    Auth $unauth();

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    44/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      Auth.$unauth();

      $state.go('home');

      });

    };

     

      {{ channelsCtrl.profile.displayName }}

    We're also dynamically adding the online  class to the span tag using ng-class , based on if the

    $firebaseArray  containing connections in the profile is present.

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    45/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    We're now able to see when our users are online! Our application is almost ready for production. In

    the next sections we will go over securing our data and deploying our application live.

    Securing Your Data with Security

    RulesWhen you first create a Firebase, the default security rules allow full read and write access. While this

    makes it a lot easier to get started developing, it's always strongly recommended that you create

    security rules to make sure that your data stays consistent and secured. There are three kinds ofrules, .read , .write , and .validate  for controlling access and validating your data.

    {

      "rules":{

      ".read": true,

      "users":{

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    46/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      "$uid":{

      ".write": "auth !== null && $uid === auth.uid",

      "displayName":{

      ".validate": "newData.exists() && newData.val().length > 0"

      },

      "online":{

      "$connectionId":{

      ".validate": "newData.isBoolean()"

      }

      }

      }

      },

      "channels":{

      "$channelId":{

      ".write": "auth !== null",

      "name":{

      ".validate": "newData.exists() && newData.isString() && newData.val().length > 0"

      }

      }

      },

      "channelMessages":{  "$channelId":{

      "$messageId":{

      ".write": "auth !== null && newData.child('uid').val() === auth.uid",

      ".validate": "newData.child('timestamp').exists()" ,

      "body":{

      ".validate": "newData.exists() && newData.val().length > 0"

      }

      }

      }

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    47/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

      },

      "userMessages":{

      "$uid1":{

      "$uid2":{

      "$messageId":{

      ".read": "auth !== null && ($uid1 === auth.uid || $uid2 === auth.uid)",

      ".write": "auth !== null && newData.child('uid').val() === auth.uid",

      ".validate": "$uid1 < $uid2 && newData.child('timestamp').exists()",

      "body":{

      ".validate": "newData.exists() && newData.val().length > 0"

      }

      }

      }

      }

      }

      }

    }

    Deploying to Firebase

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdf

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    48/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    Stay on the bleeding edge of

    your favorite technologies.

    Enter your email to receive courses on AngularJS,Node, Android, Swift and more. 1-2 emails per

    Did you like this course? Share

    it!

     

    p y g

    {

      "firebase": "firebase-name-here",

      "public": "dist"

    }

    firebase deploy  may prompt you to log in, but afterwards it should push your application to

    Firebase's hosting service. Now if you visit https://firebase-name-here.firebaseapp.com/ you shouldsee our completed app, ready for the world to use!

     week, no spam.  

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fthinkster.io%2Fangularfire-slack-tutorial%2Fhttps://twitter.com/intent/tweet?text=Learn%20to%20build%20a%20Real-Time%20Slack%20clone%20with%20AngularFire&url=https%3A%2F%2Fthinkster.io%2Fangularfire-slack-tutorial%2F&related=gothinkster,angulartutorialhttps://plus.google.com/share?url=https%3A%2F%2Fthinkster.io%2Fangularfire-slack-tutorial%2Fhttps://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fthinkster.io%2Fangularfire-slack-tutorial%2Fhttps://twitter.com/intent/tweet?text=Learn%20to%20build%20a%20Real-Time%20Slack%20clone%20with%20AngularFire&url=https%3A%2F%2Fthinkster.io%2Fangularfire-slack-tutorial%2F&related=gothinkster,angulartutorialhttps://plus.google.com/share?url=https%3A%2F%2Fthinkster.io%2Fangularfire-slack-tutorial%2F

  • 8/17/2019 Thinkster Io Angularfire Slack Tutorial

    49/49

    pdfcrowd.comopen in browser PRO version Are you a developer? Try out the HTML to PDF API

    There's more to learn! Check out our latest Firebase courses

    Help us educate millions of people around the

    world for free

    Home  Topics  Pro

    About  Contact Us

    © 2015 Thinkster

    Privacy Policy | Terms of Use

    http://pdfcrowd.com/http://pdfcrowd.com/redirect/?url=https%3a%2f%2fthinkster.io%2fangularfire-slack-tutorial&id=ma-151130115122-6285c6fchttp://pdfcrowd.com/customize/http://pdfcrowd.com/html-to-pdf-api/?ref=pdfhttps://thinkster.io/topics/firebasehttps://thinkster.io/https://thinkster.io/#start-learninghttps://thinkster.io/prohttps://thinkster.io/aboutmailto:[email protected]://thinkster.io/privacy-policyhttps://thinkster.io/tos

Recommended