AUR0774

Error Message

AUR0774: Invalid @watch decorator usage: decorated target <targetName> is not a class method.

Where <targetName> is the name of the property or field that was incorrectly decorated.

Description

This error occurs when the @watch decorator is applied to something other than its intended targets: a class method (when used as a method decorator) or a class definition (when used as a class decorator with a configuration object).

Cause

The @watch decorator is designed for specific use cases:

  1. Method Decorator: To automatically call the decorated method when a specified expression changes.

    class MyClass {
      @watch('expression')
      myHandlerMethod() { /* ... */ }
    }
  2. Class Decorator: To specify watchers via a configuration object.

    @watch({ expression: 'expr', changeHandler: 'handlerMethodName' })
    class MyClass {
      handlerMethodName() { /* ... */ }
    }

This error (AUR0774) occurs if you try to apply @watch to other kinds of class members, such as:

  • Class Fields/Properties:

    class MyClass {
      @watch('someExpression') // Incorrect: Applied to a property
      myProperty: string = 'value';
    }
  • Getters/Setters:

     class MyClass {
       _value: string;
       @watch('someExpression') // Incorrect: Applied to a getter
       get myValue() { return this._value; }
     }

The decorator logic specifically checks if context.kind (from decorator metadata) is 'method' or 'class' depending on how it's invoked. If it's anything else (like 'field', 'getter', 'setter', etc.), this error is thrown.

Solution

Ensure that the @watch decorator is only applied to:

  1. A standard class method: when you want that method to be the change handler.

  2. The class definition itself: when you are providing a configuration object with expression and changeHandler properties.

Do not apply @watch to class fields (properties), getters, or setters. If you need to react to changes related to these, watch the underlying properties or use other appropriate patterns.

Example

import { watch } from '@aurelia/runtime-html';
import { ILogger } from '@aurelia/kernel';

export class MyViewModel {
  firstName: string = 'John';
  lastName: string = 'Doe';
  userId: number = 1;

  constructor(@ILogger private readonly logger: ILogger) {}

  // Correct: @watch applied to a method
  @watch('firstName')
  firstNameChanged(newValue: string, oldValue: string) {
    this.logger.info(`First name changed: ${oldValue} -> ${newValue}`);
  }

  // Correct: @watch applied to the class
  @watch({ expression: 'lastName', changeHandler: 'lastNameChangedHandler' })
  static { /* Class decorator usage */ }

  lastNameChangedHandler(newValue: string, oldValue: string) {
     this.logger.info(`Last name changed: ${oldValue} -> ${newValue}`);
  }


  // Incorrect: @watch applied to a property/field - Causes AUR0774
  // @watch('userId')
  // watchedUserId: number = this.userId;


  private _internalValue: string = 'initial';
  // Incorrect: @watch applied to a getter - Causes AUR0774
  // @watch('somethingElse')
  // get computedValue(): string {
  //   return this._internalValue.toUpperCase();
  // }

  // Incorrect: @watch applied to a setter - Causes AUR0774
  // @watch('yetAnotherThing')
  // set computedValue(value: string) {
  //   this._internalValue = value;
  // }
}

// Correct class decorator usage syntax
@watch({ expression: 'userId', changeHandler: 'userIdChangedHandler' })
export class UserComponent {
   userId: number = 10;
   constructor(@ILogger private readonly logger: ILogger) {}

   userIdChangedHandler(newValue: number, oldValue: number){
      this.logger.info(`User ID changed: ${oldValue} -> ${newValue}`);
   }
}

Debugging Tips

  • Locate the @watch decorator mentioned in the stack trace.

  • Verify what kind of class member it is attached to (method, field, getter, setter, class).

  • Move the @watch decorator to the appropriate handler method or use it as a class decorator with the correct configuration object if necessary.

Last updated

Was this helpful?