AngularJS Chatroom Tutorial: No Database or Sockets Required

Comments 8 Standard

So I’ve been playing around with Angular lately and I decide to try my hand at making an Angular chat room since angular is great when it comes to keeping the page display current as the DOM changes. In order to deal with the message sending (and for the ease of getting a working version going) I decided to use PubNub which handles all the message transport quickly and easily.

1. Create A PubNub Account

In order to use this you’ll need to sign up for a PubNub account. There is a free plan and several paid for plans available for you to choose from. Once your account is created you’ll need to find your subscribe key and publish key.

2. Create the chat file

Now you’re going to create the main chat file. This will display your chat room. At the top of the file replace the subscribe_key and publish_key with the one you see in your newly created account.

index.html

<!doctype html>
<html ng-app="angular_chat">
  <head>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
    <script src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.4.0.js"></script>
    <div pub-key="PUB_KEY_GOES_IN_HERE" sub-key="SUBSCRIBE_KEY_GOES_IN_HERE" ssl="off" origin="pubsub.pubnub.com" id="pubnub"></div>
    <script src="http://cdn.pubnub.com/pubnub-3.1.min.js"></script>
    <script src="angular-chat.js"></script>
    <link rel="stylesheet" href="style.css" rel="stylesheet">
    <link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
  </head>
  <body>
  <div class='container-fluid' ng-controller="chatCtrl">
    <div ng-include="'chatHeader.html'"></div>
    <div ng-Show="!loggedIn" id="login">
      <h2>Login</h2>
      <label for="username">
        <b>Username:</b>
      </label>
      <label>
        <input type="text" ng-model="message.username" />
      </label>
      <span ng-click="attemptLogin()">
        <i></i> Go Chat
      </span>
    </div>
    <div ng-Show="loggedIn" id="chat">
      <table>
        <tr ng-repeat="chat in chatMessages">
            <td style="width: 30%;">
              <b>{{chat.username}}</b><br/>
              <small>
               {{chat.date}} @ {{chat.time}}
              </small>
            </td>
            <td colspan="2">{{chat.text}}</td>
        </tr>
        <tr ng-show="chatMessages.length == 0">
          <td colspan="3">No messages yet!</td>
        </tr>
      </table>
    </div>
    <form ng-Show="loggedIn" ui-keypress="{13:'postMessage()'}">
      <div id="inputMessage">
        <input type="text" placeholder="Say hello!" ng-model="message.text" />
        <span ng-click="postMessage()">
          <i></i> Post
        </span>
      </div>
    </form>
    <div ng-include="'chatFooter.html'"></div>
  </div>
  </body>
</html>

3. Create the angular file

Now we’re going to add in a file for our angular scripts. At the top of the file you’ll find some config variables which you can change as you see fit to set the default username, how many messages to show at a time, and the name of the chat channel you’ll be connecting to.

angular-chat.js

/***
 * File: angular-chat.js
 * Author: Jade Krafsig
 * Source: design1online.com, LLC
 * License: GNU Public License
 ***/

/***
 * Purpose: load bootstrap ui angular modules
 * Precondition: none
 * Postcondition: modules loaded
 ***/
angular.module('angular_chat', ['ui.bootstrap']);

/***
 * Purpose: load the existing chat logs
 * Precondition: none
 * Postcondition: chat logs have been loaded
 ***/
function chatCtrl($scope, $http) { 

  /***
   * Configurable global variables
   ***/
  $scope.chatChannel = "angular_chat";
  $scope.messageLimit = 50;
  $scope.defaultUsername = "Guest";

  /***
   * Static global variables
   ***/
  $scope.loggedIn = false;
  $scope.errorMsg;
  $scope.realtimeStatus = 0;

  /***
   * Purpose: clear the message object
   * Precondition: none
   * Postcondition: message object has been reset
   ***/
  $scope.clearMsg = function() {
    $scope.message = {
      username: $scope.defaultUsername,
      email: 'n/a',
      text: ''
    };
  }

  $scope.clearMsg();

  /***
   * Purpose: load the existing chat logs
   * Precondition: none
   * Postcondition: chat logs have been loaded
   ***/
  $scope.chatLogs = function() {
    PUBNUB.history( {
      channel : $scope.chatChannel,
      limit   : $scope.messageLimit
    }, function(messages) {
      // Shows All Messages
      $scope.$apply(function(){
        $scope.chatMessages = messages.reverse();          
      }); 
    });
   }

  /***
   * Purpose: load the existing chat logs
   * Precondition: none
   * Postcondition: chat logs have been loaded
   ***/
   $scope.attemptLogin = function() {
    $scope.errorMsg = "";

    if (!$scope.message.username) {
      $scope.errorMsg = "You must enter a username.";
      return;
    }

    if (!$scope.realtimeStatus) {
      $scope.errorMsg = "You're not connect to PubNub.";
      return;
    }

    $scope.loggedIn = true;
   }

  /***
   * Purpose: remove error message formatting when the message input changes
   * Precondition: none
   * Postcondition: error message class removed from message input
   ***/
  $scope.$watch('message.text', function(newValue, oldValue) {
    if (newValue)
      $("#inputMessage").removeClass("error");
      $scope.errorMsg = "";
  }, true);

  /***
   * Purpose: trying to post a message to the chat
   * Precondition: loggedIn
   * Postcondition: message added to chatMessages and sent to chatLog
   ***/
  $scope.postMessage = function() {

    //make sure they are logged in
    if (!$scope.loggedIn) {
      $scope.errorMsg = "You must login first.";
      return;
    }

    //make sure they enter a chat message
    if (!$scope.message.text) {
      $scope.errorMsg = "You must enter a message.";
      $("#inputMessage").addClass("error");
      return;
    }

    //set the message date
    d = new Date();
    $scope.message.date = d.getDay() + "/" + d.getMonth() + "/" + d.getFullYear();
    $scope.message.time = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();

   PUBNUB.publish({
      channel : $scope.chatChannel,
      message : $scope.message
    });

    $scope.message.text = "";
  };

  /***
   * Purpose: connect and access pubnub channel
   * Preconditions: pubnub js file init
   * Postconditions: pubnub is waiting and ready
   ***/
  PUBNUB.subscribe({
    channel    : $scope.chatChannel,
    restore    : false, 
    callback   : function(message) { 
      //update messages with the new message
      $scope.$apply(function(){
        $scope.chatMessages.unshift(message);          
      }); 
    },

    error      : function(data) {
      $scope.errorMsg = data;
    },

    disconnect : function() {   
      $scope.$apply(function(){
        $scope.realtimeStatus = 0;
      });
    },

    reconnect  : function() {   
      $scope.$apply(function(){
        $scope.realtimeStatus = 1;
      });
    },

    connect    : function() {
      $scope.$apply(function(){
        $scope.realtimeStatus = 2;
        //load the chat logs
        $scope.chatLogs();
      });
    }
  });

  /***
   * Purpose: trying to post a message to the chat
   * Precondition: loggedIn
   * Postcondition: message added to chatMessages and sent to chatLog
   ***/
  $scope.attemptLogout = function() {
    $("#inputMessage").removeClass("error");
    $scope.clearMsg();
    $scope.loggedIn = false;
  }
}

4. Create the header and footer files

These files will put a static header and footer on your chat room.

chatHeader.html

<div>
  <h1>Angular Chat</h1>
  <h5><i></i> using Bootstrap and PubNub</h5>
  <p>&nbsp;</p>
  <div ng-show="realtimeStatus == 0">
    <span><i></i> Disconnected</span>
  </div>
  <div ng-show="realtimeStatus == 1">
    <span><i></i> Connecting...</span>
  </div>
  <div ng-show="realtimeStatus == 2">
    <span><i></i> Connected</span>
  </div>
  <div ng-show="loggedIn" id="logout">
    <span ng-click="attemptLogout()"><i></i> Logout</span>
  </div>
</div>
<p>&nbsp;</p>
<div ng-show="errorMsg">
  <i></i> <b>Error:</b> {{errorMsg}}
</div>

chatFooter.html

<div>
  2013 &copy; design1online.com
</div>

5. Create the stylesheet

This will adjust the height of the chat window and the chat header status and logout buttons.

style.css

#chat {
 height: 33em; 
 overflow: auto;
 background-color: #EEE;
 border-radius: .25em;
 margin-bottom: 1em;
}

.status {
  float: left;
  margin-top: -1em;
}

#logout {
  float: right;
  margin-top: -1.2em;
}

6. Try the working version

See what the chat room looks like on my plunkr account or fork it on GitHub.

Advertisements

jQuery File Trees and File Managers

Leave a comment Standard
  • jQuery File Tree
    • Produces valid, semantic XHTML
    • Fully customizable via CSS
    • Ability to style icons based on file extension
    • Uses AJAX to fetch file information on the fly
    • Easy to configure and implement
    • Includes connector scripts for PHP and ASP.NET (C#)
    • Supports custom connector scripts for extended functionality
    • Customizable expand/collapse event
    • Customizable expand/collapse speeds
    • Supports jQuery easing functions
    • Single- and multi-folder views
    • Configurable load message
  • El Finder
    • All operations with files and folders on a remote server (copy, move, upload, create folder/file, rename, etc.)
    • High performance server backend and light client UI
    • Multi-root support
    • Local file system, MySQL, FTP volume storage drivers
    • Background file upload with Drag & Drop HTML5 support
    • List and Icons view
    • Keyboard shortcuts
    • Standard methods of file/group selection using mouse or keyboard
    • Move/Copy files with Drag & Drop
    • Archives create/extract (zip, rar, 7z, tar, gzip, bzip2)
    • Rich context menu and toolbar
    • Quicklook, preview for common file types
    • Edit text files and images
    • “Places” for your favorites
    • Calculate directory sizes
    • Thumbnails for image files
    • Easy to integrate with web editors (elRTE, CKEditor, TinyMCE)
    • Flexible configuration of access rights, upload file types, user interface and other
    • Extensibility
    • Simple client-server API based on JSON
  • jQuery File Manager
    • Move/Rename/Delete
    • Restrict viewing to a specific folder
    • Customisable output
  • File Thingy
    • Upload multiple files at once
    • Multiple users and user groups
    • Create subdirectories
    • Rename, move, delete and copy files and folders
    • Search for file and folder names
    • Control access to files based on black- or whitelists
    • Edit text files
    • Unzip files without downloading
    • Easy customization of the CSS based layout
    • Translate into your own language
  • Kae’s File Manager
    • drag-and-drop everything
    • icon-view
    • list-view
    • plugins
    • image manipulations
    • slideshows
    • easy installation and upgrades
    • syntax-highlighting text editor
    • search engine
    • tagging
    • multi-lingual
    • plugins for mp3 playback
    • video playback
  • SFBrowser
    • ajax file upload
    • optional as3 swf upload (queued multiple uploads, upload progress, upload canceling, selection filtering, size filtering)
    • localisation (English, Dutch or Spanish)
    • server side script connector
    • plugin environment (with filetree and imageresize plugin)
    • data caching (minimal server communication)
    • sortable file table
    • file filtering
    • file renaming
    • file duplication
    • file download
    • file/folder context menu
    • file preview (image, audio, video, text/ascii and swf)
    • folder creation
    • multiple files selection
    • inline or overlay window
    • window dragging and resizing
    • cookie for size, position and path
    • keyboard shortcuts
    • key file selection
  • jsTree
    • Various data sources – HTML, JSON, XML
    • Supports AJAX loading
    • Drag & drop support
    • Highly configurable
    • Theme support + included themes
    • Uses jQuery’s event system
    • Optional keyboard navigation
    • Maintain the same tree in many languages
    • Inline editing
    • Open/close optional animation
    • Define node types and fine tune them
    • Configurable multitree drag & drop
    • Optional checkbox tree support
    • Search function
    • Supports plugins
    • Optional state saving using cookies

JQuery FIX: uncaught exception: Syntax error, unrecognized expression: #

Leave a comment Standard

This one threw me for a bit of a loop today so I thought I’d share. I couldn’t get my debugger to give me a line number for this error, however it turns out I had a double # sign in my jQuery code.

<script>
$("##myButton").click(function () { alert('You are pressing my buttons'); });
</script>

Notice the duplicate pound sign (#) in the ID reference for the button. Remove the duplicate pound sign and you should be good to go.

DOJO Grid FIX: Showing/Hiding Grid Causes Grid Headings To Disappear

Leave a comment Standard

DOJO has a quirky problem if you try to show/hide grids using Javascript or JQuery. This is especially trying if you have multiple grids on a page and you only want to display one at a time. If your column headers aren’t “fixed” they disappear once you hide the grid and then try to display it again.

The only way around this is to use the .resize() method on the grid reference after you try to show it again. This re-draw the grid with all the columns showing up again.

<script>

$("#showSubGridButton").click(function() {
    $("#mainGridContainer").hide();
    $("#subGridContainer").show();
    refreshGrid(subGrid);
});
$("#showMainGridButton").click(function() {
    $("#subGridContainer").hide();
    $("#MainGridContainer").show();
    refreshGrid(mainGrid);
});
function refreshGrid(gridRef) {
   gridRef.resize();
}
</script>

JQuery Tutorial: Add & Remove A CSS Class From ALL Input Text Fields

Leave a comment Standard

One of the things I like most about JQuery is how easy it is to add and remove CSS classes from a form when you’re trying to do input validation. If the user enters an error you can quickly add a red box around the field or perhaps change the background color and text colors to red. Then once the user’s correct the error you can just as easily replace the class with another one. Or if you want to do all of your validation checking on the form submit then you can easily add and remove all of the error classes.

 

To add a CSS class to a single input field:

$(“#field_id_name_here”).addClass(“your_class_name_here”);

 

To add a CSS class to ALL input text fields:

$(“input:text”).addClass(“your_class_name_here”);

 

To remove a CSS class on a single input field:

$(“#field_id_name_here”).removeClass(“your_class_name_here”);

 

To remove a CSS class on ALL input text fields:

$(“input:text”).removeClass(“your_class_name_here”);

Jquery Tutorial: Select or deselect a radio button

Comment 1 Standard

Today was the first time I tried to do this and believe it or not many of the tutorials/examples I found on good ole Google didn’t work. So after about 45 minutes I finally found an example that worked for me. Hopefully you won’t spend as long looking for the answer as I did.

//select the radio button
$("input[name=your_field_name_here][value='the_value_of_that_field_here']").attr("checked","checked");

//deselect the radio button
$("input[name=your_field_name_here][value='the_value_of_that_field_here']").attr("checked","");

Reference:  Comment posted by JoaoLuiz at the bottom

jQuery Tutorial: Toggle enabled/disabled attribute for multiple radio buttons

Leave a comment Standard

So today I spent 10 minutes thinking about how to write something in jQuery that would go through and toggle the disable status for a group of radio buttons that have been dynamically generated and then it suddenly hit me — duh, just add a class to all of them and toggle based on the class. So I hope this code prevents you from having the same “brain fart” moment I did.

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
<script language="Javascript">

$(document).ready(function() {
 /* they've entered/removed something into my input field, toggle the radio buttons */
 $("#myInputValue").keyup( function() { toggleDisableFields($('#myInputValue').val())});
});

function toggleDisabledRadioButtons(value)
{
 /* check to see if they've entered a value in the field you want to toggle against */
 if (value) {
         $('.radioButtonClass').attr('disabled','disabled');
 }
 /* the radio buttons shouldn't be disabled yet */
 else {
         $('.radioButtonClass').attr('disabled','');
 }
}
</script>
</head>
  <body>
     <input type="text" id="myInputValue" /><br/>
     Enter something to disable my radio buttons, clear it out to use them again
     <br/><br/>
     Radio One: <input type="radio" name="myoption" class="radioButtonClass" value="1" /><br/>
     Radio Two: <input type="radio" name="myoption" class="radioButtonClass" value="2" /><br/>
     Radio Three: <input type="radio" name="myoption" class="radioButtonClass" value="3" /><br/>
     Radio Exception: <input type="radio" name="myoption" value="4" /><br/>
     (leave off the class and it won't toggle with the rest)
  </body>
</html>