Search code examples
angulartypescriptdecorator

Creating an Angular Decorator for Properties to add more Information to the Classes


I am trying to create a decorator in Angular that allows me to mark certain properties of a component as "Options" with a specific type. The goal is to add these marked properties to a list that stores all the options. Later, I would like to call a function called getOptions(), which should return a Map containing the option names and their corresponding property keys.

I came across this Stack Overflow answer that seems similar to my goal. However, when I tried implementing it, I encountered an error stating "Uncaught TypeError: Cannot assign to read-only property 'Symbol(fields)' of object '[object Object]'".

I would greatly appreciate any help resolving this issue and suggestions or alternative approaches to achieve my desired functionality.

Ideally, I would like to achieve the following:

@Component({...})
export class DashboardComponent {
    @OptionType('ColorOption')
    color: string = "";
    ...

    ngOnInit(): void {
         console.log(getAllProperties()); // Should return <<color, ColorOption>>
    }
}

I've also created a Codesandbox, where you can find a working (but not correct) example and can try out your stuff! If you need any more information, feel free to ask!


Solution

  • I've answered it myself. Basically, the decorator was a bit off. I created a fully working Codesandbox for everyone whos curious.

    Additionally, in the following you can find the decorator as code:

    /**
     * OptionType is a decorator function that can be used to annotate class properties with a specific options type.
     * @param optionsType - The options type for the annotated property.
     * @returns A decorator function that adds the options type to the target object.
     */
    export function OptionType(optionsType: OptionsType) {
      return function (target: any, key: string) {
        // Check if the target object has a property called FIELDS
        if (!target[OPTIONS_FIELD_PROP_NAME]) {
          // If not, define the FIELDS property as a new Map
          Object.defineProperty(target, OPTIONS_FIELD_PROP_NAME, {
            value: new Map(),
            writable: false, // Prevent overwriting
            enumerable: false, // Enable enumeration
            configurable: false, // Prevent attribute changes, deletions, etc.
          });
        }
    
        // Set the optionsType value in the FIELDS map with the key as the property name
        target[OPTIONS_FIELD_PROP_NAME].set(key, optionsType);
      };
    }