Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending $resource of angularjs using IResourceClass of typescript

I am developing SPA using Asp.Net Web API and AngularJS. I also use TypeScript to get static typing. So, I added DefinitelyTyped angularjs.

As I am using RESTfull services. I thought of using $resource of angularjs. now I $resource doesn't have any inbuilt method for PUT http method. So i decided to add my own as follows.

var employees = $resource('/api/employee/:id',{id:'@id'},{"update":{ method: "PUT", isArray:false }};

Now, as you can see, Its easy to do in normal AngularJS. I wanted to go through TypeScript route and define custom interface which extends IResourceClass . documentation of this interface explains like this.

// Baseclass for everyresource with default actions. // If you define your new actions for the resource, you will need // to extend this interface and typecast the ResourceClass to it.

I am really not able to make out how to extend this interface. It keeps coming up with some errors about syntax. Can some one explain how to extend this interface and add Update method which in turn calls PUT method on my controllers.

like image 885
Chintan Shah Avatar asked Feb 10 '14 18:02

Chintan Shah


2 Answers

First define your model, the interface that will describes your employee.

// Define an interface of the object you want to use, providing it's properties
interface IEmployee extends ng.resource.IResource<IEmployee>
{
    id: number;
    firstName : string;
    lastName : string;
}

Then create an interface that describes the resource you will create.

// Define your resource, adding the signature of the custom actions
interface IEmployeeResource extends ng.resource.IResourceClass<IEmployee>
{
    update(IEmployee) : IEmployee;
}

Create the EmployeeResource factory:

var myApp = angular.module('myApp', ['ngResource']).factory('EmployeeResource', 
    ['$resource', ($resource : ng.resource.IResourceService) : IEmployeeResource => {

        // Define your custom actions here as IActionDescriptor
        var updateAction : ng.resource.IActionDescriptor = {
            method: 'PUT',
            isArray: false
        };

        // Return the resource, include your custom actions
        return <IEmployeeResource> $resource('/api/employee/:id', { id: '@id' }, {
            update: updateAction
        });

    }]);

Inject your EmployeeResource into a controller:

myApp.controller('TestCtrl', ['$scope', 'EmployeeResource', ($scope, Employee : IEmployeeResource) => 
{
    // Get all employees
    var employees : Array<IEmployee> = Employee.query();

    // Get specific employee, and change their last name
    var employee : IEmployee = Employee.get({ id: 123 });
    employee.lastName = 'Smith';
    employee.$save();

    // Custom action
    var updatedEmployee : IEmployee = Employee.update({ id: 100, firstName: "John" });
}]);

Creating a new employee instance:

You can create an instance of type IEmployee by newing the EmployeeResource factory.

myApp.controller('TestCtrl', ['$scope', 'EmployeeResource', ($scope, Employee : IEmployeeResource) => 
{
    var myEmployee : IEmployee = new Employee({ firstName: "John", lastName: "Smith"});
    myEmployee.$save();
}

So in the case above we inject our IEmployeeResource as Employee. We can then new this object to create an IEmployee.

like image 106
Scott Avatar answered Oct 22 '22 19:10

Scott


I totally agree with Scott's answer: https://stackoverflow.com/a/21786326/2677975

I do have one addition: In my project, I've create the class ServiceModel<T>, which implements the interface IActionDescriptor.

module Common.ApiService {
    export class HttpHeaders {
        'Content-Type': string;
        Accept: string;
    }

    export class ServiceModel<T> implements ng.resource.IActionDescriptor {
        public method: string = 'GET';
        public params: T;
        public isArray: boolean = false;
        public url: string;
        public headers: HttpHeaders = new HttpHeaders()
    } 
}

Then, I can setup the method in my controller, just as Scott did, but now, the IDE will have type completion on the headers (and the params later on)

export interface IProfileServiceResource extends ng.resource.IResourceClass<IProfileDataModelDef> {
    SaveProfileImage(profileImageUri: Profile.Model.UserProfileModel): ng.resource.IResource<Array<any>>;
}

export class ProfileApiService 
    implements Common.ApiService.IApiServiceBase<IProfileServiceResource> {

    public SaveProfileImage: Common.ApiService.ServiceModel<Profile.Model.UserProfileModel>;

    constructor() {    
        this.SaveProfileImage = new Common.ApiService.ServiceModel<Profile.Model.UserProfileModel>();
        this.SaveProfileImage.method = "POST";
        this.SaveProfileImage.url = "/api/Profile/SaveProfileImage";
        this.SaveProfileImage.headers["Content-Type"] = 'application/json';
}

I made this as an additional answer, since it only serves to provide types for the headers and the params.

like image 1
Belicosus Avatar answered Oct 22 '22 18:10

Belicosus