【问题标题】:Passing variables to injected services to use as decorator argument将变量传递给注入的服务以用作装饰器参数
【发布时间】:2021-07-12 17:34:48
【问题描述】:

我有一个 Angular 项目,它使用的服务经常通过典型的 create/read/update/delete/read-list-of-all-entries 重复出现。我也经常想在收到数据后以类似的方式处理收到的数据。所以我编写了使用apply 的装饰器,它以传递给它们的参数确定的方式“转换”数据(参数始终是对象类)。

正常使用时是这样的:

//rule.service.ts
@Injectable({
  providedIn: 'root'
})
export class RuleService {

  rulesUrl: string = `${Constants.wikiApiUrl}/rule/`;

  constructor(private http: HttpClient) {}

  @TransformObservable(RuleObject)
  getRule(pk: number): Observable<Rule>{
    return this.http.get<Rule>(`${this.rulesUrl}/${pk}`);
  }
}

这种情况经常发生,所以我想编写一个可以扩展的父服务:

//generic-object.service.ts

@Injectable({
  providedIn: 'root'
})
export abstract class GenericObjectService{
  baseUrl: string;

  constructor(
    private http: HttpClient,
    public objectClass: any,
  ) {}

  @TransformObservable(this.objectClass) //This line won't work
  read(pk: number): Observable<any>{
    return this.http.get(`${this.baseUrl}/${pk}`);
  }
}

问题是我不能使用“this”作为装饰器参数,它在解析 IIRC 时不存在。但是有没有办法可以以某种方式将装饰器参数传递给我的注入服务,以便装饰器可以使用它?

在下面还可以找到“TransformObservable”装饰器:

export function TransformObservable(modelClass: any){
    /**Decorator to apply transformObservableContent */
    return function(target: any, propertyKey: string, descriptor: PropertyDescriptor){

        const originalMethod = descriptor.value;
        descriptor.value = function(){
            const observable = originalMethod.apply(this, arguments);
            return transformObservableContent(observable, modelClass);
        }
        return descriptor;
    }
}

【问题讨论】:

    标签: angular inheritance decorator


    【解决方案1】:

    this 关键字在函数范围之外将不可用。但是,您可以将所需的对象定义为同一类中的字段/属性,并且在装饰器注释中,您可以只传递装饰器函数将查找的字符串,或传递实际的对象引用。

    在装饰器中,检查传递给注解的参数:

    if(typeof modelClass === 'string'){
      // lookup a property in decorated class definition
      modelClass = target[modelClass]; // or, this[modelClass]
    }else {
      // assume modelClass is the needed object
    }
    

    这将允许您使用:

    @TransformObservable('propertyName');
    // or
    @TransformObservable(someObjectReference);
    

    targetthis 之间的区别:

    传递给装饰器函数的target 是对象,它定义了使用装饰器的类。它将具有类中定义的所有属性和方法,并可用于检索它们,如上所示。但是target 不是您通常在类方法中使用的运行时this。它唯一的替换方法 (descriptor.value) 将在其中包含 this,就像原始方法一样。

    因此,如果您真的需要this 而不仅仅是修饰类定义,您必须将所有代码移入替换函数descriptor.value。在那里您可以访问this 以及传递给装饰器和装饰器工厂的所有参数。

    【讨论】:

    • 哦,所以 target = 写入装饰器的对象。 “this” = ...我实际上并不太确定装饰器内部的哪个上下文“this”在那时代表。启动调用的子服务?
    • 会不会是你编辑到现在的方式不对,我回复的时候this还写成target(供以后的读者)。 thisTransformObservable 中是未定义的,target 确实包含一个对象,该对象似乎是在其中写入装饰器的对象,也就是GenericObjectService。但是,出于某种原因,GenericObjectService 的属性不会显示在该对象中,只有函数会显示。
    • @PhilippDoerner 已更新答案。
    • 啊,我终于明白modelClass的检查必须发生在我放入descriptor.value的匿名函数中。对于错误的读者,我会将您的建议作为下面的另一种解决方案付诸实践,但您的建议仍将被接受。
    • @PhilippDoerner 是的。您必须将代码移动到替换函数中并在那里进行检查,您可以在那里使用this
    【解决方案2】:

    基于@S.D.的回答我终于明白了他一直想说的:你可以在你放入descriptor.value的匿名函数中访问this。因此,为了让我的装饰器能够正确执行此操作,我必须使用this 从该匿名函数内部访问modelClass,我很高兴。以下是 S.D. 的解决方案。看来要付诸实践了:

    export function TransformObservable(modelClass: any){
        /**Decorator to apply transformObservableContent */
        return function(target: any, propertyKey: string, descriptor: PropertyDescriptor){
    
            const originalMethod = descriptor.value;
            descriptor.value = function(){
                const observable = originalMethod.apply(this, arguments);
    
                //If the decorator argument was not a class but instead a property
                // name of a property on the object that uses the decorator that 
                // contains the class, update the modelClass variable
                const decoratorArgumentIsProperty = ["String", "string"].includes(typeof modelClass);
                if(decoratorArgumentIsTargetProperty){
                    modelClass = this[modelClass];
                }
    
                return transformObservableContent(observable, modelClass);
            }
            return descriptor;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      • 2021-11-21
      • 2010-12-30
      • 2021-08-20
      • 2018-08-24
      • 2014-10-01
      • 2016-02-03
      相关资源
      最近更新 更多