2014-02-17 138 views
2

我试图用DefinatelyTypedIResourceIResourceClass和朋友)干净地编写一个角度自定义$resource扩展作为TypeScript类的工厂。

根据Misko Hevery资源只是constructor功能,所以我期待能够定义我$resource与一些类型安全接口(INamedEntityResourceINamedEntity),并混入服务定义一个普通类,但我似乎无法得到我的NamedEntityResource原型上的标准类方法最终在工厂实例上结束。

有没有办法做到这一点与constructor()功能,或者我应该放弃,只是在普通的JavaScript定义服务?

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); 
} 

进一步

所以这样做的目的是为了让我写一个资源类{}与将在每个资源我加载通过HTTP进行注释的方法。在这种情况下,我的INamedEntity s。

这是我迄今为止能够得到的最接近的解决方案,看起来可行,但感觉真的很讨厌。

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); 
} 

诀窍是:

  1. 将我想要的服务上的方法提升到基类中,因为this.prototype没有在构造函数调用的时候被初始化。
  2. 返回svc这是来自构造函数的角度$resource服务,您当然可以在JavaScript中执行该服务,但它在TypeScript中感觉非常脏鸭式输入。
  3. 为了得到svc.prototype上的方法,我直接从我的基类扩展。这特别令人讨厌,因为它意味着每次创建实例时都要设置原型。
  4. 这个sh **三明治的最后的辛辣气味是我必须在构造函数上调用super(),以便让它进行编译。

但是,最后,我可以将方法添加到NamedEntityResourceBase,它们将出现在从我的HTTP资源加载的所有实体的原型中。

回答

0

注册类与service代替factory

servicesModule.service('NamedEntityResource', NamedEntityResource); 

免责声明:我对其他信息的视频,你可能会发现关于angularjs服务注册有用+打字稿:http://www.youtube.com/watch?v=Yis8m3BdnEM&hd=1

+0

谢谢,但我不清楚这是如何帮助我扩展$资源。你有一个例子吗? – cirrus

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) { 


    } 

} 



} 
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']; 
} 

我还没有完全测试过,但我测试了一下并正常工作。

+2

如果覆盖原型,则会删除$ save等资源实例方法。你可以通过使用angular.extend(Resource.prototype,Project.prototype)来修复它。代替。 – Micke

+0

这真棒,适合我。你知道如何为错误类型做这个吗?在Typescript中,$ resource的错误类型是'angular.IHttpPromiseCallbackArg ',但我不确定'IProject'(或其他地方)中的什么地方我们会做等效的'angular.extend()'将方法添加到$资源的错误类型。 – rinogo

+0

这是否也为资源'实例'添加了其他属性?看来这些属性将成为资源类的一部分..可以说资源实例是一样的.. – Abdul23