AngularJS Directive That Invokes ASP.NET MVC Action
Recently one of the readers asked whether an AngularJS directive can invoke
ASP.NET MVC actions. This article shows how a simple AngularJS directive can be
created that invokes an ASP.NET MVC action using Ajax.
Usually an AngularJS directive is used to perform some UI centric operation.
However, nothing prevents an AngularJS directive from invoking some server side
code (say an action method) to get some job done. There are two approaches to
developing such a directive:
- The directive can directly talk to the server using $http service.
- The directive can talk to a custom service that in turn makes an Ajax
call using $http.
If the directive is quite simple you may go by the first approach. However,
this will mix the UI related concerns and the server communication related
concerns in the directive code. So, for real-world application isolating server
communication in a service and then consuming that service from a directive
would be more appropriate. This article shows you both the approaches.
Let's assume that you wish to create a custom AngularJS directive that
displays server side date and time stamp in the browser. You wish to use the
custom directive in your markup as follows:
<server-time-stamp></server-time-stamp>
Once the page is loaded, the directive code gets executed and server side
date and time stamp is displayed inside the above markup element.
Begin by creating a new ASP.NET MVC application using Visual Studio. Add
HomeController and Index view to the application as usual. Then add the
following action to the Home controller.
public ActionResult GetServerTimeStamp()
{
return Json(DateTime.Now.ToString(
"yyyy-MM-dd hh:mm:ss"),
JsonRequestBehavior.AllowGet);
}
The GetServerTimeStamp() action is quite straightforward. It simply returns
current date and time in ISO format. Notice the use of Json() method to convert
the date-time stamp into JSON format.
The custom directive will invoke this action through Ajax. Then add a
<script> reference to the AngularJS in the Index view and also add an empty
<script> block.
<script src="~/Scripts/angular-1.5.js"></script>
<script>
<script>
Then define a new module using AngularJS module API as shown below:
var app = angular.module("MyApp", []);
The above line of code creates a module named MyApp. Then create a custom
directive inside the MyApp module. This is done as follows:
app.directive("serverTimeStamp", function ($http) {
var obj = {};
obj.restrict = "E";
obj.link = function ($scope, element, attrs) {
var promise = $http.get("/home/getservertimestamp")
.success(function (timestamp)
{ element.append("<h1>" + timestamp + "</h1>"); })
.error(function () { alert('Error'); });
};
return obj;
});
The above code creates a directive named serverTimeStamp. Note that the
serverTimeStamp is used in the markup as <server-time-stamp>. The
serverTimeStamp directive receives the $http service for making Ajax calls.
An empty object - obj - is then created to store the directive's definition.
The restrict property indicates that the directive can be used only as an
Element (E).
The link property points to a function that gets invoked during the link
phase. This function is responsible for making an Ajax call to the
GetServerTimeStamp() action you created earlier. The link function calls the
get() method on $http to make a GET request to the /home/GetServerTimeStamp
action. The success callback function receives the server side date-time stamp
as its parameter and appends it to the directive element (<server-time-stamp>).
Note that element parameter of the link function gives the reference of the DOM
element representing the directive. The error callback simply displays the error
message (if any) in an alert dialog.
The object containing the directive's definition is then returned from the
directive function.
Now, add the following markup to the Index view.
<html ng-app="MyApp">
...
...
<body>
<server-time-stamp></server-time-stamp>
</body>
</html>
The <html> tag has ng-app attribute that specifies the module's name - MyApp.
The body of the document contains the directive's markup as discussed earlier.
If you run the application, you should see a timestamp in the browser as
shown below:
If you observe the DOM using F12 tools you will see something like this:
Ok. Now let's move ahead and isolate the Ajax communication in a
service. Our custom directive will then invoke this service.
Add the MyService in the <script> block as shown below.
app.service("MyService", function ($http) {
this.GetTimeStamp = function (callback) {
var promise = $http.get(
"/home/getservertimestamp")
.success(function (timestamp)
{ callback(timestamp); })
.error(function ()
{ alert('Error'); });
};
});
The above code creates a service named MyService. The service receives $http
service to make Ajax calls. Inside, the MyService defines a single function -
GetTimeStamp() - that will be called by the clients (directive in this case).
The GetTimeStamp() function accepts a callback function. This callback is
invoked upon successfully receiving the server side date-time. The code then
invokes /home/GetServerTimeStamp action as before. This time the success
callback simply invokes the callback received as the parameter.
Next, modify the serverTimeStamp directive as shown below:
app.directive("serverTimeStamp", function (MyService) {
var obj = {};
obj.restrict = "E";
obj.link = function ($scope, element, attrs) {
MyService.GetTimeStamp(
function (timestamp)
{
element.append("<h1>" + timestamp + "</h1>");
});
};
return obj;
});
Notice that the serverTimeStamp directive no longer accepts $http parameter.
Instead, it accepts MyService as the parameter. Inside, the link function now
invokes the GetTimeStamp() method on the MyService. The callback function
appends the server date-time stamp to the directive element as before.
If you run the application you should get similar results as in the previous
case.
Keep coding !!