CRUD Operations with $resource - CodeProject

:

Introduction

In this tip, we will see how to perform CRUD operations with $resource angular service. We will be using localdb to store our data and perform CRUD operations, and retrieve data using web API. In this tip, we are not going to see about web API or code first EF, we will be focusing completely on $resource service.

You can clone the code from my github repository at <a href="https://github.com/Sudheer-Reddy/AngularWithDNet.git">https://github.com/Sudheer-Reddy/AngularWithDNet.git</a>.

Background

CRUD operations are mapped to the HTTP protocols in the following manner:

  • GET: This maps to the R(Retrieve) part of the CRUD operation. This will be used to retrieve the required data (representation of data) from the remote resource.
  • PUT: This maps to the U(Update) part of the CRUD operation. This protocol will update the current representation of the data on the remote server.
  • POST: This maps to the C(Create) part of the CRUD operation. This will create a new entry for the current data that is being sent to the server.
  • DELETE: This maps to the D(Delete) part of the CRUD operation. This will delete the specified data from the remote server.

$resource is a factory which creates a resource object which lets you interact with RESTFul server side data sources. You can find great documentation here. The object has some action methods which let you perform CRUD operations. 

Usage

Note: $resource service is in a separate module ngResource, so you need to include anglular.resource.js and add ngResource module to your angular module dependencies.

$resource(url,{paramDefaults},{actions},{options}); //url is required parameter.
  • url: The webservice or web API url to perform CRUD operations.
  • paramDefaults: The defaults for url params.
  • actions: The action methods which map to CRUD operations.
  • options: It supports only one option stripTrailingSlashes which is used to remove the trailing slashes from the url.

Example

var students = $resource("api/StudentsApi");

On returned object, you will get the following action methods to perform CRUD operations:

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

or you can have your own action methods as follows:

return $resource("api/StudentsApi", {}, {
        query: { method: "GET", isArray: true },
        create: { method: "POST" },
        get: { method: "GET", url: "/api/StudentsApi?id=:id" },
        remove: { method: "DELETE", url: "/api/StudentsApi?id=:id" },
        update: { method: "PUT", url: "/api/StudentsApi?id=:id" }
    });

and then, you can use them in your controller to perform CRUD operations: 

stDataService.query(); // Will return array of  students
stDataService.get({id:2}); // Returns student with id 2
stDataService.create(student); //Adds new student entry
stDataService.update({id:2},student); //Updates student 2 info
stDataService.remove({id:2}); //Removes student with id 2

Using the Code

Coming to the source code attached, in this section, we will walk through creating a factory method to return $resource object and how we can retrieve data before view is loaded using $routeProvider resolve property and update or delete info in controllers.

stDataService.js

studentsManagement.factory("stDataService", [
    "$resource",
       function($resource) {
          return $resource("api/StudentsApi", {}, {
            query: { method: "GET", isArray: true },
            create: { method: "POST" },
            get: { method: "GET", url: "/api/StudentsApi?id=:id" },
            remove: { method: "DELETE", url: "/api/StudentsApi?id=:id" },
            update: { method: "PUT", url: "/api/StudentsApi?id=:id" }
       });
    }
]);

One of the best practices is to retrieve data before your view loads so that there won't be any flickers on the page due to async calls. 

So let's create our module and use $routeProvider to add routing to have SPA effect to your application.

main.js

var studentsManagement = angular.module("studentsManagement", 
	["ngResource", "ngCookies", "ngRoute"]).run(function ($rootScope) {
    $rootScope.title = "Home";
})
    .config([
        "$routeProvider", "$locationProvider", function ($routeProvider, $locationProvider) {
            $locationProvider.html5Mode({
                enabled: true,
                requireBase: false
            });
            $routeProvider.when("/", {
                templateUrl: "/templates/Students.html",
                controller: "stsController",
                <code>resolve</code>: {
                    students: function ($q, stDataService) {
                        var deferred = $q.defer();
                        stDataService.query(function (data) {
                            deferred.resolve(data);
                        });

                        return deferred.promise;
                    }
                }
            }).when("/Student/:id", {
                templateUrl: "/templates/StudentInfo.html",
                controller: "stController",
                <code>resolve</code>: {
                    student: function ($q, stDataService, $route) {
                        var deferred = $q.defer();
                        var id = $route.current.params.id;
                        stDataService.get({ id: id }, function (data) {
                            deferred.resolve(data);
                        });

                        return deferred.promise;
                    }
                }
            }).when("/Create", {
                templateUrl: "/templates/CreateStudent.html",
                controller: "stCreateController"
            });
        }
    ]);

Observe the resolve property above the highlighted text. If any of these dependencies of resolve properties are promises, the router will wait for all of them to be resolved or one to be rejected before the controller is instantiated. You can see above I haven't used stDataService.query(); but added a promise to get results asynchronously. It's not required, you can simply use students : stDataService.query() .

Now let's have our controllers ready to bind scope to the view.

stsController.js:(Dashboard Page)

studentsManagement.controller("stsController", [
"$scope", "$route", "$rootScope", 
"stDataService", function($scope, $route, $rootScope, stDataService) {
        $rootScope.title = "Students";
        $scope.students = $route.current.locals.students;
        $scope.removeStudent = function(id, student) {
            stDataService.remove({ id: id }).$promise.then(function() {
                var index = $scope.students.indexOf(student);
                $scope.students.splice(index, 1);
                alertify.log(student.Name + " is removed");
            });
        };
    }
]);

$route service is used to access the route params, locals properties that we have set in routing.

The resolve property students is accessed using $route.current.locals.students.

stController.js:(Edit page)

studentsManagement.controller("stController", [
"$scope", "$route","$rootScope","stDataService","$location", 
	function ($scope, $route,$rootScope,stDataService,$location) {
        $scope.student = $route.current.locals.student;
        $rootScope.title = "Student Info -" + $scope.student.Name;
        $scope.updateInfo = function(student) {
            stDataService.update({ id: student.Id }, student).$promise.then(function() {
                $location.url("/");
                alertify.log("Updated Info");
            });
        };
    }
]);

stCreateController.js:(Create page)

studentsManagement.controller("stCreateController", [
"$scope", "stDataService", "$rootScope","$location", 
	function($scope, stDataService, $rootScope,$location) {
        $rootScope.title = "Create student";
        $scope.saveStudent = function(student) {
            stDataService.create(student).$promise.then(function () {
                $location.url("/");
                $("#stsNav").toggleClass("active");
                $("#stCreate").removeClass("active");
                alertify.log(student.Name + " added successfully.");
            });
        };
    }
]);

I have used alertify.js to have some push notification kind of effect on the page to show messages after adding student, editing student and removing student.

Dash board page

Note: Add some entries to your localdb such that the dashboard page won't be empty for the first time or you can use the create student tab to add entries.

When you remove user, you get alertify style log message.

Thanks for reading.