├── .gitignore
├── api
├── images
│ ├── cat.png
│ ├── cheese.png
│ ├── durian.png
│ ├── jumper.png
│ ├── bearing.png
│ ├── cat-thumb.png
│ ├── football.png
│ ├── toaster.png
│ ├── tapemeasure.png
│ ├── bearing-thumb.png
│ ├── cheese-thumb.png
│ ├── durian-thumb.png
│ ├── football-thumb.png
│ ├── jumper-thumb.png
│ ├── toaster-thumb.png
│ └── tapemeasure-thumb.png
└── items.json
├── src
├── app
│ ├── directives
│ │ ├── item.tpl.html
│ │ ├── itemDisplay.js
│ │ ├── itemSelector.js
│ │ ├── selectable.js
│ │ ├── stateDisplay.ts
│ │ └── item.js
│ ├── app.js
│ ├── controllers
│ │ └── appController.js
│ ├── services
│ │ ├── thingFactory.js
│ │ ├── itemsService.js
│ │ └── statesProvider.js
│ └── utils
│ │ └── register.js
├── index.html
└── styles
│ └── main.less
├── .gitattributes
├── bower.json
├── package.json
├── gulpfile.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | bower_components
3 | node_modules
4 | assets-source
--------------------------------------------------------------------------------
/api/images/cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/cat.png
--------------------------------------------------------------------------------
/src/app/directives/item.tpl.html:
--------------------------------------------------------------------------------
1 |
![]()
--------------------------------------------------------------------------------
/api/images/cheese.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/cheese.png
--------------------------------------------------------------------------------
/api/images/durian.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/durian.png
--------------------------------------------------------------------------------
/api/images/jumper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/jumper.png
--------------------------------------------------------------------------------
/api/images/bearing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/bearing.png
--------------------------------------------------------------------------------
/api/images/cat-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/cat-thumb.png
--------------------------------------------------------------------------------
/api/images/football.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/football.png
--------------------------------------------------------------------------------
/api/images/toaster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/toaster.png
--------------------------------------------------------------------------------
/api/images/tapemeasure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/tapemeasure.png
--------------------------------------------------------------------------------
/api/images/bearing-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/bearing-thumb.png
--------------------------------------------------------------------------------
/api/images/cheese-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/cheese-thumb.png
--------------------------------------------------------------------------------
/api/images/durian-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/durian-thumb.png
--------------------------------------------------------------------------------
/api/images/football-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/football-thumb.png
--------------------------------------------------------------------------------
/api/images/jumper-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/jumper-thumb.png
--------------------------------------------------------------------------------
/api/images/toaster-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/toaster-thumb.png
--------------------------------------------------------------------------------
/api/images/tapemeasure-thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/angular-es6/master/api/images/tapemeasure-thumb.png
--------------------------------------------------------------------------------
/src/app/app.js:
--------------------------------------------------------------------------------
1 |
2 | var app = angular.module('app', []);
3 |
4 | app.constant('config', {
5 | apiUrl: '../api/'
6 | });
7 |
8 |
9 | app.config((statesProvider) => {
10 | statesProvider.setPrefix('You are feeling');
11 | });
12 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-es6",
3 | "version": "0.0.0",
4 | "homepage": "https://github.com/michaelbromley/angular-es6",
5 | "authors": [
6 | "Michael Bromley "
7 | ],
8 | "moduleType": [
9 | "globals"
10 | ],
11 | "keywords": [
12 | "angular",
13 | "es6"
14 | ],
15 | "license": "MIT",
16 | "ignore": [
17 | "**/.*",
18 | "node_modules",
19 | "bower_components",
20 | "test",
21 | "tests"
22 | ],
23 | "dependencies": {
24 | "angular": "~1.3.9",
25 | "requirejs": "~2.1.15"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/controllers/appController.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The one and only controller used in this app.
3 | */
4 | class AppController {
5 |
6 | /*@ngInject*/
7 | constructor($scope, itemsService, thingFactory) {
8 | this.items = [];
9 | this.selection = [];
10 |
11 | itemsService.getItems().then( result => this.items = result );
12 |
13 | $scope.$watch('vm.items', () => {
14 | this.selection = this.items.filter(item => item.selected);
15 | }, true);
16 |
17 | this.makeThing = () => { thingFactory.newThing() };
18 |
19 |
20 | this.$inject = ['$scope', 'itemService', 'Thing'];
21 | }
22 |
23 | }
24 |
25 | register('app').controller('AppController', AppController);
--------------------------------------------------------------------------------
/src/app/directives/itemDisplay.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is an example of a "component" directive which encapsulates a template.
3 | */
4 | class ItemDisplayDirective {
5 |
6 | constructor() {
7 | this.template = '
';
8 | this.restrict = 'E';
9 | this.replace = true;
10 | this.scope = {
11 | collection: '=',
12 | start: '='
13 | }
14 | }
15 |
16 | link(scope) {
17 |
18 | scope.$watch('start', (value) => {
19 | if (value) {
20 | scope.items = scope.collection;
21 | }
22 | });
23 | }
24 | }
25 |
26 | register('app').directive('itemDisplay', ItemDisplayDirective);
--------------------------------------------------------------------------------
/src/app/services/thingFactory.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A thing is an object which will be instantiated and returned by the ThingFactory
3 | */
4 | class Thing {
5 |
6 | constructor() {
7 | var time = new Date().getTime();
8 | console.log(`Created a new Thing at ${time}!`);
9 |
10 | this.explode();
11 | }
12 |
13 | explode() {
14 | console.log('BOOM!');
15 | }
16 |
17 | }
18 |
19 | /**
20 | * The ThingFactory class creates new things
21 | */
22 | class ThingFactory {
23 |
24 | /*@ngInject*/
25 | constructor($timeout) {
26 | this.$timeout = $timeout;
27 | }
28 |
29 | newThing() {
30 | console.log('Getting a new Thing...');
31 | return this.$timeout(() => new Thing(), 100);
32 | }
33 | }
34 |
35 | register('app').factory('thingFactory', ThingFactory);
--------------------------------------------------------------------------------
/src/app/services/itemsService.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Provides access to the JSON endpoint which contains the data about the items.
3 | */
4 | class ItemsService {
5 |
6 | /*@ngInject*/
7 | constructor($http, config) {
8 | this.$http = $http;
9 | this.config = config;
10 | }
11 |
12 | getItems() {
13 | var apiUrl = this.config.apiUrl;
14 |
15 | return this.$http
16 | .get(apiUrl + 'items')
17 | .then((result) => {
18 | // prepend the API url to the images
19 | return result.data.map((item) => {
20 |
21 | item.image = apiUrl + item.image;
22 | item.thumb = apiUrl + item.thumb;
23 | return item;
24 |
25 | });
26 | });
27 | }
28 |
29 | }
30 |
31 | register('app').service('itemsService', ItemsService);
32 |
--------------------------------------------------------------------------------
/src/app/directives/itemSelector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is an example of a "component" directive which encapsulates a template.
3 | */
4 | class ItemSelectorDirective {
5 |
6 | /*@ngInject*/
7 | constructor($timeout) {
8 | this.template = 'Which ones please you the most?
' +
9 | '- ' +
10 | '
' +
11 | '{{ item.name }}' +
12 | '
';
13 | this.restrict = 'E';
14 | this.replace = true;
15 | this.scope = {
16 | collection: '='
17 | };
18 | this.$timeout = $timeout;
19 | }
20 | }
21 |
22 | register('app').directive('itemSelector', ItemSelectorDirective);
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-es6",
3 | "version": "1.0.0",
4 | "description": "An experiment in using ES6 classes with AngularJS",
5 | "main": "gulpfile.js",
6 | "dependencies": {
7 | "event-stream": "^3.2.1",
8 | "gulp": "^3.8.10",
9 | "gulp-less": "^2.0.1",
10 | "gulp-inject": "^1.1.1",
11 | "gulp-6to5": "^2.0.2",
12 | "gulp-ng-annotate": "^0.5.0",
13 | "gulp-typescript": "^2.4.1"
14 | },
15 | "devDependencies": {},
16 | "scripts": {
17 | "test": "echo \"Error: no test specified\" && exit 1"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/michaelbromley/angular-es6.git"
22 | },
23 | "keywords": [
24 | "angularjs",
25 | "es6"
26 | ],
27 | "author": "Michael Bromley ",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/michaelbromley/angular-es6/issues"
31 | },
32 | "homepage": "https://github.com/michaelbromley/angular-es6"
33 | }
34 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | AngularJS & ES6 Demo: A Pleasing Array of Items
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | A Pleasing Array of Items
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/api/items.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name" : "Cheese",
4 | "image" : "images/cheese.png",
5 | "thumb" : "images/cheese-thumb.png"
6 | },
7 | {
8 | "name" : "Jumpers",
9 | "image" : "images/jumper.png",
10 | "thumb" : "images/jumper-thumb.png"
11 | },
12 | {
13 | "name" : "Cats",
14 | "image" : "images/cat.png",
15 | "thumb" : "images/cat-thumb.png"
16 | },
17 | {
18 | "name" : "Toasters",
19 | "image" : "images/toaster.png",
20 | "thumb" : "images/toaster-thumb.png"
21 | },
22 | {
23 | "name" : "Durians",
24 | "image" : "images/durian.png",
25 | "thumb" : "images/durian-thumb.png"
26 | },
27 | {
28 | "name" : "Tape Measures",
29 | "image" : "images/tapemeasure.png",
30 | "thumb" : "images/tapemeasure-thumb.png"
31 | },
32 | {
33 | "name" : "Footballs",
34 | "image" : "images/football.png",
35 | "thumb" : "images/football-thumb.png"
36 | },
37 | {
38 | "name" : "Bearings",
39 | "image" : "images/bearing.png",
40 | "thumb" : "images/bearing-thumb.png"
41 | }
42 | ]
--------------------------------------------------------------------------------
/src/app/directives/selectable.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This is an example of a "behavioural" directive that has no template, but decorates the behaviour of the
3 | * element which it is attached to.
4 | *
5 | * The directive makes an element "selectable", which means when it is clicked, the class `selected` will be added to the element, and if a
6 | * object is supplied as the value of the "selectable" attribute, the directive will attempt to add a property `selected = true` to that
7 | * object.
8 | */
9 | class Selectable {
10 |
11 | constructor() {
12 | this.restrict = 'A';
13 | }
14 |
15 | compile(tElement) {
16 | tElement.addClass('selectable');
17 | }
18 |
19 | link(scope, element, attrs) {
20 | var model = scope.$eval(attrs.selectable) || {};
21 |
22 | element.on('click', () => {
23 | scope.$apply(() => {
24 | element.toggleClass('selected');
25 | model.selected = !model.selected;
26 | });
27 | });
28 | }
29 |
30 | }
31 |
32 | register('app').directive('selectable', Selectable);
--------------------------------------------------------------------------------
/src/app/directives/stateDisplay.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A directive class written in TypeScript.
3 | */
4 | class StateDisplayDirective {
5 |
6 | private template;
7 | private restrict;
8 | private replace;
9 | private scope;
10 | private $interval;
11 | private states;
12 |
13 | /*@ngInject*/
14 | constructor($interval, states) {
15 | this.template = '{{ prefix }} {{ currentState }}
';
16 | this.restrict = 'E';
17 | this.replace = true;
18 | this.scope = {
19 | start: '='
20 | };
21 |
22 | this.$interval = $interval;
23 | this.states = states;
24 | }
25 |
26 | link(scope) {
27 |
28 | scope.prefix = this.states.getPrefix();
29 | scope.currentState = this.states.getNextState();
30 |
31 | scope.$watch('start', (val) => {
32 | if (val) {
33 | this.$interval(() => scope.currentState = this.states.getNextState(), 3500);
34 | }
35 | });
36 |
37 | }
38 | }
39 |
40 |
41 | // Declare the register function in order to avoid TypeScript compiler errors.
42 | declare var register : any;
43 |
44 | register('app').directive('stateDisplay', StateDisplayDirective);
--------------------------------------------------------------------------------
/src/styles/main.less:
--------------------------------------------------------------------------------
1 | html {
2 | overflow: hidden;
3 | font-family: "Helvetic Neue", Helvetica, Arial, sans-serif;
4 | text-align: center;
5 | }
6 |
7 | .items-selector {
8 |
9 | width: 650px;
10 | margin: auto;
11 |
12 | ul li {
13 | list-style-type: none;
14 | display: inline-block;
15 | width: 120px;
16 | text-align: center;
17 | border: 1px solid #eee;
18 | margin: 5px;
19 | padding: 5px;
20 | transition: border-color 0.3s;
21 | font-size: 14px;
22 |
23 | &:hover {
24 | border-color: #999;
25 | }
26 |
27 | img {
28 | display: block;
29 | margin: auto;
30 | }
31 | }
32 | }
33 |
34 | .selectable {
35 | cursor: pointer;
36 |
37 | &.selected {
38 | font-weight: bold;
39 | border-color: red;
40 | }
41 | }
42 |
43 | button.show-me {
44 | font-size: 24px;
45 | padding: 10px;
46 | border-radius: 8px;
47 | }
48 |
49 | .item {
50 | transition: all 1s;
51 | }
52 |
53 | button.thing-button {
54 | position: absolute;
55 | bottom: 30px;
56 | left: 30px;
57 | border-radius: 3px;
58 | z-index: 50;
59 | }
60 |
61 | .footer {
62 | position: absolute;
63 | bottom: 30px;
64 | display: block;
65 | width: 100%;
66 | }
67 |
68 | .state-label {
69 | position: relative;
70 | z-index: 10;
71 | font-size: 64px;
72 | color: darkgreen;
73 | background-color: rgba(255,255,255,0.7);
74 | }
--------------------------------------------------------------------------------
/src/app/directives/item.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A fairly fully-featured directive, including an external template file, dependencies, compile & link, isolate scope.
3 | */
4 | class ItemDirective {
5 |
6 | /*@ngInject*/
7 | constructor($interval, $timeout) {
8 | this.templateUrl = 'app/directives/item.tpl.html';
9 | this.restrict = 'E';
10 | this.replace = true;
11 | this.scope = {
12 | model: '='
13 | };
14 |
15 | this.$interval = $interval;
16 | this.$timeout = $timeout;
17 | }
18 |
19 | compile(tElement) {
20 |
21 | tElement.css('position', 'absolute');
22 | tElement.css('opacity', '0');
23 | tElement.css('left', (window.innerWidth / 2 - 150) + 'px');
24 | tElement.css('top', (window.innerHeight / 2 - 150) + 'px');
25 |
26 | }
27 |
28 | link(scope, element) {
29 |
30 | var interval = Math.random() * 500 + 800;
31 | this.$timeout(() => element.css('opacity', '1'), 500);
32 | this.$interval(() => this.move(element), interval);
33 |
34 | }
35 |
36 | move(element) {
37 | var newPos = this.getNewPosition();
38 | element.css('left', (newPos.x - 150) + 'px');
39 | element.css('top', (newPos.y - 150) + 'px');
40 | }
41 |
42 | getNewPosition() {
43 | var width = window.innerWidth,
44 | height = window.innerHeight;
45 |
46 | return {
47 | x: Math.random() * width,
48 | y: Math.random() * height
49 | };
50 | }
51 | }
52 |
53 | register('app').directive('item', ItemDirective);
--------------------------------------------------------------------------------
/src/app/services/statesProvider.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Example of a provider which can be configured at runtime in a `config()` block with the `setPrefix()` method.
3 | */
4 | class StatesProvider {
5 |
6 | constructor() {
7 | this.prefix = "You are";
8 | this.states = [
9 | 'okay',
10 | 'not too bad',
11 | 'contented',
12 | 'quite satisfied',
13 | 'moderately gratified',
14 | 'well chuffed',
15 | 'highly pleased',
16 | 'highly pleased indeed'
17 | ];
18 | }
19 |
20 | /**
21 | * This method allows the prefix value to be configured at runtime.
22 | * @param value
23 | */
24 | setPrefix(value) {
25 | this.prefix = value;
26 | }
27 |
28 | /**
29 | * The `$get` method is a requirement of the Angular provider registration API, and is a factory function that
30 | * returns our service object.
31 | *
32 | * @returns {{getNextState: Function, getPrefix: Function}}
33 | */
34 | /*@ngInject*/
35 | $get($timeout) {
36 | var index = 0;
37 | $timeout(() => console.log('This is the statesProvider $get method being invoked.'), 100);
38 |
39 | return {
40 | getNextState: () => {
41 | var currentState;
42 |
43 | if (index < this.states.length) {
44 | currentState = this.states[index];
45 | } else {
46 | currentState = this.states[this.states.length - 1];
47 | }
48 |
49 | index ++;
50 | return currentState;
51 | },
52 | getPrefix: () => this.prefix
53 | };
54 | }
55 | }
56 |
57 | register('app').provider('states', StatesProvider);
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var inject = require('gulp-inject');
3 | var to5 = require('gulp-6to5');
4 | var annotate = require('gulp-ng-annotate');
5 | var less = require('gulp-less');
6 | var ts = require('gulp-typescript');
7 | var es = require('event-stream');
8 |
9 | gulp.task('scripts', function () {
10 | var javascripts = gulp.src('./src/**/*.js')
11 | .pipe(to5());
12 |
13 | var typescripts = gulp.src('./src/**/*.ts')
14 | .pipe(ts({
15 | target: 'ES5'
16 | }));
17 |
18 | es.merge(typescripts.js, javascripts)
19 | .pipe(annotate())
20 | .pipe(gulp.dest('./build'));
21 | });
22 |
23 | gulp.task('styles', function() {
24 | return gulp.src('./src/**/*.less')
25 | .pipe(less())
26 | .pipe(gulp.dest('./build'));
27 | });
28 |
29 | gulp.task('templates', function() {
30 | return gulp.src('./src/**/*.tpl.html')
31 | .pipe(gulp.dest('./build'));
32 | });
33 |
34 | gulp.task('index', ['scripts', 'styles', 'templates'], function() {
35 |
36 | var target = gulp.src('./src/index.html');
37 |
38 | var js = gulp.src([
39 | '../bower_components/angular/angular.js',
40 | 'app/utils/register.js',
41 | 'app/app.js',
42 | '**/!(app.js)'
43 | ], {read: false, cwd: './build/'});
44 |
45 | var css = gulp.src([
46 | 'styles/main.css'
47 | ], {read: false, cwd: './build/'});
48 |
49 | target
50 | .pipe(inject(js, { addRootSlash: false }))
51 | .pipe(inject(css, { addRootSlash: false }))
52 | .pipe(gulp.dest('./build'));
53 | });
54 |
55 | gulp.task('watch', ['index'], function () {
56 |
57 | gulp.watch(['./src/**/*.js', './src/**/*.ts'], ['scripts']);
58 | gulp.watch('./src/**/*.less', ['styles']);
59 | gulp.watch('./src/**/*.tpl.html', ['templates']);
60 | gulp.watch('./src/index.html', ['index']);
61 |
62 | });
63 |
64 |
65 | gulp.task('default', ['index']);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular ES6
2 |
3 | An example approach to using ES6 classes in an AngularJS 1.x app.
4 |
5 | **Please see the article [Exploring ES6 Classes In AngularJS 1.x](http://www.michaelbromley.co.uk/blog/350/exploring-es6-classes-in-angularjs-1-x) for
6 | a full explanation.**
7 |
8 | **Working demo [here](http://www.michaelbromley.co.uk/experiments/angular-es6-demo/build/)**
9 |
10 | ## register.js
11 |
12 | The style of class definition you see below is enabled by including the file [register.js](src/app/utils/register.js) in the project, which exposes the global function `register`
13 |
14 | The API is as follows:
15 |
16 | ```JavaScript
17 | class MyAngularComponent {
18 | // ...
19 | }
20 |
21 | register(appName)
22 | .controller('MyController', MyAngularComponent)
23 | .service('myService', MyAngularComponent)
24 | .provider('myOtherService', MyAngularComponent)
25 | .factory('myFactory', MyAngularComponent)
26 | .directive('myDirective', MyAngularComponent);
27 | ```
28 |
29 | ## Example Component Classes
30 |
31 | ### Service
32 |
33 | ```JavaScript
34 | class UserService {
35 | /*@ngInject*/
36 | constructor($http) {
37 | this.$http = $http;
38 | }
39 | getFullName() {
40 | return this.$http.get('api/user/details');
41 | }
42 | }
43 |
44 | register('app').service('userService', UserService);
45 | ```
46 |
47 | ### Controller
48 |
49 | ```JavaScript
50 | class MyController {
51 | /*@ngInject*/
52 | constructor(userService) {
53 | userService.getFullName()
54 | .then(result => this.userName = result.fullName);
55 | }
56 | }
57 |
58 | register('app').controller('MyController', MyController);
59 | ```
60 |
61 | ### Factory
62 |
63 | ```JavaScript
64 | class ThingFactory {
65 | /*@ngInject*/
66 | constructor($timeout) {
67 | this.$timeout = $timeout;
68 | }
69 | newThing() {
70 | console.log('Getting a new Thing...');
71 | return this.$timeout(() => new Thing(), 1000);
72 | }
73 | }
74 |
75 | register('app').factory('thingFactory', ThingFactory);
76 | ```
77 |
78 | ### Directive
79 |
80 | ```JavaScript
81 | class MyDirective {
82 | /*@ngInject*/
83 | constructor($interval) {
84 | this.template = 'I\'m a directive!
';
85 | this.restrict = 'E';
86 | this.scope = {}
87 | // etc. for the usual config options
88 |
89 | // allows us to use the injected dependencies
90 | // elsewhere in the directive (e.g. compile or link function)
91 | this.$interval = $interval;
92 | }
93 |
94 | // optional compile function
95 | compile(tElement) {
96 | tElement.css('position', 'absolute');
97 | }
98 |
99 | // optional link function
100 | link(scope, element) {
101 | this.$interval(() => this.move(element), 1000);
102 | }
103 |
104 | move(element) {
105 | element.css('left', (Math.random() * 500) + 'px');
106 | element.css('top', (Math.random() * 500) + 'px');
107 | }
108 | }
109 |
110 | register('app').directive('myDirective', MyDirective);
111 | ```
112 |
113 | ### Provider
114 |
115 | ```JavaScript
116 | class ThingServiceProvider {
117 | constructor() {
118 | this.apiPath = 'default/api';
119 | }
120 | setApiPath(value) {
121 | this.apiPath = value;
122 | }
123 | /*@ngInject*/
124 | $get($http) {
125 | return {
126 | getThings: () => $http.get(this.apiPath)
127 | };
128 | }
129 | }
130 |
131 | register('app').provider('thingService', ThingServiceProvider);
132 | ```
133 |
134 | ## Build
135 |
136 | Clone this repo and then `npm install` and `bower install` to download the required dependencies.
137 |
138 | Then `gulp watch` and start hacking!
139 |
140 | ## License
141 |
142 | MIT
143 |
--------------------------------------------------------------------------------
/src/app/utils/register.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A helper class to simplify registering Angular components and provide a consistent syntax for doing so.
3 | */
4 | function register(appName) {
5 |
6 | var app = angular.module(appName);
7 |
8 | return {
9 | directive: directive,
10 | controller: controller,
11 | service: service,
12 | provider: provider,
13 | factory: factory
14 | };
15 |
16 | function directive(name, constructorFn) {
17 |
18 | constructorFn = _normalizeConstructor(constructorFn);
19 |
20 | if (!constructorFn.prototype.compile) {
21 | // create an empty compile function if none was defined.
22 | constructorFn.prototype.compile = () => {};
23 | }
24 |
25 | var originalCompileFn = _cloneFunction(constructorFn.prototype.compile);
26 |
27 | // Decorate the compile method to automatically return the link method (if it exists)
28 | // and bind it to the context of the constructor (so `this` works correctly).
29 | // This gets around the problem of a non-lexical "this" which occurs when the directive class itself
30 | // returns `this.link` from within the compile function.
31 | _override(constructorFn.prototype, 'compile', function () {
32 | return function () {
33 | originalCompileFn.apply(this, arguments);
34 |
35 | if (constructorFn.prototype.link) {
36 | return constructorFn.prototype.link.bind(this);
37 | }
38 | };
39 | });
40 |
41 | var factoryArray = _createFactoryArray(constructorFn);
42 |
43 | app.directive(name, factoryArray);
44 | return this;
45 | }
46 |
47 | function controller(name, contructorFn) {
48 | app.controller(name, contructorFn);
49 | return this;
50 | }
51 |
52 | function service(name, contructorFn) {
53 | app.service(name, contructorFn);
54 | return this;
55 | }
56 |
57 | function provider(name, constructorFn) {
58 | app.provider(name, constructorFn);
59 | return this;
60 | }
61 |
62 | function factory(name, constructorFn) {
63 | constructorFn = _normalizeConstructor(constructorFn);
64 | var factoryArray = _createFactoryArray(constructorFn);
65 | app.factory(name, factoryArray);
66 | return this;
67 | }
68 |
69 | /**
70 | * If the constructorFn is an array of type ['dep1', 'dep2', ..., constructor() {}]
71 | * we need to pull out the array of dependencies and add it as an $inject property of the
72 | * actual constructor function.
73 | * @param input
74 | * @returns {*}
75 | * @private
76 | */
77 | function _normalizeConstructor(input) {
78 | var constructorFn;
79 |
80 | if (input.constructor === Array) {
81 | //
82 | var injected = input.slice(0, input.length - 1);
83 | constructorFn = input[input.length - 1];
84 | constructorFn.$inject = injected;
85 | } else {
86 | constructorFn = input;
87 | }
88 |
89 | return constructorFn;
90 | }
91 |
92 | /**
93 | * Convert a constructor function into a factory function which returns a new instance of that
94 | * constructor, with the correct dependencies automatically injected as arguments.
95 | *
96 | * In order to inject the dependencies, they must be attached to the constructor function with the
97 | * `$inject` property annotation.
98 | *
99 | * @param constructorFn
100 | * @returns {Array.}
101 | * @private
102 | */
103 | function _createFactoryArray(constructorFn) {
104 | // get the array of dependencies that are needed by this component (as contained in the `$inject` array)
105 | var args = constructorFn.$inject || [];
106 | var factoryArray = args.slice(); // create a copy of the array
107 | // The factoryArray uses Angular's array notation whereby each element of the array is the name of a
108 | // dependency, and the final item is the factory function itself.
109 | factoryArray.push((...args) => {
110 | //return new constructorFn(...args);
111 | var instance = new constructorFn(...args);
112 | for (var key in instance) {
113 | instance[key] = instance[key];
114 | }
115 | return instance;
116 | });
117 |
118 | return factoryArray;
119 | }
120 |
121 | /**
122 | * Clone a function
123 | * @param original
124 | * @returns {Function}
125 | */
126 | function _cloneFunction(original) {
127 | return function() {
128 | return original.apply(this, arguments);
129 | };
130 | }
131 |
132 | /**
133 | * Override an object's method with a new one specified by `callback`.
134 | * @param object
135 | * @param methodName
136 | * @param callback
137 | */
138 | function _override(object, methodName, callback) {
139 | object[methodName] = callback(object[methodName])
140 | }
141 |
142 | }
--------------------------------------------------------------------------------