2014-02-17 398 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