将变量传递给注入的服务以用作装饰器参数
Posted
技术标签:
【中文标题】将变量传递给注入的服务以用作装饰器参数【英文标题】: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;
【问题讨论】:
【参考方案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);
target
和 this
之间的区别:
传递给装饰器函数的target
是对象,它定义了使用装饰器的类。它将具有类中定义的所有属性和方法,并可用于检索它们,如上所示。但是target
不是您通常在类方法中使用的运行时this
。它唯一的替换方法 (descriptor.value
) 将在其中包含 this
,就像原始方法一样。
因此,如果您真的需要this
而不仅仅是修饰类定义,您必须将所有代码移入替换函数descriptor.value
。在那里您可以访问this
以及传递给装饰器和装饰器工厂的所有参数。
【讨论】:
哦,所以 target = 写入装饰器的对象。 “this” = ...我实际上并不太确定装饰器内部的哪个上下文“this”在那时代表。启动调用的子服务? 会不会是你编辑到现在的方式不对,我回复的时候this
还写成target
(供以后的读者)。 this
在TransformObservable
中是未定义的,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;
【讨论】:
以上是关于将变量传递给注入的服务以用作装饰器参数的主要内容,如果未能解决你的问题,请参考以下文章