#angular #inheritance #decorator
Вопрос:
У меня есть угловой проект, в котором используются сервисы, которые очень часто повторяются с типичным созданием/чтением/обновлением/удалением/списком всех записей для чтения. Я также очень часто хочу манипулировать полученными данными аналогичными способами после их получения. Поэтому я написал декораторы, которые используют 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}`);
}
}
Проблема в том, что я не могу использовать «это» для параметра декоратора, его не существует во время анализа IIRC. Но есть ли способ, которым я мог бы каким-то образом передать параметр декоратора в мой введенный сервис, чтобы он мог использоваться декоратором?
Ниже также приведен «Наблюдаемый» декоратор:
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
всем параметрам, которые передаются декоратору и фабрике декораторов.
Комментарии:
1. Ооо, итак, цель = объект, внутри которого написан декоратор. «это» = … На самом деле я не совсем уверен, какой контекст «это» внутри декоратора представляет в этот момент. Служба поддержки детей, которая начинает вызов?
2. Может быть, то, как вы отредактировали сейчас, неверно, я написал свой ответ, когда
this
еще был написан какtarget
(для будущих читателей).this
inTransformObservable
не определен,target
включает объект, который, по-видимому, является объектом, внутри которого написан декоратор, он жеGenericObjectService
. Однако по какой-то причине свойства GenericObjectService не отображаются в этом объекте, отображаются только функции.3. @PhilippDoerner Обновил ответ.
4. Ах, я наконец понял, что проверка класса модели должна происходить в анонимной функции, которую я ввел в descriptor.value. Для заблудшего читателя я приведу ваше предложение, воплощенное на практике, в качестве другого решения ниже, но ваш ответ останется принятым.
5. @PhilippDoerner Правда. Вы должны переместить код внутри функции замены и выполнить проверки там, и вы можете использовать
this
его там.
Ответ №2:
Основываясь на ответе @S. D., я, наконец, понял, что он все это время пытался сказать: у вас есть доступ к this
внутренней анонимной функции, которую вы помещаете в descriptor.value. Поэтому, чтобы мой декоратор мог правильно это сделать, я должен получить доступ modelClass
изнутри к этой анонимной функции с помощью this
, и я готов. Вот как выглядит решение от 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;
}
}