Mohamed Nuur
Mohamed Nuur

Reputation: 5655

Angular 1.5 + TypeScript Directive with Controller

I'm trying to create a directive with controller using Angular 1.5 and TypeScript 1.8 but it won't work for me. I looked at a bunch of examples but I am following those examples to the tee and it still won't work for me. Am I cursed?

Here is the code:

export class FooController implements angular.IController {
    static $inject = ['myService', '$state'];

    constructor(private myService: Services.MyService, private $state: angular.ui.IStateService) {
    }

    public foo: Services.Dtos.Foo;
    public bar;

    $onInit() {
        if (!this.foo) return;

        this.bar = this.$state.href('app.bar', {id: this.foo.id});
    }
}

export class FooDirective implements angular.IDirective {
    public restrict = 'E';
    public replace = true;
    public scope = {};
    public templateUrl = 'content/templates/foo.template.html';
    public controller = FooController;
    public controllerAs = 'ctrl';
    public bindToController = {
        foo: '<',
    };
}

It errors on FooDirective saying the following:

  1. Class 'FooDirective' incorrectly implements interface 'IDirective'.
  2. Types of property 'bindToController' are incompatible.
  3. Type '{ foo: string; }' is not assignable to type 'boolean | { [boundProperty: string]: string; }'.
  4. Type '{ foo: string; }' is not assignable to type '{ [boundProperty: string]: string; }'.
  5. Index signature is missing in type '{ foo: string; }'.

What am I doing wrong?

UPDATE 2017/02/15 IDirective looks like this (from angular.d.ts file):

interface IDirective {
    compile?: IDirectiveCompileFn;
    controller?: string | Injectable<IControllerConstructor>;
    controllerAs?: string;
    /**
     * @deprecated
     * Deprecation warning: although bindings for non-ES6 class controllers are currently bound to this before
     * the controller constructor is called, this use is now deprecated. Please place initialization code that
     * relies upon bindings inside a $onInit method on the controller, instead.
     */
    bindToController?: boolean | {[boundProperty: string]: string};
    link?: IDirectiveLinkFn | IDirectivePrePost;
    multiElement?: boolean;
    priority?: number;
    /**
     * @deprecated
     */
    replace?: boolean;
    require?: string | string[] | {[controller: string]: string};
    restrict?: string;
    scope?: boolean | {[boundProperty: string]: string};
    template?: string | ((tElement: JQuery, tAttrs: IAttributes) => string);
    templateNamespace?: string;
    templateUrl?: string | ((tElement: JQuery, tAttrs: IAttributes) => string);
    terminal?: boolean;
    transclude?: boolean | 'element' | {[slot: string]: string};
}

Upvotes: 0

Views: 1258

Answers (2)

Vlada
Vlada

Reputation: 258

It seems to be the problem with TypeScript definition only accepting boolean.

export class FooDirective implements angular.IDirective {
  public restrict = 'E';
  public replace = true;
  public scope = {};
  public templateUrl = 'content/templates/foo.template.html';
  public controller = FooController;
  public controllerAs = 'ctrl';
  public bindToController:any = {
      foo: '<',
  };
}

This would fix the error, on the other hand it won't typecheck bindToController.

Upvotes: 0

larlon
larlon

Reputation: 587

The directive should be a function that returns a configuration object. I would write the directive like this:

export const fooDirective = (): angular.IDirective => {
    return {
        restrict: 'E',
        replace: true,
        scope: {},
        templateUrl: 'content/templates/foo.template.html',
        controller: FooController,
        controllerAs: 'ctrl',
        bindToController: {
            foo: '<'
        }
    };
};

I would also recommend you take a look at the new component API. It greatly simplifies creating new components, and it uses many best practices by default, such as controllerAs.

https://docs.angularjs.org/guide/directive
https://docs.angularjs.org/guide/component

Upvotes: 1

Related Questions