【问题标题】:Extending AngularJS $resource factory in TypeScript在 TypeScript 中扩展 AngularJS $resource factory
【发布时间】:2014-03-16 04:41:08
【问题描述】:

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

根据Misko Hevery 资源只是constructor 函数,所以我希望能够将我的$resource 定义为具有一些类型安全接口(@98​​7654328@ 或INamedEntity)的常规类并混合服务定义但我似乎无法让我的 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 加载的每个资源上进行注释。{}在这种情况下,我的INamedEntitys。

这是迄今为止我能得到的最接近的解决方案,它看起来确实有效,但感觉真的很糟糕。

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 在我的 constructor() 函数被调用时还没有初始化。
  2. 返回svc,这是来自构造函数的有角度的$resource 服务,您当然可以在JavaScript 中执行此操作,但在TypeScript 中感觉就像是非常脏的鸭子类型。
  3. 为了获得 svc.prototype 上的方法,我直接从我的基类扩展了它。这尤其令人讨厌,因为这意味着每次创建实例时都要设置原型。
  4. 这个 sh** 三明治的最终刺鼻气味是我必须在构造函数上调用 super() 来处理我要丢弃的实例,以便编译它。

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

【问题讨论】:

    标签: angularjs typescript


    【解决方案1】:

    service而不是factory注册课程:

    servicesModule.service('NamedEntityResource', NamedEntityResource);
    

    免责声明:我的视频是关于在 angularjs + typescript 中注册服务可能有用的附加信息:http://www.youtube.com/watch?v=Yis8m3BdnEM&hd=1

    【讨论】:

    • 谢谢,但我不清楚这如何帮助我扩展 $resource。你有例子吗?
    【解决方案2】:

    这就是我在这里如何使用 $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'];
      }
      

      我还没有完全测试,但我已经测试了一点并且可以工作。

      【讨论】:

      • 如果覆盖原型,资源实例方法如 $save 将被移除。您可以使用 angular.extend(Resource.prototype, Project.prototype); 修复它而是。
      • 这太棒了,对我有用。您也知道如何为错误类型执行此操作吗?在 Typescript 中,$resource 的错误类型是 angular.IHttpPromiseCallbackArg&lt;T&gt;,但我不确定在 IProject(或其他地方)的哪个位置我们会做一个等效的 angular.extend() 来为 $resource 的错误类型添加方法。
      • 这是否也向资源instance 添加了其他属性?似乎这些属性将是资源类的一部分。资源实例不能说同样的话。
      猜你喜欢
      • 2014-03-08
      • 1970-01-01
      • 1970-01-01
      • 2018-06-13
      • 1970-01-01
      • 2016-09-24
      • 2013-05-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多