AngularJS – Post data using the $resource Service in an ASP.NET MVC app

Last time we looked at how we could use the $resource service to make our code more compact when making HTTP calls. Today we will see how to post data to a server using the $resource service.

We’ll continue using the code from the previous article. So you can download the zip from here or fork it from Github.

Posting Data

In our previous article we re-organized our application to use the $resource service. However we were only using the query function. As we know the resource function can also do GET, POST, PUT etc. Following is the map of actions to the HTTP verbs

{ 'get':    {method:'GET'},
  'save':   {method:'POST'},
  'query':  {method:'GET', isArray:true},
  'remove': {method:'DELETE'},
  'delete': {method:'DELETE'} };


(Source: Angular JS Documentation)

So to post data, we need to simply call the save function on our resource instance. However there is a slight gotcha. The $resource object expects all the functions to post to the same URL. In ASP.NET MVC terms, this means posting to the same action method with different HttpGet/HttpPost/HttpPut etc attributes.

In our current code currently, the HomeController uses GetTweets method to get the Data. Now Posting to a URL called GetTweets is semantically icky. So let’s update the Controller method to be called just Tweet. So we will do GET, POST, PUT etc. requests to the url /Home/Tweet.

updated-controller-method-name

Next we update our TwitterService to access the new URL

ngTwitter.factory("TwitterService", function ($resource)
{
    return {
        timeline: $resource("/Home/Tweet")
    }
});


Now if we run the Application, we should get the same UI where the latest 20 tweets are shown on screen.

Adding a Tweet Field

To send out Tweets we need an input box and a Tweet button. To do this we add the following markup to the Index.cshtml

<div>
<textarea ng-model="statusText" rows="5" />
<button class="btn btn-success" ng-click="sendStatus()">Tweet</button>
</div>


This adds a multiline textbox and a button to the top of the page. Note that we have added a view model element called statusText to contain the text or the status that we’ll be sending to Twitter, and a click handler sendStatus.

Adding the sendStatus method to the $scope

With our UI updated we can add the sendStatus() event handler to our $scope as follows

$scope.sendStatus = function ()
{
var tweetText = $scope.statusText;
var newTimeLine = new TwitterService.timeline(
  {
   tweet: tweetText
  });
newTimeLine.$save();
}


In the method, we are fetching the text from the statusText into tweetText variable. Next we are retrieving an instance of the timeline $resource and saving it in var newTimeLine

Note we are instantiating it with a JSON object with the status text in a property called tweet.

Finally we are calling newTimeLine.$save() method that will do an HTTP Post to a Tweet method in our Home Controller.

Adding a Tweet Method to handle Post

Our client is ready to post Data. Let’s complete our controller to accept it.

[HttpPost]
public JsonResult Tweet(string tweet)
{
Authorize();
twitterCtx = new TwitterContext(auth);
try
{
  Status stat = twitterCtx.UpdateStatus(tweet);
  if (stat != null)
  {
   return Json(new { success = true });
  }
  else
  {
   return Json(new { success = false, errorMessage = "Unknown Error" });
  }
}
catch (Exception ex)
{
  return Json(new { success = false, errorMessage= ex.Message });
}
}

As seen above, the method accepts a string parameter called tweet. Note that it is same as the name of JSON property we initialized our service with.

We check if our AuthToken is still valid via the Authorize() helper method, next we initialize the TwitterContext class provided by Linq2Twitter. Finally we call UpdateStatus method on the twitterContext to send the Tweet.

If all goes well, we get a non-null Status and send back a JSON with ‘success’ property set to true. If there are any exceptions, we handle the exception and send a success = false and an error message.

Updating client to notify Save Status

So far, we have not used the return data after Posting the Tweet. Let’s update our client to do that. You can pass a callback method to $resource.save(…) that will retrieve the data returned as well as the HTTP headers. Using the returned data’s success property, we can determine if the Tweet was sent successfully or not. We display an alert appropriately and if successful, we update the Text area to empty.

$scope.sendStatus = function ()
{
var tweetText = $scope.statusText;
var newTimeLine = new TwitterService.timeline(
{
  Tweet: tweetText
});
newTimeLine.$save(function (data, headers)
{
  if (data.success && data.success == true)
  {
   alert("Tweet Sent Successfully!");
   $scope.statusText = "";
  }
  else
  {
   alert("ERROR: " + data.errorMessage);
  }
});
}


That wraps up the code changes. Time to take it for a spin.

Demo

Step 1: On launch we are redirected to the Twitter page

demo-twitter-auth

Step 2: Once we authenticate and log, we’ll see the top 20 tweets for the account. Note that the account is not getting permissions to Post tweets. Without this of course we can send tweets through the API.

demo-tweets-loaded

Step 3: Now let’s try to send a Tweet longer than 140 characters.

demo-tweet-error

Boom! Error as expected.

Step 4: Now let’s try to send a legit tweet!

demo-tweet-success

Nice! Successfully sent! When you click OK the text area is cleared out.

Step 5: Hit refresh!

demo-tweet-posted

Bingo! There is our Tweet!

Conclusion

With that we complete this small step in Angular. I am deliberately going a little slow here. We were able to use the $resource service to post data. In our case, we posted it all the way up to Twitter. But instead of Twitter we could have easily posted it to a DB if we wanted to.

We will continue exploring AngularJS in future posts so stay tuned.

Customary Note of Caution: The Twitter Authentication method used here is NOT production ready!

Download the entire source code of this article (Github)




2 comments:

Unknown said...

Interesting article!
But, wouldn't it be better to use WebApi in this case? Especially if you are start to use the full set of verbs (get, post, delete)

Anonymous said...

Hi Lorenzo,

Web API would absolutely be a nice complement to Angular. In this particular case I was avoiding the plumbing required to manage the Auth Token from Twitter; Linq2Twitter does that for free in MVC.

Was trying to keep focus on Angular

Hope this helps,
- Sumit.