ng-right
TypeScript icon, indicating that this package has built-in type declarations

0.2.36 • Public • Published

ng-right

ES7 decorators to make angular 1.x apps awesome. Resembles how angular2 is, but it is not a 1:1, as ng-decorate puts it, This lib is more to handle the angular 1.x DI hell.

Also, this is written in typescript and for sure works with typescript. I have heard it has problems with just es6 and babel. So, use typescript!!!

This is a mash up of mainly ng-decorate and some angular2-now

Readme copy of ng-decorate

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-right
# or
jspm install npm:ng-right

In app:

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

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

Directives

ng-right 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-right';

@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-right';

@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-right-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-right';

@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-right';

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

Expands to:

import {Component} from 'ng-right';

@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-right. 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-right';

@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) {
    /* ... */
  }
}

Extras based off of angular2-now

@State

import {State, autoinject} from 'ng-right';

@State({
  name: 'name-of-state',
  templateUrl: 'something.tpl.html'
})
class StateController {

  @autoinject $q;

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

@View

  • This decorator is optional and included to be more like angular2
  • Can be used with Component to specify template, templateUrl
import {Component, View, autoinject} from 'ng-right';

@Component({
  selector: 'app-head'
})
@View({ 
  template: '<div> the app head </div>',
  templateUrl: 'path-to-template.tpl.html' // template or templateUrl
})
class appHead {

  @autoinject $q;

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

Package Sidebar

Install

npm i ng-right

Weekly Downloads

2

Version

0.2.36

License

MIT

Last publish

Collaborators

  • j-walker23