• Home
  • About
    • Glen Cooney photo

      Glen Cooney

      Welcome to my official portfolio site

    • Learn More
    • Email
    • LinkedIn
    • Github
    • StackOverflow
  • Posts
    • All Posts
    • All Tags
  • Projects

Chatterbox

24 Jul 2016

Reading time ~3 minutes

Github

The Project

Chatterbox is a chatroom application that updates in realtime through the use of Angular and Firebase.

Tools

Javascript, Angular, UI-Router, jQuery, Firebase, AngularFire, Grunt, ngCookies, Bootstrap, Brackets, Ubuntu

The App

Upon loading the app for the first time, the user is prompted to enter a username. From there, they may click to enter any of the chatrooms listed in the left navbar to see messages for that room. Upon closing and/or reloading the page, cookies ensure the user remains logged in1 as their chosen username.

Process

The first task was to set up my Firebase database and associated service for facilitating CRUD operations. Using my RoomSrv service, I was able to push new rooms to the database, as well as pull down an array of all available Rooms.

To allow users to create new chat rooms, I created a button to trigger a modal input box to appear, allowing users to name and create new chat rooms. This was accomplished using UI Bootstrap’s $uibModal service.

Upon receiving user input, an $emit and corresponding $on function are used to facilitate communication between the ModalCtrl and RoomCtrl Controllers.

function ModalCtrl($scope, $uibModal){
  ...

  modalInstance.result.then(
    function(roomName){
      $scope.$emit('createNewRoom', roomName);
    }
    );
  };
}
}

function RoomCtrl($scope, RoomSrv, $cookies, $window){
  ...

  $scope.$on('createNewRoom', function(event, args){
      $scope.addRoom({name: args});
    });
    ...
  };

The next step was to allow messages to be posted in chat rooms. Upon creation, each message object was set up to be associated with the currently selected room via its $id autogenerated by Firebase.

(function(){
  function MessageCtrl($scope, MessageSrv, $cookies){
    $scope.send = function(){
      if($scope.messageContent !== ''){
        MessageSrv.addMessage(
          {
            content: $scope.messageContent,
            roomID: $scope.selectedRoom.$id,
            sentAt: 'default',
            username: $cookies.get('currentUser')
          }
        );

        $scope.messageContent = '';
      }
    }
  }

  angular
    .module('chatterBox')
    .controller('MessageCtrl', ['$scope', 'MessageSrv', '$cookies', MessageCtrl]);
})();

Retrieving the messages to render to the view involves some unorthdox syntax, which was initially confusing:

RoomSrv.getMessages = function(id){
  return $firebaseArray(firebaseRef.child('messages')
                                   .orderByChild('roomID')
                                   .equalTo(id));
}

Understanding this statement involves looking at the return types of each function. .child() returns a Firebase reference to the object containing all messages. orderByChild() returns a Query with all messages ordered by roomID. Finally equalTo() filters the Query down further to only those messages with a matching roomID. Finally $firebaseArray() converts the final query into an array to be rendered to the View.```

What is peculiar is that the 2nd statement seems to both reorder the array 2 of results and passes a new array, with equalTo() receiving the key to compare id against. I can only assume that this was coded this way for some purpose such as optimization, but I would think retrieving queries using key/value pairs would make more sense:

  var ref = firebaseRef.child('messages');
  return $firebaseArray(ref[roomID][id]);

Nonetheless, with the display of messages and rooms out of the way, the next step was to set up Users. Upon loading the page for the first time, users are greeted with a Modal prompt asking for a username. This username is stored in a cookie using the ngCookies service. To make this step manditory, the code the preventDefault() function is used to prevent the Modal from being dismissed the the input field is empty.

$scope.$on('modal.closing', function(event, reason, closed){
  if($scope.userName === ''){
    event.preventDefault();
  }
})

Finally, I added functionality to MessageCtrl to allow users to post new messages into chat rooms associated with their username.

Outcome

This was a fairly straightforward project, and for me gave me a taste having a Frontend interacting with a non-Rails backend. Tripping up on the terminology for retrieving a message was a bit confusing, but appears to be relatively common with web development, as I have discovered. Given the diversity of toolsets and the “hacks” required in order to optimize functionality for a live web app, I have come to expect some things to initially defy conventional comprehension.

Annotations

1 Chatterbox uses only a rudimentary user system, not a complex system of authentication. This was done to limit the scope of the project to only its most valuable features.

2 Not a true Array, of course, but an object behaving as one in this context.



BlocFrontendAngular Like Tweet +1