├── .babelrc
├── .gitignore
├── .npmignore
├── README.md
├── module-helpers.js
├── module-loader.js
├── package.json
├── src
├── module-helpers
│ ├── config.js
│ ├── controller.js
│ ├── decorator.js
│ ├── directive.js
│ ├── factory.js
│ ├── filter.js
│ ├── index.js
│ ├── injectable.js
│ ├── provider.js
│ ├── runner.js
│ └── service.js
├── module-loader.js
└── utils.js
└── test
├── index.html
├── lib
├── src
├── index.js
└── specs
│ ├── loader.spec.js
│ └── module-helpers
│ ├── config.spec.js
│ ├── controller.spec.js
│ ├── decorator.spec.js
│ ├── directive.spec.js
│ ├── factory.spec.js
│ ├── filter.spec.js
│ ├── injectable.spec.js
│ ├── provider.spec.js
│ ├── runner.spec.js
│ └── service.spec.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0"]
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | npm-debug.log
4 | test/dist
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | test
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular-Ecmascript
2 |
3 | `angular-ecmascript` is a utility library which will help you write an [AngularJS](https://angularjs.org) app using `ES6`'s class system.
4 | As for now there is no official way to do so, however using `ES6` syntax is recommended, hence this library was created.
5 |
6 | In addition, `angular-ecmascript` provides us with some very handy features, like auto-injection without using any pre-processors like [ng-annotate](https://github.com/olov/ng-annotate). For more information about `angular-ecmascript`'s API and features please read the following [docs](#docs).
7 |
8 | ## Docs
9 |
10 | `angular-ecmascript` provides us with some base `module-helpers` classes which we should inherit from while writing our helpers. These are the available helpers:
11 |
12 | - [Provider](#provider)
13 | - [Service](#service)
14 | - [Factory](#factory)
15 | - [Controller](#controller)
16 | - [Directive](#directive)
17 | - [Decorator](#decorator)
18 | - [Filter](#filter)
19 | - [Config](#config)
20 | - [Runner](#runner)
21 |
22 | Each helper can be defined with a static `$inject` property which will be responsible for dependencies injection, and a static `$name` property, which is responsible for specifying the helper's name and defaults to the class'es name.
23 |
24 | ```js
25 | import { Service, Controller } from 'angular-ecmascript/module-helpers';
26 |
27 | class DateService extends Service {
28 | static $name = '$date'
29 |
30 | now() {
31 | return new Date().getTime();
32 | }
33 | }
34 |
35 | class MyController extends Controller {
36 | static $inject = ['$date']
37 |
38 | constructor(...args) {
39 | super(...args);
40 |
41 | this.createdAt = this.$date.now();
42 | }
43 | }
44 | ```
45 |
46 | To interface with these helpers we will need to use a `module-loader` provided by `angular-ecmascript`. Just create a new `AngularJS` module wrapped by the loader and use it like so:
47 |
48 | ```js
49 | // libs
50 | import Angular from 'angular';
51 | import Loader from 'angular-ecmascript/module-loader';
52 | // module-helpers
53 | import MyCtrl from './controllers/my.ctrl';
54 | import MyDirective from './directives/my.directive';
55 | import MyService from './services/my.service';
56 |
57 | // app
58 | App = Angular.module('my-app', [
59 | 'module1',
60 | 'module2',
61 | 'module3'
62 | ]);
63 |
64 | // loader
65 | new Loader(App)
66 | .load(MyCtrl)
67 | .load(MyDirective)
68 | .load(MyService);
69 | ```
70 |
71 | - `Loader()` can take a module name as the first argument and an optional dependencies array if you'd like to load a module by name.
72 | - `Loader.load()` can take an array of several module-helpers instead of chaining them one-by-one.
73 | - `Loader.load()` can take a string as the first argument representing the provider type and its value as the second argument, just like the [$provide](https://docs.angularjs.org/api/auto/service/$provide) service.
74 |
75 | ### Provider
76 |
77 | Used to define a new [provider](https://docs.angularjs.org/guide/providers).
78 |
79 | ```js
80 | import { Provider } from 'angular-ecmascript/module-helpers';
81 |
82 | class MomentProvider extends Provider {
83 | static $name = '$now'
84 |
85 | $get() {
86 | return new Date().getTime();
87 | }
88 | }
89 | ```
90 |
91 | ### Service
92 |
93 | Used to define a new [service](https://docs.angularjs.org/guide/services).
94 |
95 | ```js
96 | import { Service } from 'angular-ecmascript/module-helpers';
97 |
98 | class DateService extends Service {
99 | static $name = '$date'
100 |
101 | now() {
102 | return new Date().getTime();
103 | }
104 | }
105 | ```
106 |
107 | ### Factory
108 |
109 | Used to define a new `factory`.
110 |
111 | - Note that the `create` method must be implemented, otherwise an error will be thrown during load time.
112 |
113 | ```js
114 | import { Factory } from 'angular-ecmascript/module-helpers';
115 |
116 | class MomentFactory extends Factory {
117 | static $name = 'now'
118 |
119 | create() {
120 | return new Date().getTime();
121 | }
122 | }
123 | ```
124 |
125 | ### Controller
126 |
127 | Used to define a new [controller](https://docs.angularjs.org/guide/controller).
128 |
129 | - `$scope` will be injected automatically so no need to specify it.
130 | - When using [angular-meteor](https://github.com/Urigo/angular-meteor) the controller will be set as the view model automatically.
131 |
132 | ```js
133 | import { Controller } from 'angular-ecmascript/module-helpers';
134 |
135 | class MyController extends Controller {
136 | constructor(...args) {
137 | super(...args);
138 |
139 | this.createdAt = new Date().getTime();
140 | }
141 |
142 | logCreationTime() {
143 | console.log(`created at: ${this.createdAy}`);
144 | }
145 | }
146 | ```
147 |
148 | ### Directive
149 |
150 | Used to define a new [directive](https://docs.angularjs.org/guide/directive).
151 |
152 | ```js
153 | import { Directive } from 'angular-ecmascript/module-helpers';
154 |
155 | class MyDirective extends Directive {
156 | templateUrl: 'my-template.html'
157 | restrict = 'E'
158 | transclude = true
159 | scope = {}
160 |
161 | link(scope) {
162 | scope.foo = 'foo';
163 | scope.bar = 'bar';
164 | }
165 | }
166 | ```
167 |
168 | ### Decorator
169 |
170 | Used to define a new [decorator](https://docs.angularjs.org/guide/decorators).
171 |
172 | - `$delegate` will be injected automatically so no need to specify it.
173 | - Note that the `decorate` method must be implemented, otherwise an error will be thrown during load time.
174 | - No need to return the `$delegate` object, it should be handled automatically.
175 |
176 | ```js
177 | import { Decorator } from 'angular-ecmascript/module-helpers';
178 |
179 | class MyDecorator extends Decorator {
180 | static $name = 'myService'
181 |
182 | helperFn() {
183 | // an additional fn to add to the service
184 | }
185 |
186 | decorate() {
187 | this.$delegate.aHelpfulAddition = this.helperFn;
188 | }
189 | }
190 | ```
191 |
192 | ### Filter
193 |
194 | Used to define a new [filter](https://docs.angularjs.org/guide/filter).
195 |
196 | - Note that the `filter` method must be implemented, otherwise an error will be thrown during load time.
197 |
198 | ```js
199 | import { Filter } from 'angular-ecmascript/module-helpers';
200 |
201 | class MyFilter extends Filter {
202 | filter(input = '', uppercase) {
203 | let out = '';
204 |
205 | for (let i = 0; i < input.length; i++) {
206 | out = input.charAt(i) + out;
207 | }
208 |
209 | // conditional based on optional argument
210 | if (uppercase) {
211 | out = out.toUpperCase();
212 | }
213 |
214 | return out;
215 | }
216 | }
217 | ```
218 |
219 | ### Config
220 |
221 | Used to define a new `config`.
222 |
223 | - Note that the `configure` method must be implemented, otherwise an error will be thrown during load time.
224 |
225 | ```js
226 | import { Config } from 'angular-ecmascript/module-helpers';
227 |
228 | class RoutesCfg extends Config {
229 | static $inject = ['$routeProvider']
230 |
231 | constructor(...args) {
232 | super(...args);
233 |
234 | this.fetchUser = ['http', this::this.fetchUser];
235 | }
236 |
237 | configure() {
238 | this.$routeProvider
239 | .when('/', {
240 | template: '',
241 | resolve: {
242 | user: this.fetchUser
243 | }
244 | });
245 | }
246 |
247 | fetchUser($http) {
248 | return $http.get('...');
249 | }
250 | }
251 | ```
252 |
253 | ### Runner
254 |
255 | Used to define a new `run block`.
256 |
257 | - Note that the `run` method must be implemented, otherwise an error will be thrown during load time.
258 |
259 | ```js
260 | import { Runner } from 'angular-meteor/module-helpers';
261 |
262 | class RoutesRunner extends Runner {
263 | static $inject = ['$rootScope', '$state']
264 |
265 | run() {
266 | this.$rootScope.$on('$stateChangeError', (...args) => {
267 | const [,,, err] = args;
268 |
269 | if (err === 'AUTH_REQUIRED') {
270 | this.$state.go('login');
271 | }
272 | });
273 | }
274 | }
275 | ```
276 |
277 | ## Download
278 |
279 | The source is available for download from [GitHub](http://github.com/DAB0mB/angular-ecmascript). Alternatively, you can install using Node Package Manager (`npm`):
280 |
281 | npm install angular-ecmascript
282 |
--------------------------------------------------------------------------------
/module-helpers.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/module-helpers');
2 |
--------------------------------------------------------------------------------
/module-loader.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/module-loader');
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-ecmascript",
3 | "description": "Build an AngularJS app using ES6's class system",
4 | "version": "0.0.3",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/DAB0mB/angular-ecmascript.git"
8 | },
9 | "scripts": {
10 | "build": "babel src/ -d dist/ --source-maps",
11 | "build:watch": "babel src/ -d dist/ --source-maps --watch",
12 | "test": "xdg-open ./test/index.html",
13 | "test:build": "npm run build && webpack --config ./test/webpack.config.js",
14 | "test:build:watch": "npm run build:watch & webpack --config ./test/webpack.config.js --watch",
15 | "prepublish": "npm run build"
16 | },
17 | "peerDependencies": {
18 | "angular": "^1.5.0"
19 | },
20 | "devDependencies": {
21 | "angular": "^1.5.0",
22 | "babel-cli": "^6.8.0",
23 | "babel-core": "^6.7.6",
24 | "babel-loader": "^6.2.4",
25 | "babel-plugin-add-module-exports": "^0.1.2",
26 | "babel-preset-es2015": "^6.6.0",
27 | "babel-preset-stage-0": "^6.5.0",
28 | "jasmine-core": "^2.4.0",
29 | "webpack": "^1.13.0"
30 | },
31 | "keywords": [
32 | "angular",
33 | "ecmascript",
34 | "angular-ecmascript"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/src/module-helpers/config.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Config extends Injectable {
4 | configure() {
5 | throw Error('Config#configure() must be implemented');
6 | }
7 | }
--------------------------------------------------------------------------------
/src/module-helpers/controller.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 | import * as Utils from '../utils';
3 |
4 | export default class Controller extends Injectable {
5 | constructor(...args) {
6 | super(...args);
7 |
8 | const createViewModel = this.$scope &&
9 | (this.$scope.$viewModel || this.$scope.viewModel);
10 |
11 | if (Utils.isFunction(createViewModel)) {
12 | this.$scope::createViewModel(this);
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/module-helpers/decorator.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Decorator extends Injectable {
4 | decorate() {
5 | throw Error('Decorator#decorate() must be implemented');
6 | }
7 | }
--------------------------------------------------------------------------------
/src/module-helpers/directive.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Directive extends Injectable {
4 | compile() {
5 | return this.link.bind(this);
6 | }
7 | }
--------------------------------------------------------------------------------
/src/module-helpers/factory.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Factory extends Injectable {
4 | create() {
5 | throw Error('Factory#create() must be implemented');
6 | }
7 | }
--------------------------------------------------------------------------------
/src/module-helpers/filter.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Filter extends Injectable {
4 | filter() {
5 | throw Error('Filter#filter() must be implemented');
6 | }
7 | }
--------------------------------------------------------------------------------
/src/module-helpers/index.js:
--------------------------------------------------------------------------------
1 | export Injectable from './injectable';
2 | export Directive from './directive';
3 | export Controller from './controller';
4 | export Filter from './filter';
5 | export Config from './config';
6 | export Factory from './factory';
7 | export Service from './service';
8 | export Provider from './provider';
9 | export Runner from './runner';
10 | export Decorator from './decorator';
11 |
--------------------------------------------------------------------------------
/src/module-helpers/injectable.js:
--------------------------------------------------------------------------------
1 | export default class Injectable {
2 | constructor(...args) {
3 | this.constructor.$inject.forEach((name, i) => {
4 | this[name] = args[i];
5 | });
6 | }
7 | }
--------------------------------------------------------------------------------
/src/module-helpers/provider.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Provider extends Injectable {}
--------------------------------------------------------------------------------
/src/module-helpers/runner.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Runner extends Injectable {
4 | run() {
5 | throw Error('Runner#run() must be implemented');
6 | }
7 | }
--------------------------------------------------------------------------------
/src/module-helpers/service.js:
--------------------------------------------------------------------------------
1 | import Injectable from './injectable';
2 |
3 | export default class Service extends Injectable {}
--------------------------------------------------------------------------------
/src/module-loader.js:
--------------------------------------------------------------------------------
1 | import Angular from 'angular';
2 | import * as Helpers from './module-helpers';
3 | import * as Utils from './utils';
4 |
5 | export default class Loader {
6 | constructor(ngModule, dependencies) {
7 | if (Utils.isString(ngModule)) {
8 | ngModule = Angular.module(ngModule, dependencies);
9 | }
10 |
11 | this.module = ngModule;
12 | }
13 |
14 | load(Helper, ...args) {
15 | if (Utils.isFunction(Helper)) {
16 | const proto = Helper.prototype;
17 | Helper.$name = Helper.$name || Helper.name;
18 | Helper.$inject = Helper.$inject || [];
19 |
20 | if (proto instanceof Helpers.Provider)
21 | this.loadProvider(Helper);
22 | else if (proto instanceof Helpers.Service)
23 | this.loadService(Helper);
24 | else if (proto instanceof Helpers.Controller)
25 | this.loadController(Helper);
26 | else if (proto instanceof Helpers.Directive)
27 | this.loadDirective(Helper);
28 | else if (proto instanceof Helpers.Decorator)
29 | this.loadDecorator(Helper);
30 | else if (proto instanceof Helpers.Factory)
31 | this.loadFactory(Helper);
32 | else if (proto instanceof Helpers.Filter)
33 | this.loadFilter(Helper);
34 | else if (proto instanceof Helpers.Config)
35 | this.loadConfig(Helper);
36 | else if (proto instanceof Helpers.Runner)
37 | this.loadRunner(Helper);
38 | else
39 | throw Error('can\'t load unknown module-helper');
40 | }
41 | else if (Utils.isArray(Helper)) {
42 | Helper.forEach(Helper => this.load(Helper));
43 | }
44 | else if (Utils.isString(Helper)) {
45 | this.module[Helper](...args);
46 | }
47 | else {
48 | throw Error('`Helper` must be a function or a string');
49 | }
50 |
51 | return this;
52 | }
53 |
54 | loadProvider(Provider) {
55 | this.module.provider(Provider.$name, Provider);
56 | }
57 |
58 | loadService(Service) {
59 | this.module.service(Service.$name, Service)
60 | }
61 |
62 | loadController(Controller) {
63 | const $inject = Controller.$inject;
64 |
65 | if (!Utils.hasValue($inject, '$scope')) {
66 | $inject.unshift('$scope');
67 | }
68 |
69 | this.module.controller(Controller.$name, Controller);
70 | }
71 |
72 | loadDirective(Directive) {
73 | const helper = (...args) => {
74 | return new Directive(...args);
75 | }
76 |
77 | helper.$inject = Directive.$inject;
78 | this.module.directive(Directive.$name, helper);
79 | }
80 |
81 | loadDecorator(Decorator) {
82 | let decorator;
83 | const $inject = Decorator.$inject;
84 |
85 | const helper = (...args) => {
86 | decorator = new Decorator(...args);
87 | return decorate;
88 | }
89 |
90 | const decorate = (...args) => {
91 | decorator.decorate(...args);
92 | return decorator.$delegate;
93 | }
94 |
95 | if (!Utils.hasValue($inject, '$delegate')) {
96 | $inject.unshift('$delegate');
97 | }
98 |
99 | this.module.decorator(Decorator.$name, helper);
100 | }
101 |
102 | loadFactory(Factory) {
103 | const helper = (...args) => {
104 | const factory = new Factory(...args);
105 | return factory::factory.create;
106 | }
107 |
108 | helper.$inject = Factory.$inject;
109 | this.module.factory(Factory.$name, helper);
110 | }
111 |
112 | loadFilter(Filter) {
113 | const helper = (...args) => {
114 | const filter = new Filter(...args);
115 | return filter::filter.filter;
116 | }
117 |
118 | helper.$inject = Filter.$inject;
119 | this.module.filter(Filter.$name, helper);
120 | }
121 |
122 | loadConfig(Config) {
123 | const helper = (...args) => {
124 | const config = new Config(...args);
125 | return config.configure();
126 | }
127 |
128 | helper.$inject = Config.$inject;
129 | this.module.config(helper);
130 | }
131 |
132 | loadRunner(Runner) {
133 | const helper = (...args) => {
134 | const runner = new Runner(...args);
135 | return runner.run();
136 | }
137 |
138 | helper.$inject = Runner.$inject;
139 | this.module.run(helper);
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export function hasValue(obj, v) {
2 | return getValues(obj).indexOf(v) != -1;
3 | };
4 |
5 | export function hasKey(obj, k) {
6 | return getKeys(obj).indexOf(k) != -1;
7 | };
8 |
9 | export function getValues(obj) {
10 | return getKeys(obj).map(function(k) {
11 | return obj[k];
12 | });
13 | };
14 |
15 | export function getKeys(obj) {
16 | return Object.keys(obj);
17 | };
18 |
19 | export function isHash(obj) {
20 | return obj.__proto__ === Object.prototype;
21 | }
22 |
23 | export function isArray(arr) {
24 | return arr instanceof Array;
25 | }
26 |
27 | export function isString(str) {
28 | return typeof str == 'string';
29 | }
30 |
31 | export function isFunction(fn) {
32 | return typeof fn == 'function';
33 | }
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | angular-ecmascript test
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |