Skip to content

Instantly share code, notes, and snippets.

@theladyjaye
Last active May 31, 2022 11:57
Show Gist options
  • Save theladyjaye/b9063c68cee51e9e0a7535f222e27e6e to your computer and use it in GitHub Desktop.
Save theladyjaye/b9063c68cee51e9e0a7535f222e27e6e to your computer and use it in GitHub Desktop.
Marionette TypeScript Utils

Decorators

Read the following:

The code below is directly taken from: http://benmccormick.org/2015/07/06/backbone-and-es6-classes-revisited/

YOU MUST ENABLE THE FOLLOWING IN YOUR TS CONFIG FOR THIS TO WORK

"experimentalDecorators": true

tagName, template, regions are all on the Class itself:

@tagName("div")
class ViewA extends Marionette.View<Backbone.Model>{}

on and model decorate methods Things like events becomes really nice:

import * as Backbone from 'backbone'
import * as Marionette from 'backbone.marionette'
import { on } from '../decorators';

export default class ViewA extends Marionette.View<Backbone.Model>{

  @on('click .label') // this will register it in events: {}
    onLabelClick(){
      channel.trigger('click');
  }
}

The Code

import * as _ from 'underscore';


export function tagName(value: string) {
    return function decorator(target: any): any {
        target.prototype.tagName = value;
    }
}

export function template(value: any): any {
    return function decorator(target) {
        target.prototype.template = value
    }
}


export function regions(value: {[name: string]: string}): any{
     return function(target){
        if(!target.prototype.regions) {
            target.prototype.regions = {};
        }
        if(_.isFunction(target.prototype.regions)) {
            throw new Error('The on decorator is not compatible with an regions method');
        }

        if(!value) {
            throw new Error('The on decorator requires an eventName argument');
        }

        target.prototype.regions = value
    }
}

export function model(eventName: string): any{
    return function(target, name, descriptor){
        if(!target.modelEvents) {
            target.modelEvents = {};
        }

        if(_.isFunction(target.modelEvents)) {
            throw new Error('The on decorator is not compatible with an modelEvents method');
        }

        if(!eventName) {
            throw new Error('The on decorator requires an eventName argument');
        }

        target.modelEvents[eventName] = name;
        return descriptor;
    }
}


export function on(eventName: string): any{
    return function(target, name, descriptor){
        if(!target.events) {
            target.events = {};
        }

        if(_.isFunction(target.events)) {
            throw new Error('The on decorator is not compatible with an events method');
        }

        if(!eventName) {
            throw new Error('The on decorator requires an eventName argument');
        }

        target.events[eventName] = name;
        return descriptor;
    }
}

Typed Models:

myModel.props.<IDE_COMPLETIONS>

Still runs though the model.get/set methods, but you get nice IDE support.

Concept taken from:

http://stackoverflow.com/a/20553104/1060314

The Code

export default class TypedModel<T> extends Backbone.Model {
    attributes: T
    props: T;

    constructor(attributes?: T, options?: any) {
        super(attributes, options);

        var defaults = this.defaults();
        this.props = <T>{};

        for (var key in defaults) {
            var value = defaults[key];

            ((k: any) => {
                Object.defineProperty(this.props, k, {
                    get:(): typeof value => {
                        return this.get(k);
                    },
                    set:(value: any) => {
                        this.set(k, value);
                    },
                    enumerable: true,
                    configurable: true
                });
            })(key);
        }
    }

    public defaults(): T {
        throw new Error(`"defaults()" not implemented. You must implement the defaults() function in your subclass`);
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment