Introduction
We are going to create an email application using Angular JS. First, we're going to build the front end using AngularJS and HTML, at the end we’re going to add in the server portion.
We're going to learn angular by diving right in and explaining concepts as we go as we have to. Since we’re not doing the server portion until the end, we will need to mock that data right in the code. I think this is not only a good way to learn, it's a good way to go about building an application.
To start with, we need our boilerplate HTML. This HTML will include all the necessary libraries. We are going to use Bootstrap for our look and feel and of course Angular for our guts.
Here is what that code looks like:
<!DOCTYPE html>
<html>
<head>
<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css"/>
</head>
<body>
<div class="container">
</div>
</body>
</html>
To start with, we are going to focus on functionality related to managing emails, this means we are not going to address things like user sign in, at least not right now.
The first thing we are going to tackle is the email list. From this screen, we should be able to see who the email is from, what the subject of the email is, and when we received it.
First, we are going to write the HTML for this table, then we are going to use angular to generate it dynamically. Here is the HTML for the static version:
<table class="table table-bordered table-condensed">
<tbody>
<tr>
<td>Guinan</td>
<td>It's an Earth drink. Prune juice.</td>
<td>Dec 24</td>
</tr>
<tr>
<td>Beverly Crusher</td>
<td>Captain always push themselves too hard.</td>
<td>Dec 23</td>
</tr>
<tr>
<td>Mom</td>
<td>Don't forget to wear clean underwear today.</td>
<td>Nov 22</td>
</tr>
</tbody>
</table>
The classes on the table
are bootstrap classes that make the table look nice. This is what it looks this:
We're going to take a very incremental step by step approach to build this table using angular in order to introduce several concepts in detail. First, we are going to try to use an angular expression to replace one of the pieces of data. Replace Guinan
with {{ "Guinan Test" }}
. This should cause Guinan Test
to be displayed instead of Guinan
.
But that’s not what happens. Instead this happens:
This is because AngularJS does not automatically apply itself. You have to tell Angular what portions of the page to apply itself on. You do that by adding the ng-app attribute, you can add that attribute at any level, let’s add it to the container <div>
tag and see what happens.
Your HTML tag should look like this:
<div class="container" ng-app>
When you refresh, you should see the value inside the brackets:
Expressions allow you to embed JavaScript right into your HTML. (Technically it’s not JavaScript - but that’s a topic for another time). In the real world, there would be no point in using an expression just to display a string
, I’m doing it here in order to introduce the concept.
Basically, angular runs the code inside the double curly brackets and renders the result. If for example, you replaced that expression with:
{{ "Guinan" + " " + "Smith" }}
If you replaced the expression with:
{{ 10 * 3 }}
You would get:
Again these aren’t real world examples, there would be no point in using an express that multiplies 10 times 3 since we already know the answer. The real fun is when we start using expressions with data.
So let’s continue making this table data driven. Now we already used the ng-app
attribute in order for angular to know what portion of the HTML to process. Angular calls these directives. Now we are going to use the ng-init
attribute to initialize some data, then update one of the rows to use that data.
We can use this attribute anywhere. I’m going to put it in <tbody>
tag. So my <tbody>
tag looks like this:
<tbody ng-init="email={from: 'John', subject: 'I love angular', date: 'Jan 1'}">
Now I’m going to update the first row in my table to use that data. From this:
<tr>
<td>{{ 10 * 3 }}</td>
<td>It's an Earth drink. Prune juice.</td>
<td>Dec 24</td>
</tr>
To this:
<tr>
<td>{{ email.from }}</td>
<td>{{ email.subject }}</td>
<td>{{ email.date }}</td>
</tr>
And when I run it, I get this:
Now instead of one email, let’s do an array of emails, then display one row for each email. To do that, we will have to change our “ng-init
” so that it creates an array, then use another Angular directive to repeat the HTML row for each email. To do that, we use another directive called “ng-repeat
”.
So change the ng-init
to:
<tbody ng-init="
emails = [
{ from: 'John', subject: 'I love angular', date: 'Jan 1' },
{ from: 'Jack', subject: 'Angular and I are just friends', date: 'Feb 15' },
{ from: 'Ember', subject: 'I hate you Angular!', date: 'Dec 8' }
]
">
Then add the following ng-repeat
attribute and value to the <tr>
tag:
ng-repeat="email in emails"
So that it looks like this:
<tr ng-repeat="email in emails">
When you run it, you should get this:
Now we have a dynamically generated table. The problem is that the data is all in the HTML, what good is that, let's fix that.
Let’s get the code that initializes the emails array out of the HTML to where it belongs in the JavaScript. To do this, we will have to create a special function that Angular calls a controller. Then, we will use the ng-controller
directive to associate that controller with a portion of the HTML - similar to what we did with ng-app
.
So add the following code to your JavaScript:
function EmailController($scope) {
$scope.emails = [
{ from: 'John', subject: 'I love angular', date: 'Jan 1' },
{ from: 'Jack', subject: 'Angular and I are just friends', date: 'Feb 15' },
{ from: 'Ember', subject: 'I hate you Angular!', date: 'Dec 8' }
];
}
Then in your HTML, add the ng-controller
directive and assign it the value EmailController
. You can use this directive on any element, but only that element and its child elements can access the emails variable created in the controller, so it should be assigned to a parent of the <tr>
tag. I’m going to add it to the table
tag so that it looks like this:
<table ng-controller="EmailController" class="table table-border table-condensed">
If you run it, everything should look exactly the same. The only thing that changed is that instead of the emails being initialized inside the HTML, it’s now being initialized inside the controller function.
Did you notice the controller function has a parameter called $scope
? That’s how data is shared between a controller and HTML. Any variables created in the $scope
by the controller are accessible by the HTML, and vise versa.
Now we will add the ability to click on an email and see its content. We are going to use Bootstrap’s standard modal dialog HTML to create the email popup. To do that, add the following HTML below your table:
<div class="modal">
<div class="modal-header">
<button type="button" class="close">×</button>
<h3>Subject</h3>
</div>
<div class="modal-body">
<strong>From:</strong> Steve <br />
<strong>Date:</strong> Jan 2 <br />
<br />
<p>
Hey You, <br />
<br />
How you doing?<br />
<br />
Sincerely<br />
Your Bro
</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary">Close</a>
</div>
</div>
When you run this, you should see a popup overlapping your table that looks like this:
Now we only want this popup to appear when the user clicks on an email on the list. So first, we need a way to control its visibility. We do that by binding it to a variable, when the variable is true
the popup is visible, when it's false
it's not.
First let's create the variable in the controller, let's call it isPopupVisible
and let's set it to false
. Like so:
$scope.isPopupVisible = false;
Your controller should look something like this:
function EmailController($scope) {
$scope.isPopupVisible = false;
$scope.emails = [
{ from: 'John', subject: 'I love angular', date: 'Jan 1' },
{ from: 'Jack', subject: 'Angular and I are just friends', date: 'Feb 15' },
{ from: 'Ember', subject: 'I hate you Angular!', date: 'Dec 8' }
];
}
Now we add the ng-show
directive to the popup in order to bind it to that variable. To do that, add the following directive to your modal div
:
ng-show="isPopupVisible"
It should look like this:
<div class="modal" ng-show="isPopupVisible">
Now that we have bound the visibility of the popup to the isPopupVisible
variable, we need a way to set that variable to true
. So let's wire up that click
event to the email list. First, we’ll create a function that sets that variable to true
, let's call it showPopup()
, like so:
$scope.showPopup = function () {
$scope.isPopupVisible = true;
};
Your controller should now look like this:
function EmailController($scope) {
$scope.isPopupVisible = false;
$scope.showPopup = function () {
$scope.isPopupVisible = true;
};
$scope.emails = [
{ from: 'John', subject: 'I love angular', date: 'Jan 1' },
{ from: 'Jack', subject: 'Angular and I are just friends', date: 'Feb 15' },
{ from: 'Ember', subject: 'I hate you Angular!', date: 'Dec 8' }
];
}
Since each email corresponds to a row, we’ll do it at the row level. We'll use Angular's ng-click
directive to call this function from each row, like so:
ng-click="showPopup()"
So that your <tr>
tag looks like this:
<tr ng-repeat="email in emails" ng-click="showPopup()">
Now before this will work, we will have to do one more thing. If you remember from above ng-controller
applies a controller to the element its on and child elements. Last time we placed it on the <table>
, but since the modal HTML is outside that table we need to move this directive one level up to the container. Your container <div>
should look like this:
<div class="container" ng-app ng-controller="EmailController">
Now you have a popup that appears when you click on an email. You will probably notice right away that there is no way to close it. Let's create another function called closePopup()
that sets isPopupVisible
to false
. Then let's add an ng-click
directive to the close button, the ×
, and have the close button call that function.
Great, now we have a popup that opens and closes. Now let's get it to display the contents of the email. To accomplish that, we are going to have to modify showPopup()
to include a parameter for us to specify the email we want to display. Then, we need to store that email in a variable and bind it to the popup. Finally, we need to pass the email to the function when the user clicks on the list.
So modify the function to look like this:
$scope.showPopup = function (email) {
$scope.isPopupVisible = true;
$scope.selectedEmail = email;
};
Then modify the markup for your modal to use the data from selectedEmail
. It should change from this:
<div class="modal" ng-show="isPopupVisible">
<div class="modal-header">
<button type="button" class="close" ng-click="closePopup()">×</button>
<h3>Subject</h3>
</div>
<div class="modal-body">
<strong>From:</strong> Steve <br />
<strong>Date:</strong> Jan 2 <br />
<br />
<p>
Hey You, <br />
<br />
How you doing?<br />
<br />
Sincerely<br />
Your Bro
</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" ng-click="closePopup()">Close</a>
</div>
</div>
To this:
<div class="modal" ng-show="isPopupVisible">
<div class="modal-header">
<button type="button" class="close" ng-click="closePopup()">×</button>
<h3>{{selectedEmail.subject}}</h3>
</div>
<div class="modal-body">
<strong>From:</strong> {{selectedEmail.from}} <br />
<strong>Date:</strong> {{selectedEmail.date}} <br />
<br />
<p>
Hey You, <br />
<br />
How you doing?<br />
<br />
Sincerely<br />
Your Bro
</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" ng-click="closePopup()">Close</a>
</div>
</div>
Finally, change the call to showPopup()
in the table to pass the email. Like so:
ng-click="showPopup(email)"
Your <tr>
tag should look like this:
<tr ng-repeat="email in emails" ng-click="showPopup(email)">
Note that email
is the name we assigned in ng-repeat
. Now when you click on it, the popup will display the content of the email you clicked on.
The only thing we don't display is the email body. Let's do that now. Let's modify the array of emails to include an email body.
$scope.emails = [
{
from: 'John',
subject: 'I love angular',
date: 'Jan 1',
body: 'hello world!'
},
{
from: 'Jack',
subject: 'Angular and I are just friends',
date: 'Feb 15',
body: 'just kidding'
},
{
from: 'Ember',
subject: 'I hate you Angular!',
date: 'Dec 8',
body: 'wassup dude'
}
];
Now update the markup for the modal so that it displays that copy in the body of the modal.
Now we’re going to add functionality to compose an email. 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:
As we’ve done before, 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. In summary, we need to do 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
Now we want to capture the information that the user enters so we're going to bind the textboxes to the fields of an object. Similar to what we did with the email popup using the selectedEmail
variable.
The difference is that with the email popup we used expressions, expressions are 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.
To get started, 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 displaying an alert box with this information 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:
<br />
<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.
Now let's add a couple more things. The first thing we want to do is make the popup disappear. To do that, we’ll add $scope.isComposePopupVisible = false;
to sendEmail()
. Also, you will notice that if you click the "Compose" button again instead of it being empty, it has the information you entered the last time. Let's correct 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;
};
So now let's do something a little more interesting than displaying an alert box. Normally emails you send out 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:
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.
Problem is you can't tell which tab is selected. Bootstrap has a way to make a tab look selected, it requires adding the active CSS 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:
Now the only thing left is to display the sent emails in the "Sent" tab. To do that, we essentially need to copy 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 }}
You can do a quick test by adding objects to the sentEmails
array and confirm that they show up.
Now what we want to do now 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 now see sent emails in the "Sent" tab. YoursendEmail()
function should look like this:
$scope.sendEmail = function() {
$scope.isComposePopupVisible = false;
$scope.sentEmails.push($scope.composeEmail);
};
So far so good. There are just a couple of things 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 not something the user specifies. Normally, this would be done on the server, for our purposes though since we don’t have a server portion yet we'll do this in 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 another 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 string
s, not actual JavaScript date
objects.
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 the data through 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 just a few more things to cleanup. For example, if you send an email and then click on it to see its 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, it’s something we have to assign when it’s sent. Finally, there is no "To" field. The solutions to these are the same as the issues we dealt with before so we know the solution:
- 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
Now we are going to work on some of the remaining functionality to nicely round out the application, like forwarding and replying to emails. We are going to implement it by reusing the compose email functionality. I’ll go through it quickly since it’s pretty straight forward and we’re not introducing any new concepts.
First, we need to add the “Reply
” and “Forward
” buttons to the email details popup.
<a href="#" class="btn">Forward</a>
<a href="#" class="btn">Reply</a>
Should look like this:
Then, we create the reply
and forward
functions on the controller:
$scope.forward = function() {
};
$scope.reply = function() {
};
Then, we bind those functions to the buttons.
<a href="#" class="btn" ng-click="forward()">Forward</a>
<a href="#" class="btn" ng-click="reply()">Reply</a>
Now we need to do the following:
- Hide the view details popup
- Populate the
composeEmail
object - How it’s populated depends on whether it’s a reply or a forward
- Show the compose email popup
That’s it. Here is the commented reply()
function:
$scope.reply = function() {
$scope.isPopupVisible = false;
$scope.composeEmail = {};
angular.copy($scope.selectedEmail, $scope.composeEmail);
$scope.composeEmail.body =
"\n-------------------------------\n"
+ "from: " + $scope.composeEmail.from + "\n"
+ "sent: " + $scope.composeEmail.date + "\n"
+ "to: " + $scope.composeEmail.to + "\n"
+ "subject: " + $scope.composeEmail.subject + "\n"
+ $scope.composeEmail.body;
$scope.composeEmail.subject = "RE: " + $scope.composeEmail.subject;
$scope.composeEmail.to = $scope.composeEmail.from;
$scope.composeEmail.from = "me";
$scope.isComposePopupVisible = true;
};
When you click “Reply”, the compose window should look like this:
The forward()
is practically identical to this, the only difference is that the subject is prefixed with “FW:“ instead of “RE:” and to is blank instead of the sender.
If you run this now, you will notice 2 things. One, in the "Sent" tab, the emails are listed from top down in the order they were sent. We want to display this in the reverse order so the last email you sent shows up at the top. To do that, we can change the line of code that adds composeEmail
to the sentEmails
array. Instead of using push()
to add the email to the end of the array, we will use splice()
to add it to the beginning. Like so:
$scope.sentEmails.splice(0,0,$scope.composeEmail);
Also, if you open up one of the sent emails with all the new added text you will see that all the text runs into each other, no line breaks. We can fix this with a little CSS. We’ll use inline CSS just this once. We’ll wrap the body of the email around a <span>
tag and set the CSS white-space property to pre
. This tells the browser to honor line breaks. Like so:
<span style="white-space:pre">{{selectedEmail.body}}</span>
In terms of UI, our app is now fairly complete. But before it can be a real app, it has to be able to talk to a server to do the actual work of sending and storing emails. That’s what we’re going to do in this post.
Now the first thing I have to do is setup the server portion. I purposely left this post for last because I wanted you to be able to try out the Angular code without forcing you to setup a local development environment. Also, I wanted it to be useful for everyone no matter what server technology they used - PHP, .NET, Node.js, etc.
Obviously, I won’t be able to do past this point. After all, I can’t show you how to communicate with a server without a server, and that means I have to pick a technology. I’m going to use ASP.NET MVC. I will also do my best to describe the server portion in enough detail so that it’s also useful to you regardless of what server technology you use.
ASP.NET MVC Primer
A very quick primer for those not familiar with ASP.NET MVC. In this type of web application, URLs are mapped to classes and methods. For example, if a user entered the URL http://localhost/Home/Index in their web browser that would trigger the method Index()
in the class HomeController
to run.
That method can choose whether to return data or HTML. The HTML is stored in a separate file referred to as a view. These views have the extension .cshtml. Similar to the controller, the proper view is selected based on the URL, once again for the URL http://localhost/Home/View, it will use the view stored in the folder\Views\Home\Index.cshtml.
Of course I’m simplifying things, but this explanation should suit our purposes.
Back to the App
To start we need to create an ASP.NET MVC project in Visual Studio. I chose to start with an empty project and added a controller class called HomeController
. And an action method called Index
. In my view I include the CSS, JavaScript, and HTML I mentioned at the beginning.
Now let’s update the app so that we get the email list from the server instead of hardcoding it in the JavaScript. First, we’ll create the method on the ASP.NET controller:
public ActionResult GetEmails()
{
return Json(new[] {
new {
from = "Riker",
to = "me",
subject = "Nice to meet you, Pinocchio",
date = "Jan 8",
body = "That and helping Data finish 'Pop Goes the Weasel'. Also counts as a CMOH."
},
new {
from = "Picard",
to = "me",
subject = "I'm sorry, this is becoming a speech",
date = "Jan 7",
body = "But then I'm entitled, I'm the captain."
},
new {
from = "Data",
to = "me",
subject = "Could you please continue...",
date = "Jan 6",
body = "...the petty bickering? I find it most intriguing."
},
new {
from = "Troi",
to = "me",
subject = "But you spell knife with a 'k'",
date = "Jan 5",
body = "I spell 'knife' with an 'n', but then I never could spell."
},
});
}
In this example, we’re returning a static list of emails. We’re using the Json()
method to tell ASP.NET to return this data back to the browser as JSON.
Now, we’re going to replace the hardcoded list in our JavaScript with a call to this action. The JavaScript is in the view along with our HTML (/Views/Home/Index.cshtml). So replace this:
$scope.emails = [
{
from: 'John',
to: 'me',
subject: 'I love angular',
date: 'Jan 1',
body: 'hello world!'
},
{
from: 'Jack',
to: 'me',
subject: 'Angular and I are just friends',
date: 'Feb 15',
body: 'just kidding'
},
{
from: 'Ember',
to: 'me',
subject: 'I hate you Angular!',
date: 'Dec 8',
body: 'wassup dude'
}
];
With this:
$http.post("/Home/GetEmails").then(function(response) {
$scope.emails = response.data;
});
Finally, we need to add $http
as a parameter in our Angular controller, like we did with $scope
. So your controller method should be changed from this:
EmailController($scope)
to this:
EmailController($scope, $http)
That’s it, if you compile and run this, you should see your email list change.
One down and one to go. Sending email is going to be very similar, the main difference is that we didn’t pass any data to GetEmails()
but we need to pass information about the email we’re sending to SendEmail()
.
This method is going to be simple. All the server is going to do is populate the date and from fields of the email and send that information back to Angular. To accomplish this, we are going to need two things. We need to add an action to our ASP.NET controller and a C# class to hold the data Angular is sending.
Let’s start with the C# class. It should contain all the same fields found in the JavaScript code. For our purposes, you can put this class anywhere. In order to simplify things and keep all the code in one place, I’m adding it as a nested class inside the controller:
public class ComposeEmail
{
public string from { get; set; }
public string to { get; set; }
public string subject { get; set; }
public string date { get; set; }
public string body { get; set; }
}
Now for the ASP.NET action method itself:
public ActionResult SendEmail(ComposeEmail composeEmail)
{
composeEmail.from = "me";
composeEmail.date = DateTime.Now.ToString("MMM d");
return Json(composeEmail);
}
Finally, the call from Angular. Change the sendEmail()
method from this:
$scope.sendEmail = function () {
$scope.isComposePopupVisible = false;
$scope.composeEmail.from = "me";
$scope.composeEmail.date = new Date();
$scope.sentEmails.splice(0, 0, $scope.composeEmail);
};
To this:
$scope.sendEmail = function () {
$http.post("/Home/SendEmail", $scope.composeEmail).then(function (response) {
$scope.isComposePopupVisible = false;
$scope.composeEmail = response.data;
$scope.sentEmails.splice(0, 0, $scope.composeEmail);
});
};
That’s it! If you run your code now, send an email and check the “Sent” tab, you will see that the date is populated. If you click to open that email, you will see that the “from
” field is populated as well.
This covers the essentials of AngularJS and everything you need to start creating a simple functional web application. In this series, you created a fairly elaborate UI with multiple screens that update dynamically. To make it functional, you communicated with a backend server to do the work.
We’re just scratching the surface of what’s possible with AngularJS.