-1

I think i have a scope problem with js. Please take a look to my code below. This is my AngularJS example in es6. I compile the code to es5 with grunt browserify.

If i call my example i got the error: TypeError: this.gatewayServiceGet is not a function at ChainsDirective.loadChains [as chainsServiceLoadChains]

I check it and find out that this in loadChains is not the same this than in the constructor.

What can i do?

This is my app.js

'use strict';

import AppController from './appController.js';
import ChainsDirective from './components/chains/chains.directive.js';
import ChainsService from './components/chains/chains.service.js';
import GatewayService from './components/common/gateway/gateway.service.js';

angular
    .module('SalesCockpit', ['ui.router', 'ui.grid'])
    .config($stateProvider => {
        $stateProvider
            .state('chains', {
                url: '/chains',
                templateUrl: 'components/chains/chains.html'
            })
            .state('chainDetail', {
                url: '/chain/{chainId:int}/detail',
                templateUrl: 'components/chain-detail/chain-detail.html'
            })
        ;

    })
    .controller('AppController', AppController)
    .service('chainsService', ChainsService)
    .service('gatewayService', GatewayService)
    .directive('chains', ChainsDirective);

This is my chain directive

export default function ChainsDirective() {
    class ChainsDirective {

        /*@ngInject*/
        constructor(chainsService, $state) {
            this.chainsServiceLoadChains = chainsService.loadChains;
            this.gridOptions = {
                enableColumnMenus: false,
                columnDefs: [
                    {
                        name: 'id',
                        visible: false
                    },
                    {
                        name: 'name',
                        displayName: 'Kette',
                        cellTemplate: '<div class="ui-grid-cell-contents"><a ng-click="grid.appScope.openDetail(row.entity.id)">{{row.entity.name}}</a></div>'
                    }
                ]
            };
            this.$stateGo = $state.go;
            this.fetch();
        }

        /**
         * @param int chainId
         */
        openDetail(chainId) {
            this.$stateGo('chainDetail', {chainId})
        }

        fetch() {
            return this.chainsServiceLoadChains().then(data => {
                this.gridOptions.data = data
            })
        }
    }

    return {
        restrict: 'E',
        template: '<div id="chains" ui-grid="gridOptions" external-scopes="$scope" class="grid"></div>',
        controller: ChainsDirective,
        controllerAs: 'chains'
    }
}

This is my chain servie

export default class ChainsService {

    /*@ngInject*/
    constructor(gatewayService) {
        this.gatewayServiceGet = gatewayService.get;
    }

    /**
     * @returns Promise
     */
    loadChains() {
        return this.gatewayServiceGet('loadChains');
    }
}
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • I assume the code is being cross compiled and not ran in something like Edge. Can you post the translated code? I'm particularly interested in how the class gets translated. – Tahsis Claus Oct 20 '15 at 15:08

1 Answers1

1

FWIW, this has nothing to do with ECMAScript 2015. JavaScript always worked that way.


The value of this depends on how the function is called. So if you call it as

this.chainsServiceLoadChains()

this inside chainsServiceLoadChains will refer to what is before the ., which is this that refers to the ChainsDirective instance.

One solution would be to bind the this value of the function to a specific value:

this.chainsServiceLoadChains = chainsService.loadChains.bind(chainsService);

Now it doesn't matter anymore how the function is called, this will always refer to chainsService.

Learn more about this:

Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143