在TypeScript中扩展AngularJS $资源工厂

时间:2014-02-17 02:26:34

标签: angularjs typescript

我正在尝试使用DefinatelyTyped$resourceIResource和朋友)干净地将Angular自定义IResourceClass扩展名作为工厂编写为TypeScript类。

根据Misko Hevery资源只有constructor个函数,因此我希望能够将$resource定义为具有某些类型安全接口的常规类(INamedEntityResource或{ {1}})并混合服务定义但我似乎无法在我的NamedEntityResource原型上获得标准类方法以最终出现在工厂实例上。

有没有办法使用INamedEntity函数执行此操作,还是应该放弃并在纯JavaScript中定义服务?

constructor()

另外

所以这样做的目的是允许我编写一个资源类{},其方法将在我通过HTTP加载的每个资源上进行注释。在这种情况下,我的declare module EntityTypes { interface INamedEntity { } } module Services { export interface INamedEntitySvc { Name(params: {}, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity; Clear(params: {}, value: EntityTypes.INamedEntity, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity; } // WILL have correct interface definition for the resource export interface INamedEntityResource extends NamedEntityResource, INamedEntitySvc { } export class NamedEntityResource { // #1 DOESN'T WORK - These are on NamedEntityResource.prototype but don't end up on svc public someMethod() { } public someOtherMethod() { } constructor($resource) { var paramDefaults = { }; var svc: INamedEntitySvc = $resource(getUrl(), paramDefaults, { Name: <any>{ method: "GET", params: { action: "Name" } }, Clear: <any>{ method: "PATCH", params: { action: "Clear" }, headers: { 'Content-Type': 'application/json' } }, }); // THIS WORKS - but it's not a NamedEntityResource svc["prototype"].someMethod = function () { } svc["prototype"].someOtherMethod = function () { } return <any>svc; // #1 DOESN'T WORK THOUGH return; // doesn't pick up methods on prototype // #2 THIS DOESN'T WORK EITHER NamedEntityResource["prototype"] = angular.extend(this["prototype"] || {}, svc["prototype"]); return this; } } // Registration var servicesModule: ng.IModule = angular.module('npApp.services'); servicesModule.factory('NamedEntityResource', NamedEntityResource); } s。

这是迄今为止我能够获得的最接近的解决方案,看起来确实有效,但感觉非常糟糕。

INamedEntity

诀窍是;

  1. 将我想要的服务上的方法提升到基类中,因为this.prototype在我的构造函数()函数被调用时没有初始化。
  2. 返回module Services { export interface INamedEntitySvc { Name(params: {}, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity; Clear(params: {}, value: EntityTypes.INamedEntity, successCallback: (data: any, headers: any) => void, errorCallback: (data: any, headers: any) => void): EntityTypes.INamedEntity; } // WILL have correct interface definition for the resource export interface INamedEntityResource extends NamedEntityResource, INamedEntitySvc { } export class NamedEntityResourceBase { public someMethod() { } public someOtherMethod() { } } // extend our resource implementation so that INamedEntityResource will have all the relevant intelisense export class NamedEntityResource extends NamedEntityResourceBase { constructor($resource) { super(); // kind of superfluous since we're not actually using this instance but the compiler requires it var svc: INamedEntitySvc = $resource(getUrl(), { }, { Name: <any>{ method: "GET", params: { action: "Name" } }, Clear: <any>{ method: "PATCH", params: { action: "Clear" }, headers: { 'Content-Type': 'application/json' } }, }); // Mixin svc definition to ourself - we have to use a hoisted base class because this.prototype isn't setup yet angular.extend(svc["prototype"], NamedEntityResourceBase["prototype"]); // Return Angular's service (NOT this instance) mixed in with the methods we want from the base class return <any>svc; } thisWontWork() { // since we never actually get a NamedEntityResource instance, this method cannot be applied to anything. // any methods you want have to go in the base prototype } } // Registration var servicesModule: ng.IModule = angular.module('npApp.services'); servicesModule.factory('NamedEntityResource', NamedEntityResource); } 这是构造函数中的角度svc服务,当然,您可以在JavaScript中执行此操作,但感觉就像在TypeScript中真的很脏鸭子类型。
  3. 为了获取svc.prototype上的方法,我直接从我的基类扩展它。这特别令人讨厌,因为这意味着每次创建实例时都要设置原型。
  4. 这个sh **三明治最后的刺激性香气是我必须在构造函数上调用super(),因为我只是为了让它编译而丢弃。
  5. 但是,最后,我可以向$resource添加方法,它们将出现在从我的HTTP资源加载的所有实体的原型中。

3 个答案:

答案 0 :(得分:3)

我一直在寻找答案。它在打字稿文档中。接口可以扩展类。将方法添加到资源实例的解决方案如下:

class Project {
    id: number;
    title: string;

    someMethod(): boolean {
        return true;
    }
}

export interface IProject extends ng.resource.IResource<IProject>, Project {
    // here you add any method interface generated by the $resource
    // $thumb(): angular.IPromise<IProject>;
    // $thumb(params?: Object, success?: Function, error?: Function): angular.IPromise<IProject>;
    // $thumb(success: Function, error?: Function): angular.IPromise<IProject>;
}

export interface IProjectResourceClass extends ng.resource.IResourceClass<IProject> { }

function projectFactory($resource: ng.resource.IResourceService): IProjectResourceClass {
    var Resource = $resource<IProject>('/api/projects/:id/', { id: '@id' });

    // the key, for this to actually work when compiled to javascript
    angular.extend(Resource.prototype, Project.prototype);
    return Resource;
}
module projectFactory {
    export var $inject: string[] = ['$resource'];
}

我还没有完全测试过,但我已经测试了一下并且有效。

答案 1 :(得分:0)

使用service而不是factory注册课程:

servicesModule.service('NamedEntityResource', NamedEntityResource);

免责声明:我的视频介绍了有关angularjs + typescript中服务注册的其他信息:http://www.youtube.com/watch?v=Yis8m3BdnEM&hd=1

答案 2 :(得分:0)

这是我如何在这里使用$ http

module portal{

  var app =angular.module('portal',[]);
  app.service(services);
}

module portal.services {


export class apiService {


    public getData<T>(url?:string): ng.IPromise<T> {

        var def = this.$q.defer();
        this.$http.get(this.config.apiBaseUrl + url).then((successResponse) => {

            if(successResponse)
                def.resolve(successResponse.data);
            else
                def.reject('server error');

        }, (errorRes) => {

            def.reject(errorRes.statusText);
        });

        return def.promise;
    }

    public postData<T>(formData: any, url?:string,contentType?:string): ng.IPromise<T>{

        var def = this.$q.defer();

        this.$http({
            url: this.config.apiBaseUrl + url,
            method: 'POST',
            data:formData,
            withCredentials: true,
            headers: {
                'Content-Type':contentType || 'application/json'
            }
        }).then((successResponse)=>{
            def.resolve(successResponse.data);
        },(errorRes)=>{
            def.reject(errorRes);
        });

        return def.promise;

    }

    static $inject = ['$q','$http', 'config'];

    constructor(public $q:ng.IQService,public $http:ng.IHttpService, public config:interfaces.IPortalConfig) {


    }

}



}