liron_hazan
liron_hazan

Reputation: 1546

Can I use an Angular es5 controller with a new es6 service using babel and grunt?

I want to start using ES6 (2015) in my Angular 1.3 + grunt stack without refactoring the entire existing ES5 code, (or switching grunt with other tool..) But when trying to use a new ES6 service from an "old" controller I'm getting following error,

" Cannot read property '1' of null at Function.annotate [as $$annotate] .... "

The babel configuration in grunt:

        babel: {
        options: {
            sourceMap: true,
            presets: ['es2015']
        },
        dist: {
            files: [{
                expand: true,
                cwd: '<%= yeoman.app %>',
                src: '**/*.es6.js',
                dest: '.tmp/app',
                ext: '.es5.js'
            }]
        },
        test: {
            files: [{
                expand: true,
                cwd: 'test/spec',
                src: '{,*/}*.es6.js',
                dest: '.tmp/spec',
                ext: '.es5.js'
            }]
        }
    },

The service code:

class InfoService {
    constructor($http) {
      this.$http = $http;
    }

    getInfo() {
        console.log('getting');
        return this.$http.get('/backend/something/readAll');
    }
}

InfoService.$inject = ['$http'];

angular.module('app').service('Info', $http => InfoService($http));

The use in es5 controller:

angular.module('app').controller('SomeCtrl', 
function ComposerCtrl(Info) {
    Info.getInfo();
});

The transpiled ES5 InfoService was generated under .tmp/app (and I configured grunt watch to update changes while developing) so I wonder what am I doing wrong..

Upvotes: 0

Views: 452

Answers (2)

liron_hazan
liron_hazan

Reputation: 1546

So I've found a way to make it work but I don't like it so much.. I configured the babel to dist the files at the same place my *.es6.js files and updated the index.html so Angular will load the es5 transpiled file (InfoService.js), and when I debug I do it on the es6 file (I guess it relates to the sourceMap)

        babel: {
        options: {
            sourceMap: true,
            presets: ['es2015']
        },
        dist: {
            files: [{
                expand: true,
                src: '**/*.es6.js',
                ext: '.js'
            }]
        },

Upvotes: 0

rzelek
rzelek

Reputation: 4013

You have forgotten about new:

...
angular.module('app').service('Info', $http => new InfoService($http))

In this case, angular will not benefit from $inject property and you will need to ng-annotate your code, as it solves to:

angular.module('app').service('Info', function($http) { return new InfoService($http); });

The simpler solution it to replace service definition with:

angular.module('app').service('Info', InfoService);

Angular's DI will use $inject property and add new operator for you.

It is worth noting, that TypeScript users had the same problem: How can I define an AngularJS service using a TypeScript class that doesn't pollute the global scope?

EDIT:

It is possible, that you are using wrong controller expression (for example unclosed ng-controller attribute:

 <div .... ng-controller="SignupFormController as signupFormCtrl> 

This messes up angular and leads to this error message on older versions of angular (1.3).

More info about issue: https://github.com/angular/angular.js/issues/10875

Upvotes: 1

Related Questions