npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ng-decorate

v0.0.18

Published

ES7 decorators for AngularJS 1.x

Downloads

5

Readme

Description

ES7 decorators for Angular 1.x. Help you:

  • Get around Angular's dependency injection while using ES6 modules.
  • Make custom directive declarations very short and semantic.
  • Declare bindables directly on class properties.

Perfect for an ES7 / TypeScript application.

This readme assumes you're already using jspm and System, and have an ES6 transpilation workflow with babel or typescript. If not, here's a guide to get you started: [1].

Contents

Installation

In shell:

npm i --save-dev ng-decorate
# or
jspm install npm:ng-decorate

In app:

import {Component, bindTwoWay} from 'ng-decorate';

@Component({
  selector: 'my-accordeon'
})
class X {
  @bindTwoWay length: number;
}

Directives

ng-decorate provides two directive shortcuts: custom element (@Component) and custom attribute (@Attribute).

@Component

Defines a custom element: a directive with a template and an isolated scope.

Simplest usage:

import {Component} from 'ng-decorate';

@Component({
  selector: 'my-element'
})
class X {}

With default settings, this expands to:

angular.module('myElement', ['ng']).directive('myElement', function() {
  return {
    restrict: 'E',
    scope: {},
    templateUrl: 'my-element/my-element.html',
    controller: X,
    controllerAs: 'self',
    bindToController: true
  };
});
class X {}

See defaults for customisation.

@Attribute

Defines a custom attribute.

Simplest usage:

import {Attribute} from 'ng-decorate';

@Attribute({
  selector: '[my-attribute]'
})
class X {}

With default settings, this expands to:

angular.module('myAttribute', ['ng']).directive('myAttribute', function() {
  return {
    restrict: 'A',
    scope: false,
    controller: X
  };
});
class X {}

See defaults for customisation.

Directive Options

This applies to both @Component and @Attribute.

Any passed options will be included into the resulting directive definition object, so you can use the standard directive API on top of ng-decorate-specific options. Example:

@Component({
  selector: 'my-element',
  scope: {myProperty: '='}
})

selector : string

Required. This is the selector string for the resulting directive. For @Attribute, you can optionally enclose it into brackets:

@Attribute({selector: '[my-attribute]'})

module : ng.IModule

Optional. The directive will be registered under the given angular module, no new module will be created, and other module options will be ignored:

@Component({
  module: angular.module('myApp'),
  selector: 'my-element'
})

I recommend using a single angular "module" for the entire application. Angular dependencies are kinda fake: all providers (services, etc.) are registered globally in the application injector, and dependencies "bleed through" to sister modules. Might as well admit this and keep it simple. The real dependency tree of your application is defined by ES6 modules. See defaults for setting up a default angular module.

moduleName : string

Optional. Dictates the name of the new angular "module" that will be created:

@Attribute({
  moduleName: 'app.myAttribute',
  selector: '[my-attribute]'
})

If omitted, defaults to the directive's name, as shown above. See defaults for how to set up an implicit module.

dependencies : string[]

Optional. Names of other angular "modules" the newly created module depends on. This is necessary when you depend on third party services that need to be dependency-injected (see inject below):

@Component({
  selector: 'my-link',
  dependencies: ['ui.router']
})

If omitted, defaults to ['ng'], as shown above.

inject : string[]

Deprecated, see @autoinject.

Optional. Names of angular services that will be dependency-injected and automatically assigned to the class's prototype:

@Component({
  selector: 'my-element',
  inject: ['$q']
})
class X {
  constructor() {
    console.log(this.$q)
  }
}

This lets you easily get hold of angular services while using ES6 modules for everything else. The magic happens during Angular's "run phase", before any directives are instantiated.

See the gotcha for the possible dependency injection issues. They can be easily avoided by using just one module.

injectStatic : string[]

Deprecated, see @autoinject.

Optional. Works exactly like inject, but assigns the injected services to the class as static properties.

@Component({
  selector: 'my-element',
  injectStatic: ['$http']
})
class X {
  constructor() {
    console.log(X.$http)
  }
}

Statics

These directive options can be included as static methods or properties:

template
templateUrl
compile
link
scope

Example:

@Attribute({
  selector: 'svg-icon'
})
class X {
  @autoinject $templateCache;

  static template($element) {
    var element = $element[0];
    var src = 'svg/' + element.getAttribute('icon') + '.svg';
    return X.$templateCache.get(src);
  }
}

Services

@Ambient

Dependency injection helper. It's a strict subset of other decorators in this library. Use it when you want to obtain Angular's services without creating any new directives or services.

import {Ambient, autoinject} from 'ng-decorate';

@Ambient
class X {
  @autoinject $q;
  @autoinject static $http;

  constructor() {
    console.log(this.$q)
    console.log(X.$http)
  }
}

(See @autoinject below.)

Just like with other class decorators, you can include module, moduleName and so on. For this particular decorator, arguments are optional.

@Service

Same as @Ambient but registers a new angular service with the given name. The serviceName option is mandatory. This is useful when you're porting a legacy application, where some parts still rely on Angular's DI.

Create a service:

@Service({
   serviceName: 'MySpecialClass'
})
class X {}

Get it in Angular's DI:

angular.module('app').run(['MySpecialClass', function(MySpecialClass) {
  /* ... */
}]);

@Controller

Same as @Ambient but also registers the class as an "old school" controller under the given name. Can optionally publish the class to the DI system, just like @Service.

Register the controller:

@Controller({
  controllerName: 'X'
})
class X {}

@Controller({
  controllerName: 'Y',
  serviceName: 'Y'
})
class Y {}

Use in a template:

<div ng-controller="X"></div>

This type of controller usage is outdated and generally not recommended. Use @Component instead. This decorator is provided to ease migration of legacy apps to ES6/7.

Service Options

This applies to @Ambient, @Service, and @Controller.

module : ng.IModule

moduleName : string[]

dependencies : string[]

See Directive Options.

Bindings

Directly annotate class properties to declare them as bindable. Perfect with TypeScript. When used with @Component or @Attribute, the annotations are grouped and converted into a scope: {/* ... */} declaration for the internal directive definition object.

Example:

import {Component, bindString, bindTwoWay} from 'ng-decorate';

@Component({
  selector: 'editable'
})
class X {
  @bindString label: string;
  @bindTwoWay value: string;
}

Expands to:

import {Component} from 'ng-decorate';

@Component({
  selector: 'editable',
  scope: {
    label: '@',
    value: '='
  }
})
class X {
  label: string;
  value: string;
}

This lets you declare bindings directly in the class body and makes them more semantic.

@bindString

@Component({
  selector: 'my-element'
})
class X {
  @bindString first: string;
  @bindString('last') second: string;
}

Expands to:

@Component({
  selector: 'my-element',
  scope: {
    first: '@',
    second: '@last'
  }
})
class X {
  first: string;
  second: string;
}

@bindTwoWay

@Component({
  selector: 'my-element'
})
class X {
  @bindTwoWay first: any;
  @bindTwoWay({collection: true, optional: true, key: 'last'})
  second: any;
}

Expands to:

@Component({
  selector: 'my-element',
  scope: {
    first: '=',
    second: '=*?last'
  }
})
class X {
  first: any;
  second: any;
}

@bindOneWay

This is a special feature of ng-decorate. It bridges the gap between Angular 2, where one-way binding is the default, and Angular 1.x, which doesn't support it natively.

Example with a hardcoded string:

<controlled-input value="'constant value'"></controlled-input>
@Component({
  selector: 'controlled-input'
})
class X {
  @bindOneWay value: any;

  constructor() {
    this.value = 123;         // has no effect
    console.log(this.value);  // prints 'constant value'
  }
}

@bindExpression

@Component({
  selector: 'my-element'
})
class X {
  @bindExpression first: Function;
  @bindExpression('last') second: Function;
}

Expands to:

@Component({
  selector: 'my-element',
  scope: {
    first: '&',
    second: '&last'
  }
})
class X {
  first: Function;
  second: Function;
}

@autoinject

Properties annotated with @autoinject are automatically assigned to the prototype (instance properties) or class (static properties). You don't need to inject them in the constructor.

Must be used with one of the class decorators, like @Component or @Ambient.

import {Component, autoinject} from 'ng-decorate';

@Component({
  selector: 'todo-list'
})
class X {
  @autoinject $q;
  @autoinject static $timeout;

  constructor() {
    console.log(this.$q);
    console.log(X.$timeout);
  }
}

Works great with TypeScript and property type annotations.

As an alternative, you can pass arrays of properties as inject and injectStatic to the class decorator (deprecated).

Note that this only works for global services. For contextual dependencies like $scope, use constructor injection:

class X {
  static $inject = ['$scope', '$element'];
  constructor($scope, $element) {
    /* ... */
  }
}

With TypeScript, use the private or public modifier to automatically assign the injected value to the instance:

class X {
  static $inject = ['$scope', '$element'];
  constructor(private $scope, private $element) {
    /* ... */
  }
}

defaults

The package is stateful. You can import its configuration object and mutate it to set global defaults.

Default configuration (with type annotations for the sake of clarity):

export const defaults = {
  // Default angular module. Supersedes module declarations.
  module: <ng.IModule>null,
  // Default module name.
  moduleName: <string>null,
  // Controller key. Other common variants include 'ctrl' and 'vm'.
  controllerAs: 'self',
  // Generates a template url from an element name. Another common variant:
  //   'components/elementName/elementName.html'.
  makeTemplateUrl(elementName: string): string {
    return elementName + '/' + elementName + '.html';
  }
};

Example configuration:

import {defaults} from 'ng-decorate';

defaults.module = angular.module('app');
defaults.controllerAs = 'vm';

I recommend setting your application's main, or only, module as the default (see below).

Gotcha

Each angular "module" created or reused by the decorators must be present in the dependency tree of your main module (the one that has been bootstrapped via ng-app or angular.bootstrap). If you let the decorators create new modules, you must add them to the dependency list of the main module.

To avoid this, use a single angular module for the entire application, setting it in defaults or explicitly passing it to decorators. There's nothing to gain by using fake Angular dependendencies that end up sharing the same global namespace.

Alternatives

ng-forward is an upcoming ng1->ng2 migration helper based on angular-decorators with input from a1atscript, angular2-now and ng-decorate.