Skip to content

Instantly share code, notes, and snippets.

@xogeny
Created July 8, 2015 20:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xogeny/fcbeba98b8d70dc5003a to your computer and use it in GitHub Desktop.
Save xogeny/fcbeba98b8d70dc5003a to your computer and use it in GitHub Desktop.
My canonical directive structure
/// <reference path="../typings/angularjs/angular.d.ts" />
// A canonical layout for defining angular directives in TypeScript
module ModuleForDirective {
// Defines what we expect to find in $scope. This mainly the stuff
// mapped in the scope: attribute of the directive, but it could
// include stuff inherited into $scope as well...
interface Scope extends ng.IScope {
// Stuff defined in { scope: { ... } } (attrs on directive)
// Stuff inherited in $scope
}
class Controller {
// View properties (stuff we compute from $scope)
// ...
static $inject = ["$scope", ...];
constructor(private $scope: Scope, ...) {
// Assign watches on stuff in { scope: { ... } }
$scope.$watch('prop1', (nv: TypeOfProp1) => {
this.computeView(nv, $scope.prop2)
}, true);
$scope.$watch('prop2', (nv: TypeOfProp2) => {
this.computeView($scope.prop1, nv)
}, true);
}
// Any methods you want to be able to invoke on the controller (from the template)
// (generally, these are to manipulate values on $scope are to evaluate
// computed properties.
// This method is used to recompute view properties based on values in $scope
// This assumes all members of the Controller class depend on all members in $scope.
// If they don't, then multiple functions can be defined to more efficiently compute
// subsets based on changes (dependencies) in $scope.
private computeView(prop1: TypeOfProp1, prop2: TypeOfProp2) {
// Update setup related view quantities
// (compute values of members of this class based on p1 and p2 from $scope)
// this._____ = f(prop1, prop2);
}
}
export function Directive(): ng.IDirective {
return {
restrict: "E", // Can only be an Element
replace: false,
controller: Controller,
controllerAs: "cname",
template: ...,
scope: {
prop1: '=',
prop2: '=',
...
// Should match inputs to Scope interface
},
// Fancy stuff often requires a link: field to be defined as well
};
}
Directive.$inject = []; // Any dependencies to inject in call to Directive
}
angular.module("ModuleName").directive(ModuleForDirective.Directive)
@xogeny
Copy link
Author

xogeny commented Jul 8, 2015

Note that ModuleForDirective.Scope and ModuleForDirective.Controller are not exported (nor do they need to be). The names prop1 and prop2 are just examples of attributes the directive might have.

I'm using controllerAs to cleanly delineate between information that is passed into the directive (members of Scope) and information that is computed by the directive (members of Controller).

It seems like the use of watch here is probably not the most efficient. This example assumes the worst case which is that prop1 and prop2 are objects and that they can potentially change during digests. Obviously, this can be optimized for more specific cases.

@basarat
Copy link

basarat commented Jul 8, 2015

I don't see anything bad here. Here's what we have at Picnic:

Filename fooDirective.ts

import {dustApp} from "../main";

export class FooDirectiveController {

    static $inject = ['$element', '$scope'];
    constructor(public $element: JQuery, public $scope: FooDirectiveScope) {
        $scope.vm = this;

        // Any Jquery access goes here. Use $element

        // Setup any $watch on $scope that you need
    }
}

export interface FooDirectiveScope extends ng.IScope {
    bar: string;

    // Local design only
    vm: FooDirectiveController;
}

dustApp.directives.directive('foo', function (): ng.IDirective {
    return {
        restrict: 'E',
        scope: {
            // NOTE : see documentation in type information
            bar: '='
        },
        templateUrl: 'fooDirective.html',
        controller: FooDirectiveController
    };
});

Filename fooDirective.html

<div>
    Foo
</div>

Note: I'm using external modules

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment