However these SPA templates are rather opinionated on the usage of the underlying frameworks and may not be the best starting point if we are trying to understand the framework under the hood. So today, we will look at AngularJS in a plain vanilla ASP.NET MVC app. We’ll start with an empty project and go ground up.
What is Angular JS?
Angular JS is (another) client side MVC framework in JavaScript. It was created by a group of developers at Google when they realized that their project (Feedback) had become too unwieldy and needed a cleanup. The three weeks effort at that cleanup lay seed to what we now know as AngularJS. Of course AngularJS was thrown open to community and since then had garnered a pretty vibrant community and a boat load of features.Okay, enough talk when do we see code?
Well today we’ll take a small step towards learning AngularJS by learning how to do basic data binding and retrieving data via AJAX. We’ll do it the way we have done it around here – Using a code walkthrough.
Demo – The Premise
I could have done the code walkthrough without any server side component and some hardcoded client side data, but that’s too far away from the real world. So we’ll get some data from the ethereal stream of data that is Twitter and display it in our Application demonstrating the concept of model binding and directives in AngularJS.To connect to Twitter, we’ll use the excellent Linq2Twitter library on the server side. Once we have logged in we’ll retrieve and display data using AngularJS.
So first part of this article is setting up the infrastructure or plumbing to get LinqToTwitter hooked up. As with all Twitter apps, we have to get a CustomerKey and a CustomerSecret from developers.twitter.com.
Update: The second part of this article can be read at Using AngularJS Modules and Services Feature in an ASP.NET MVC application .
The third part of the article can be read at AngularJS – Post data using the $resource Service in an ASP.NET MVC app .
The Fourth part at Using Custom Directive in AngularJS to create reusable JavaScript components for your ASP.NET MVC app and
The fifth one at Angular JS: Routing to a SPA mode using ASP.NET MVC
Getting Started with ASP.NET MVC & AngularJS Plumbing
1. We create a MVC 4 project with the Empty template as we want to get started with the bare bones today.2. We add the LinqToTwitter from Nuget
PM> install-package linqtotwitter
3. We install Twitter Bootstrap CSS and JavaScript files using the following command
PM> install-package Twitter.Bootstrap
4. We add an Empty HomeController in the Controllers folder. The Index action method calls the Authorization function and if the current user is not Authorized, it redirects to Twitter’s Authorization page. Once Authorized, it navigates back to Index page.
Very briefly, the ConsumerKey and the ConsumerSecret that we got while creating Twitter app is in the AppSettings. These two along with the OAuth token returned by Twitter, complete the Authorization part.
Note: We have not taken any measure to hide our Auth token once it returns from the Server. IT should not be this unprotected in a production application.
public class HomeController : Controller
{
private IOAuthCredentials credentials = new SessionStateCredentials();
private MvcAuthorizer auth;
private TwitterContext twitterCtx;
public ActionResult Index()
{
var unAuthorized = Authorize();
if (unAuthorized == null)
{
return View("Index");
}
else
{
return unAuthorized;
}
}
private ActionResult Authorize()
{
if (credentials.ConsumerKey == null || credentials.ConsumerSecret == null)
{
credentials.ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
credentials.ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
}
auth = new MvcAuthorizer
{
Credentials = credentials
};
auth.CompleteAuthorization(Request.Url);
if (!auth.IsAuthorized)
{
Uri specialUri = new Uri(Request.Url.ToString());
return auth.BeginAuthorization(specialUri);
}
ViewBag.User = auth.Credentials.ScreenName;
return null;
}
5. Finally we add a method that returns a JsonResult containing list of Latest Tweets from the logged in user
[HttpGet]
public JsonResult GetTweets()
{
Authorize();
string screenName = ViewBag.User;
IEnumerable<TweetViewModel> friendTweets = new List<TweetViewModel>();
if (string.IsNullOrEmpty(screenName))
{
return Json(friendTweets, JsonRequestBehavior.AllowGet);
}
twitterCtx = new TwitterContext(auth);
friendTweets =
(from tweet in twitterCtx.Status
where tweet.Type == StatusType.Home &&
tweet.ScreenName == screenName &&
tweet.IncludeEntities == true
select new TweetViewModel
{
ImageUrl = tweet.User.ProfileImageUrl,
ScreenName = tweet.User.Identifier.ScreenName,
MediaUrl = GetTweetMediaUrl(tweet),
Tweet = tweet.Text
})
.ToList();
return Json(friendTweets, JsonRequestBehavior.AllowGet);
}
private string GetTweetMediaUrl(Status status)
{
if (status.Entities != null && status.Entities.MediaEntities.Count > 0)
{
return status.Entities.MediaEntities[0].MediaUrlHttps;
}
return "";
}
6. The ViewModel used to encapsulate a Tweet is as follows
public class TweetViewModel
{
public string ImageUrl { get; set; }
public string ScreenName { get; set; }
public string MediaUrl { get; set; }
public string Tweet { get; set; }
}
7. Next we setup the View skeleton using BootStrap:
a. Add _ViewStart.cshtml in the Views folder. It’s content is as follows
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
b. Add Views\Shared\_Layout.cshtml folder that will serve as our Master Page
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Hello AngularJS</title>
<link href="~/Content/bootstrap.css" rel="stylesheet" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="navbar navbar-inverse">
<div class="navbar-inner">
<div class="brand">
@ViewBag.Title
</div>
@if (ViewBag.User != null)
{
<div class="button-group pull-right">
<div class="btn">
<i class="icon-user"></i>
@ViewBag.User
</div>
</div>
}
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
@RenderBody()
</div>
</div>
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>
c. Finally we add Index.cshtml that for now has only the Title
@{
ViewBag.Title = "Hello AngularJS";
}
d. If we run the application at this point, it will ask user to Authorize our app with Twitter, and once authorized, we’ll be redirected to the home page as follows
That completes our ground work and we are ready to dive into AngularJS now.
Introducing AngularJS
Thanks to the community, AngularJS is easy to get started with, using Nuget.PM> install-package angularjs
Once installed we add a reference to it in _Layout.cshtml
<script src="~/Scripts/angular.js"></script>
The Angular App
First point of difference from libraries like KO is that Angular needs an ng-app attribute to declared wherever we want the Scope of Angular to start. We will add it in our Index.cshtml by adding a <div> as follows.<div ng-app>
<!-- More Goodness Coming -->
</div>
The AngularJS Client Side Controller and ViewModel
We will add a hello-angular.js in the Scripts folder. We start off with a Controller that is defined as follows:var indexController = function ($scope)
{
}
In Angular the indexController object is regarded as Controller simply if someone pulls in the $scope parameters.
What is $scope?
$scope is the client side version of ViewBag as we know from MVC as in it is used for sharing data between the Angular Controller and the View.
How to we use $scope?
1. As we mentioned above, we use Scope as a ViewBag, so to start off with we’ll add a couple of Json objects in it.
var indexController = function ($scope)
{
$scope.tweets = [
{ screenName: "Sumit", tweetText: "Test 1" },
{ screenName: "Sumit", tweetText: "Test 2" }];
}
Using Scope and Data Binding in AngularJS
1. Next we go back to Index.cshtml and add the following markup.<!-- More Goodness Coming -->
<div ng-controller="indexController">
<ul>
<li ng-repeat="item in tweets">
{{item.screenName}}, {{item.tweetText}}
</li>
</ul>
</div>
Angular Directives
There is a lot of stuff going on here.a. In the outermost div, we have specified the directive ng-controller=”indexController”. This specifies what is the scope of the $scope object.
b. Note the <ul> doesn’t have anything special, so data binding to collections is by default container less.
c. The <li> element finally defines the ng-repeat directive and sets it to tweets (as defined in $scope earlier). Note the syntax is like C#’s foreach.
d. Finally we have the {{ … }} handlebar syntax to define the placeholders.
Those who are familiar with KO’s data binding will note how Angular doesn’t use data- attributes rather it uses AngularJS Specific attributes that it modifies at runtime. If we run this app now, we’ll see the following:
We can see how the DOM has been manipulated at runtime to get the output.
Just to recap, the $scope that was passed into the controller was actually instantiated and passed by Angular, those familiar with constructor injection will realize that Angular actually injected the $scope without us having to worry about it. This is the uniqueness and strength of Angular.
The Pluralizer
Directives in AngularJS are special keywords that have built in functionality. They can apply to elements attributes, classes or comments. Let’s put in a directive to see the total number of tweets.In the view, we add the following markup inside the scope of the ng-controller
<h3>
<ng-pluralize count="tweets.length" when="newTweets"></ng-pluralize>
</h3>
The ng-pluralize directive can be read as ‘check for the count in tweets.length and based on it, evaluate the switch case in the newTweets function.’ Actually newTweets is an object in our indexController that we define as follows:
$scope.newTweets = {
0: "No new Tweets",
other: "{} new Tweets"
}
So now our ng-pluralize delegate reads like – “check of the count in tweets.length and if there are no tweets put the text “No new Tweets” else put the text ‘n new Tweets’ where n is the count”.
If we run the application as is, we’ll get the following
Nice. Now let’s move on and see how we can get data from the Server.
Promises and Fetching Data from Server
Angular makes it really easy to get data from the server using AJAX. We’ll update our client controller (hello-angular.js) as followsvar indexController = function ($scope, $http)
{
var resultPromise = $http.get("/Home/GetTweets");
resultPromise.success(function (data)
{
$scope.tweets = data;
});
$scope.newTweets = {
0: "No new Tweets",
other: "{} new Tweets"
}
}
Notice the additional parameter $http, getting injected into the controller. This is a wrapper for http functions as we can guess. We will use this to retrieve the Promise return by the $http.get(…) call. A promise is like a delegate or callback used when doing Async AJAX calls. Note we are calling the GetTweets method on our server controller. Once the Promise (delegate) completes successfully, we get the actual JSON data from the server.
Let’s update the Index.cshtml to create a nice list of the Tweets
<!-- More Goodness Coming -->
<div ng-controller="indexController">
<h3>
<ng-pluralize count="tweets.length" when="newTweets"></ng-pluralize>
</h3>
<table class="table table-striped">
<tr ng-repeat="item in tweets">
<td>
<img src="{{item.ImageUrl}}" />
</td>
<td>
<strong>{{item.ScreenName}}</strong> <br />
{{item.Tweet}}
</td>
</tr>
</table>
</div>
There are a couple of new things to note
a. We have moved the ng-repeat from the <li> to the <tr> tag.
b. We have added an <img> element and notice how the src is bound inplace to the ImageUrl property in our ViewMode.
Well with that we are all set. Run the Application and a view like the following comes up!
Pretty neat eh!
With that we conclude today’s post. In future we will pick apart more features of AngularJS and keep getting deeper.
Conclusion
Angular provides a very nice Framework for data-binding, Templating and implementing observable behavior. However we have barely scratched the surface.For devs familiar with data-binding in any form (no pun intended), be it WebForms, WPF, Silverlight etc., Angular’s binding philosophies match closely except for the fact that Angular’s magic happens on the browser.
Download the entire source code of this article (Github)
Tweet
7 comments:
Thanks for sharing, very helpful!
So, why are you using the server side controller to do a webservice call, when the client is more than capable of doing this?
Hi Dave,
If you are implying why I am calling Twitter from the server-side instead of client side, well I am treating Twitter as my data source, it could well have been a database call from the server, instead of calling Twitter.
Hope this clarifies.
-Sumit.
great app example using angular.js. I also use bootstrap. Def. will check your others posts.
xzczxcxzcxzcxzc
what does twitterConsumerKey and twitterConsumerSecret reference..as it is giving me unauthorized even after placing my twitter id and pwd respectively...
I'm using twitterConsumerKey and twitterConsumerSecret created on my Twitter App but it throws Unauthorized...
is it something more to configure on Twitter App??
Post a Comment