vytant / stimulus-decorators

TypeScript decorators for the Stimulus framework
https://npm.im/@vytant/stimulus-decorators
MIT License
12 stars 2 forks source link
decorators stimulus typescript

Stimulus Decorators

Stimulus Decorators is a TypeScript library that extends the Stimulus framework with TypeScript decorators to give you improved IntelliSense and type safety of automatically generated Stimulus controller properties.

Prerequisites

Installation

If you use Yarn package manager.

yarn add @vytant/stimulus-decorators

If you use npm package manager.

npm install --save @vytant/stimulus-decorators

Usage

There are several decorators:

@Target decorator

Explicitly define target properties with types using the @Target decorator, and it will automatically add them to the static targets array for your Stimulus controller.

// hello_controller.ts
import { Controller } from '@hotwired/stimulus';
import { Target, TypedController } from '@vytant/stimulus-decorators';

@TypedController
export default class extends Controller {
  @Target outputTarget!: HTMLElement;
  @Target nameTarget!: HTMLInputElement;

  greet() {
    this.outputTarget.textContent = `Hello, ${this.nameTarget.value}!`;
  }
}

Equivalent to:

// hello_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static targets = ['name', 'output'];

  greet() {
    this.outputTarget.textContent = `Hello, ${this.nameTarget.value}!`;
  }
}

@Targets decorator

To get an array of all matching targets in scope, use the @Targets decorator.

// slider_controller.ts
import { Controller } from '@hotwired/stimulus';
import { Targets, TypedController } from '@vytant/stimulus-decorators';

@TypedController
export default class extends Controller {
  @Targets slideTargets!: HTMLElement[];

  connect() {
    this.slideTargets.forEach((element, index) => {
      /* … */
    });
  }
}

Equivalent to:

// slider_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static targets = ['slide'];

  connect() {
    this.slideTargets.forEach((element, index) => {
      /* … */
    });
  }
}

@Value decorator

Explicitly define value properties with types and default values using the @Value decorator, and it will automatically add them to the static values object for your Stimulus controller.

// loader_controller.ts
import { Controller } from '@hotwired/stimulus';
import { Value, TypedController } from '@vytant/stimulus-decorators';

@TypedController
export default class extends Controller {
  @Value(String) urlValue!: string;
  @Value(String) methodValue: string = 'GET';

  connect() {
    fetch(this.urlValue, { method: this.methodValue }).then(/* … */);
  }
}

Equivalent to:

// loader_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static values = {
    url: String,
    method: { type: String, default: 'GET' },
  };

  connect() {
    fetch(this.urlValue, { method: this.methodValue }).then(/* … */);
  }
}

If you'd like to set the type of each value from its type definition, you must use reflect-metadata.

  1. Set "emitDecoratorMetadata": true in your tsconfig.json.
  2. Import reflect-metadata before importing @vytant/stimulus-decorators (importing reflect-metadata is needed just once).
// loader_controller.ts
import 'reflect-metadata';
import { Controller } from '@hotwired/stimulus';
import { Value, TypedController } from '@vytant/stimulus-decorators';

@TypedController
export default class extends Controller {
  @Value urlValue!: string;
  @Value methodValue: string = 'GET';

  connect() {
    fetch(this.urlValue, { method: this.methodValue }).then(/* … */);
  }
}

@Class decorator

Explicitly define CSS class properties with types using the @Class decorator, and it will automatically add them to the static classes array for your Stimulus controller.

// search_controller.ts
import { Controller } from '@hotwired/stimulus';
import { Class, TypedController } from '@vytant/stimulus-decorators';

@TypedController
export default class extends Controller {
  @Class loadingClass!: string;

  loadResults() {
    this.element.classList.add(this.loadingClass);

    fetch(/* … */);
  }
}

Equivalent to:

// search_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static classes = ['loading'];

  loadResults() {
    this.element.classList.add(this.loadingClass);

    fetch(/* … */);
  }
}

@Classes decorator

To get an array of classes in the corresponding CSS class attribute, use the @Classes decorator.

// search_controller.ts
import { Controller } from '@hotwired/stimulus';
import { Classes, TypedController } from '@vytant/stimulus-decorators';

@TypedController
export default class extends Controller {
  @Classes loadingClasses!: string[];

  loadResults() {
    this.element.classList.add(...this.loadingClasses);

    fetch(/* … */);
  }
}

Equivalent to:

// search_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static classes = ['loading'];

  loadResults() {
    this.element.classList.add(...this.loadingClasses);

    fetch(/* … */);
  }
}

@TypedController decorator

It is required to use the @TypedController decorator on every Stimulus controller where you use @Target, @Targets, or @Value decorators.

// controller.ts
import { Controller } from '@hotwired/stimulus';
import { TypedController } from '@vytant/stimulus-decorators';

@TypedController
export default class extends Controller {
  /* … */
}

License

The project is MIT licensed.