How to make an email web app using Angular (Part III)

In the last post we created a popup that allowed us to see the contents of the emails on our email list. We introduced the ng-show and ng-click, data bound to functions, and passed parameters to those functions. As promised, in this post we will add another popup, this time to compose an email. This will be our first time using input fields.

We will start the same way we did for previous posts, by writing the HTML then adding angular code to make it functional. For our compose email popup we will need 3 fields and 2 buttons. One field for the To address, another for Subject, and one for the body of the email. One button to send and another to cancel. Our HTML looks like this:

<div class="modal">
    <div class="modal-header">
        <button type="button" class="close"">×</button>
        <h3>Compose Email</h3>
    </div>
    <div class="modal-body">
        <form>
            <input type="text" placeholder="To" style="width:95%;"><br />
            <input type="text" placeholder="Subject" style="width:95%;"><br />
            <textarea style="width:95%;" rows="10"></textarea>
        </form>
    </div>
    <div class="modal-footer">
        <a href="#" class="btn">Close</a>
        <a href="#" class="btn btn-primary">Send</a>
    </div>
</div>

The output looks like this: http://jsfiddle.net/luisperezphd/LAG2M/

compose-email-popup
Like in the previous post we want to add a variable and some functions to control the visibility. Since we covered how to do this already I won’t dive into it here. But you can see the code in the JSFiddle. In summary I did the following:

  • Add the variable isComposePopupVisible to the controller
  • Create the function showComposePopup() that sets it to true
  • Create the function closeComposePopup() that sets it to false
  • Added ng-show="isComposePopupVisible" to the modal
  • Added ng-click="closeComposePopup()" to the × and the “Close” button
  • Added a compose button under the email list: <button class="btn btn-primary">Compose</button>
  • Added ng-click="showComposePopup()" to the button

http://jsfiddle.net/luisperezphd/fYVCL/

Now we want to capture the information that the user enters, in our controller so we’re going to bind the textboxes to the fields of an object. Similar to what we did with the email popup and the selectedEmail variable.

The difference is that with the email popup we used expressions, expressions great for rendering data. In this case we are going to use the ng-model directive because it’s a two-way binding. By that I mean that if the user types in something into the input field we want the variable to automatically be updated. Likewise if the controller changes the value of one of the variables then we want the inputs to automatically updated to reflect that.

Let’s get to it. Add the composeEmail variable to the controller as an empty object, like so:

$scope.composeEmail = {};

Then update the HTML to bind to fields in this object, by adding ng-model="composeEmail.to" and ng-model="composeEmail.body" to the <input>‘s and <textarea> respectively. So it looks something like this:

<input type="text" placeholder="To" ng-model="composeEmail.to">
<input type="text" placeholder="To" ng-model="composeEmail.subject">
<textarea rows="10" ng-model="composeEmail.body"></textarea>

Now let’s confirm this is actually binding by using displaying an alert box when the user clicks the “Send” button. So let’s create a function in our controller, bind it to the “Send” button and have it display the values in the composeEmail variable. Your function should look like this:

$scope.sendEmail = function() {
    alert($scope.composeEmail.to 
        + " " + $scope.composeEmail.subject
        + " " + $scope.composeEmail.body);
};

Now let’s bind the “Send” button to it like so:

<a href="#" class="btn btn-primary" ng-click="sendEmail()">Send</a>

Now you can test it by clicking the “Compose” button, entering some information into the “To” and “Body” fields on the form and clicking “Send”. You should see whatever you typed come up in the popup.

http://jsfiddle.net/luisperezphd/9C3NX/

Now let’s add a couple of things. The first thing we want to do is make the popup disappear. To do that let’s just add $scope.isComposePopupVisible = false; in sendEmail(). Also you will notice that if you click the “Compose” button again it still has the information you entered last time. Let’s clear this by assigning an empty object to the composeEmail in the showComposePopup() function. When you are done your functions should look like this:

$scope.sendEmail = function() {
    $scope.isComposePopupVisible = false;
    alert($scope.composeEmail.to
        + " " + $scope.composeEmail.subject
        + " " + $scope.composeEmail.body);
};

$scope.showComposePopup = function() {
    $scope.composeEmail = {};
    $scope.isComposePopupVisible = true;
};

http://jsfiddle.net/luisperezphd/35zL9/

Adding Tabs

So now let’s do something a little more interesting than displaying an alert box. Normally the emails you send go into your “Sent” folder. Right now we only have the equivalent of an inbox. First let’s add a “Sent” folder and toggle between it and the “Inbox” using tabs.

Let’s start with the HTML. Add the following HTML to the top of your container:

<ul class="nav nav-tabs">
    <li><a>Inbox</a></li>
    <li><a>Sent</a></li>
</ul>

It should look like this: http://jsfiddle.net/luisperezphd/9QxLp/

email-tabs
Like everything else we’ve “angularized” we need a variable to keep track of which tab we’re in. Let’s call it activeTab, and by default let’s set it to “inbox”.

$scope.activeTab = "inbox";

Now let’s change this variable when the user clicks on “Inbox” or “Sent”. We can do that right inside ng-click, like this ng-click="activeTab='inbox'". So your HTML looks like this:

<ul class="nav nav-tabs">
    <li><a ng-click="activeTab='inbox'">Inbox</a></li>
    <li><a ng-click="activeTab='sent'">Sent</a></li>
</ul>

Now let’s only display the current email list when the activeTab is “inbox” by using the ng-show directive like so:

ng-show="activeTab=='inbox'"

So far so good. Now when you click on the “Sent” tab the email list disappears and when you click on “Inbox” it reappears: http://jsfiddle.net/luisperezphd/68fNh/

Problem is you can’t tell which tab is selected. Bootstrap has a way to show the selected tab, it requires adding the active class to the selected tab’s <li> element. See http://getbootstrap.com/2.3.2/javascript.html#tabs

So we need a way to add this class but only when the activeTab variable is set to the corresponding value. So the <li> for “Inbox” should have an active class only when activeTab is set to “inbox”. For this we use the ng-class directive. This directive takes an object, for each property in that object if the value is true it adds a class by that name.

For example in the following code angular adds the active CSS class to the <li> element:

<li ng-class="{active: true}">Inbox</li>

The result is:

<li class="active">Inbox</li>

If the value were false, the class in this case would be empty.

In our case we want to replace true with the appropriate condition like so:

<li ng-class="{active: activeTab == 'inbox'}">Inbox</li>

Your final code should look something like this:

<ul class="nav nav-tabs">
    <li ng-class="{active: activeTab == 'inbox'}">
        <a ng-click="activeTab='inbox'">Inbox</a>
    </li>
    <li ng-class="{active: activeTab == 'sent'}">
        <a ng-click="activeTab='sent'">Sent</a>
    </li>
</ul> 

And should look like this: http://jsfiddle.net/luisperezphd/KvLc2/

email-tabs-selected
Now the only thing left is to display the sent emails in the “Sent” tab. To do that we essentially need to copy the what we did for the inbox:

  • Create a variable in the controller called sentEmails assign it an empty array
  • Copy the HTML table for the email list
  • Change the ng-repeat to ng-repeat="email in sentEmails"
  • Change the ng-show condition to ng-show="activeTab=='sent'"
  • Change {{ email.from }} to {{ email.to }}

http://jsfiddle.net/luisperezphd/zvHSA/

You can do a quick test by adding objects to the sentEmails array and confirm that it shows up.

Now what we want to do is add the sent email to this array. This part is purely JavaScript, we just use JavaScript’s Array.push() function to add the composeEmail to the sentEmails array. Like so:

$scope.sentEmails.push($scope.composeEmail);

Also we can use this opportunity to remove the alert, since we can how see sent emails in the “Sent” tab. Your sendEmail() function should look like this:

$scope.sendEmail = function() {
    $scope.isComposePopupVisible = false;
    $scope.sentEmails.push($scope.composeEmail);
};

http://jsfiddle.net/luisperezphd/n3tLs/

Touch Ups

Everything should be working, there are a couple of things that we can touch up. For example after you send an email and go to the “Sent” tab you will see the date column is blank. This is because the date field is not bound to anything on the form so it’s not populated. This is as it should be because the date should be the date you sent the email. Normally this would be done on the server, for our purposes though we’ll do this on the sendEmail() function. You can simply assign the date property directly like so:

$scope.composeEmail.date = new Date();

Now if you test this by sending an email you will notice a problem right away. The date looks something like this “2013-12-27T21:47:01.678Z”. I don’t like that, I want it to look like the dates in the inbox, but those dates were strings, not actual JavaScript date objects.

http://jsfiddle.net/luisperezphd/237JF/

Luckily angular provides us with a way to format our expressions using something called “filters”. In our case we can fix how the date is displayed by changing this {{ email.date }} to this {{ email.date | date:'MMM d' }}. If you notice what we did was add | date:'MMM d' to the end of the existing expression.

The vertical line, known as a pipe (|), is how you tell angular to run a filter. The name of filter in this case is date, the colon (:) separates the filter from the parameter, and the string that follows tells the date filter how to format it. For the different ways you can format the date see the Angular documentation: http://docs.angularjs.org/api/ng.filter:date
http://jsfiddle.net/luisperezphd/UKYnz/

Alright we’re almost done with this post, just a few more things to cleanup. For example if you send an email and then click on it to see it’s content, the date on the popup is still ugly – missed a spot. The “From” field is blank because as in the case with the date before it was never assigned. Finally there is no “To” field. These are all issues we dealt with before and we know the solution to all of them.

  • Format the date using the filter by appending | date:'MMM d' at the end of the expression
  • Assigned $scope.composeEmail.from the value “me” in the sendEmail() function
  • Add “To” field and expression to the HTML
  • Assign “To” values to the sample emails in the controller

http://jsfiddle.net/luisperezphd/6gE45/

In the next post we are going to work on some of the remaining functionality to round out the application, like forwarding and replying to emails.

How to make an email web app using Angular (Part IV)

Comments

  1. Anonymous says

    Hi, can you tell me how to send an email using JS / gJS?
    I have created a registration form and I want to send an welcome email upon user registration

Leave a Reply

Your email address will not be published. Required fields are marked *