0

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};
}
3
  • I copy pasted your code to an Angular project and didn't get an error. What typings for IDirective do you use? Please add them to your question. Commented Feb 15, 2017 at 7:47
  • @Sabastin, I added the typings. This is very puzzling to me. I'm not sure why it doesn't work. Commented Feb 15, 2017 at 18:34
  • According to this: stackoverflow.com/questions/22077023/… the solution is to explicitly cast scope and bindToController to {[boundProperty:string]:string} and the error goes away. Commented Feb 15, 2017 at 18:55

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

This answer doesn't help at all. The problem is TypeScript is complaining about my bindToController not being right. I'm not sure what's going on.
0

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.

2 Comments

You don't need to write the word public seven (7) times, it has no impact on your code except where it reduces readability.
@AluanHaddad that depend on tslint settings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.