Creating a custom directive with controller in Angular 1.5 using TypeScript

Struggling to create a directive with controller using Angular 1.5 and TypeScript 1.8, but it's not working out as expected. Despite closely following examples, the code just won't cooperate. Is there some sort of curse at play here?

Let's take a look at 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: '<',
    };
}

The error is triggered in FooDirective and presents the following issues:

  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 could be going wrong here?

UPDATE 2017/02/15 IDirective definition extracted 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};
}

Answer №1

To create a directive, you should define a function that returns a configuration object. Here's an example of how I would write the directive:

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

If you're looking to streamline the process of creating components, I suggest checking out Angular's new component API. It simplifies the creation process and follows best practices like using controllerAs by default.

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

Answer №2

There appears to be an issue with the TypeScript definition only allowing booleans.

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

While this would resolve the error, it may not pass typechecking for bindToController.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the purpose of utilizing rootscope in Angular?

What is the purpose of using $rootscope in AngularJS? Can someone explain the difference between scope and rootscope? $rootscope.mystr=''; ...

ES6 Implementation of AngularJS Provider

I'm facing an issue with creating a provider in angularjs using es6 as it doesn't seem to be injected properly ($get is not being called). Check out the code snippet below: export default class NgIntlTelInputProvider { constructor() { ...

React: Issue with passing arguments to redux action hooks

In my React application, I have implemented Redux-Toolkit to manage reducers and actions with slices. I am currently working on creating actions that can update and delete values from the store, requiring arguments for their usage. To achieve this, I have ...

Cypress - Streamlining login procedures by relocating them to a standalone script

Recently, I delved into the world of Cypress testing and came across a code snippet from an official documentation recipe that I slightly modified: let token: string; function fetchUser() { cy.request('POST', 'https://my-api/', { ...

"Upon studying the Reflect-metadata index.d.ts file, I find myself perplexed by the variances in

While utilizing the reflect-metadata package, I encountered this particular type. However, I am uncertain about its meaning and function signature. function metadata(metadataKey: any, metadataValue: any): { (target: Function): void; ( ...

Leveraging Angular component in codebases of AngularJS and Angular versions

I am currently trying to implement the TableComponent within the AppInventoryModule // app-inventory.component.html ... <div class="table-container"> <mat-table [model]="tableModel" (sortChange)="onSortChange($ ...

Attempting to create distinct match matchups for every team in a manner reminiscent of the Swiss system format used in the 2024/25 UEFA Champion League

I've been working on devising a tournament pairing system modeled after the updated UEFA Champion League structure. The league phase involves 36 teams, categorized into 4 different pots. Each team is scheduled to play a total of 8 matches against 2 op ...

Access social media login functionality from the client side using AngularJS

I am currently implementing social login in Angular using satellizer. However, I came across a section in the documentation that mentions the need for server-side code as well. The specific lines are provided below: "Additionally, authorization (obtaining ...

How can we update the information in the initial observable using data from a separate observable, taking into consideration specific conditions in Angular with RxJS?

Encountered a scenario where I need to fetch data from an API (e.g. cars via getCars()) that returns Observables and then get additional data by car model (e.g. features via getFeatures(model)) in order to replace the features data for each car. In relati ...

Using Angular to bind data from dictionaries to objects

Within my Angular framework, I am managing several collections. One of these is a global 'product attribute' dictionary: attributes : [ {id:1, value:"red", type:"color"}, {id:2, value:"green", type:"color"}, {id:3, value:"big", typ ...

Unable to run 'ng serve -o', however 'ng serve' functions correctly

Encountering an issue with Angular when attempting to configure the Micro frontend Framework (module federation) for both the remote and host applications. They are not located in the same workspace. When running the remote app with "ng serve -o", an error ...

Troubleshooting: AngularJS ng-repeat not functioning correctly with array iteration

I've been encountering an issue while trying to create a loop using ng-repeat to iterate over an array of objects. It appears that the loop isn't running as expected. To provide some context, I have set up a jsfiddle - http://jsfiddle.net/a2xhzb ...

When $httpBackend.verifyNoOutstandingExpectation() results in the error message "Response not specified!"

From what I understand, $httpBackend.verifyNoOutstandingExpectation() serves the purpose of ensuring that all requests expected by your tests using $httpBackend.expect() were actually made by your code. If any request was not made, an exception is thrown. ...

Customize the form using a custom component in react-hook-form: setting a default value

I have been learning ReactJS + TypeScript for 3 months now. Recently, I have a question about using react-hook-form (v7) to edit a form. I want to integrate my custom component into the form and I managed to do it on my own! Here is a snippet of my form p ...

Ways to verify if Angular has been properly loaded

Imagine if I want to use angularjs from a CDN, but also have a backup in case the CDN request fails - perhaps by pointing to a local js file. For JQuery, there are examples of using javascript like this: if(typeof jQuery == 'undefined') { .. ...

Unable to compile Angular application because TypeScript build problem is caused by the data array returned from httpClient response

Hey there, I've been away from coding for a while and now I'm diving back into Angular 8 (not exactly loving it at the moment). When I run 'ng serve' everything seems fine, but when I try to build, I keep running into an error. userD() ...

Sometimes, Express may return the message "not found" on and off

After working with express for many years, I find myself a bit out of practice with TypeScript - and it seems like my eyesight is failing me! This is the first time I've encountered this issue, so I must be missing something... My current dilemma is ...

Angular Material - Text currently not displaying

As a student at university, I recently decided to delve into Angular for my academic purposes. To ease myself in, I simply copied some material from the official Angular webpage and pasted it into my editor after using bower to install angular-material ("b ...

Tips on creating a literal type that is determined by a specific value within an object

In the flow, I am able to create a dynamic literal type in this manner: const myVar = 'foo' type X = { [typeof myVar]: string } const myX: X = { foo: 1 } // will throw, because number const myX: X = { foo: 'bar' } // will not throw ...

Tips for incorporating a TypeScript enum value into an Angular2 ngSwitch expression

Is there a way to effectively use Typescript enums with Angular2's ngSwitch directive? I keep encountering the error "Cannot read property 'xxx' of undefined in ..." when attempting to utilize an enum in my component's template. How can ...