Algimantas Krasauskas
Algimantas Krasauskas

Reputation: 403

AngularJS Typescript Directive

Having problems creating nested directive using Typescript. I can do it in simple AngularJs: http://plnkr.co/edit/UruTqEdMnqNT5wjxcQNC?p=preview ,

but using TypeScript it gives me "No controller" error message.

/// <reference path="../../Scripts/AngularJs/Typings/angular.d.ts" />

export class directiveA {
    public static $inject: Array<string> = [];
    constructor() {
        var directive: ng.IDirective = {};
        directive.priority = 0; 
        directive.restrict = "A";
        directive.scope = {};
        directive.transclude = true;
        directive.templateUrl = "otherTemplate.html";
        directive.replace = true;
        directive.controller = function ($scope, $element) {
            this.flip = function () {
                $element.toggleClass("flipped");
             }
        }
        directive.replace = true;

        return directive;
    }
} 


export class directiveB{
    public static $inject: Array<string> = [];
    constructor() {
        var directive: ng.IDirective = {};
        directive.require = "^directiveA";
        directive.priority = 1;
        directive.restrict = "A";
        directive.scope = {
            simplrSide : "@"
        };
        directive.transclude = true;
        directive.templateUrl = "templeUrl.html";
        directive.link = function (scope, iElement, iAttrs, simplrEditable) {
            scope.flip = function () {
                simplrEditable.flip();
            }
        }
        directive.replace = true;
        return directive;
    }
}

I dont know if its relevant, but i am using AMD Require.JS for script load

Upvotes: 11

Views: 23147

Answers (4)

user89862
user89862

Reputation:

With my solution you can both use a TS class and not having to worry about a factory and repeat what you need injected.

module YourApp.Common.Directives {

    class SearchInputController {
        public query: string;

        constructor(private $location: ng.ILocationService) {
        }

        doSearch(): void {
            this.$location.url(`/search?q=${this.query}`);
            this.query = '';
        }
    }

    export function SearchInputDirective($location: ng.ILocationService): ng.IDirective {
        return {
            restrict: 'E',
            templateUrl: 'common/directives/searchInput/SearchInputDirective.html',
            replace: true,
            scope: true,
            controllerAs: 'SearchInputController',
            bindToController: {
                'query': '@'
            },
            controller: (): any => new SearchInputController($location)
        };
    }

    SearchInputDirective.$inject = ['$location'];
}

To register:

angular.module('common', [])
    .directive('searchInput', YourApp.Common.Directives.SearchInputDirective);

And the HTML to see the whole picture (templateUrl):

<form ng-submit="SearchInputController.doSearch()">
    <input type="search" ng-model="SearchInputController.query">
</form>

Upvotes: 2

Whisher
Whisher

Reputation: 32806

a simple way to write directive in ts I think can work with nested directive as well

class D{
    static foo(){
        return {
                restrict:'A',
                template:'<div>Here I am to save the day</div>',
                replace: true
            }
    }
}


/// <reference path="./angular.d.ts"/>
/// <reference path="./directive.ts"/>
class MyApp{
    public app:AngularModule;
    constructor(){
          this.app = angular.module('myApp', []);
          this.app.directive ('superman',() => {
                return D.foo();
            } 
          );
    }
}
new MyApp();

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Directive</title>
    </head>
    <body>
        <div data-ng-app="myApp">
            <div data-superman></div>  
        </div>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>

    <script src="./directive.js"></script>
    <script src="./appDirective.js"></script>

    </body>
</html>

Upvotes: 1

Algimantas Krasauskas
Algimantas Krasauskas

Reputation: 403

The issue is not related to Typescript, but to AngularJS Directives. Changing templateUrl to template and using inline code, helps to resolve the errors. It's AngularJS Issue, more on that: https://github.com/angular/angular.js/issues/1903 Hope they will fix this in the future!

export class directiveA {
    public static $inject: Array<string> = [];
    constructor() {
        var directive: ng.IDirective = {};
        directive.priority = 0; 
        directive.restrict = "A";
        directive.scope = {};
        directive.transclude = true;
        directive.template = "<div>Your content</div>";
        directive.replace = true;
        directive.controller = function ($scope, $element) {
            this.flip = function () {
               //Some func
             }
        }
        directive.replace = true;

        return directive;
    }
} 

Upvotes: 4

basarat
basarat

Reputation: 276343

Assuming you are registering these as :

import mod = require('yourfile')
youmodule.directive('directiveA',mod.directiveA);
youmodule.directive('directiveB',mod.directiveB);

That should work as long as your html looks like:

<div directiveA>
  <div directiveB>
  </div>
</div>

A couple of notes beyond that:

Use functions for your directive definitions.

This is because directives (unlike controllers) are called without the new operator. So if you have something like:

class Test{
    foo = "EAC";
    constructor(){
        var directive:any = {}; 
        directive.restrict = this.foo;
    }
} 

It compiles to incorrect javascript. As the function Test is called without the new operator and that means that this refers to window and not an instance of the class. So you can't use anything defined outside the constructor anyways. I recommend something like:

function foo():ng.IDirective{
    return {
        restrict: 'EAC';    
    }
}

This way typescript will help you write correct javascript for angular instead of point you in the wrong way. I will make a video about this at some point

Use classes for your controller Controllers inside of directives are also called with the new operator. Same as controllers outside : http://www.youtube.com/watch?v=WdtVn_8K17E Again let typescript help you with the meaning of this inside the controller definition. Plus you can use the type for the controller in the child directive something like (for typesafety and inference):

link: function (scope, iElement, iAttrs, simplrEditable:YourControllerClass)

For injection into directive functions I still use $inject. I have the following interface definition :

interface Function{
    $inject:string[]
}

This means you can do :

foo.$inject = ['$compile']; // e.g

Upvotes: 14

Related Questions