2

I have 3 nested directives:

ExchangeWidget -> CoinInput (2 total) -> Coin (Several)

The code:

ExchangeWidgetDirective

'use strict';

import cointInputTpl from './exchangewidget.html';

function exchangeWidgetComponenet($log) {
    'ngInject';

  var directive = {
    restrict: 'E',
    templateUrl: cointInputTpl,
    controller: exchangeWidgetController,
    controllerAs: 'vm',
    bindToController: true,
    scope: {
      amount: '@',
      type: '@',
      address: '=',
      postSelect: '=',
      changeCounterPrice: '='
    }
  };

  return directive;

  function exchangeWidgetController ($scope, CHANGED_EVENT_SUFFIX) {
    'ngInject';
    let self = this;

    self.postSelect = function (newCoin, prevCoin, type) {
        // TODO: migrate to conf
        type = type === 'deposit' ? 'receive' : 'deposit';
        let eventData = {
          newCoin: newCoin,
          prevCoin: prevCoin,
        };
        let event = 'reset' + type;
        $scope.$broadcast(event, eventData);
    };

    self.changeCounterPrice = function (eventData) {
      let eventName = eventData.type + CHANGED_EVENT_SUFFIX;
      $scope.$broadcast(eventName, eventData);
    };

    self.showWithdrawAddress = function () {
      $scope.$parent.showWithdrawAddress = true;
    }

  }

}

export default exchangeWidgetComponenet;

CointInputDirective

'use strict';

import cointInputTpl from './coininput.html';
import _ from 'lodash/core';

function coinInputComponenet($log) {
    'ngInject';

  var directive = {
    restrict: 'E',
    templateUrl: cointInputTpl,
    controller: coinInputController,
    controllerAs: 'vm',
    bindToController: true,
    scope: {
      amount: '@',
      type: '@',
      selectedCoin: '@',
      showWithdrawAddress: '@',
      postSelect: '=',
      changeCounterPrice: '='
    }
  };

  return directive;

  function coinInputController ($scope, $element, $attrs,
                                Price, AMOUNT_CHANGE_DEBOUNCE, CHANGED_EVENT_SUFFIX) {
    'ngInject';

    let self = this;
    let hideCounterEvent = 'reset' + this.type;
    // TODO: refactor to configs
    let counterChangedEvent = this.type === 'deposit' ? 'receive' + CHANGED_EVENT_SUFFIX : 'deposit' + CHANGED_EVENT_SUFFIX;

      $log.debug('Hello from coinInput controller!');

      self.icons = [
      {name: 'BTC'},
      {name: 'LTC'},
      {name: 'ETH'}
    ];

      self.select = function (coinName) {
        let prevCoin = self.selectedCoin;
        self.selectedCoin = coinName;
        self.postSelect(coinName, prevCoin, self.type);
        $scope.$broadcast('reset')
    };

      $scope.$on(hideCounterEvent, function(event, eventData) {
        if (eventData.newCoin === this.selectedCoin) {
          self.select(eventData.prevCoin);
      }
    });

      $scope.$watch('amount', function(newAmount, oldAmount) {
        let eventData = {
        newAmount: newAmount,
        oldAmount: oldAmount,
        coin: self.selectedCoin,
        type: self.type
      };

      self.changeCounterPrice(eventData)
    });

      $scope.$on(counterChangedEvent, function (event, eventData) {
      _.debounce(function () {
        let pair = self.type === 'deposit' ? self.selectedCoin.toUpperCase() + eventData.coin.toUpperCase() :
          eventData.coin.toUpperCase() + self.selectedCoin.toUpperCase();

        Price.all(pair).all('latest').getList().then(function (priceList) {
          let basePrice = priceList[0].ask;
          $scope.amount = eventData.newAmount * basePrice;
        });
      }, AMOUNT_CHANGE_DEBOUNCE);
    });


  }

}

export default coinInputComponenet;

CoinDirective

'use strict';

import coinTpl from './coin.html';

function coinComponent($log) {
    'ngInject';

  var directive = {
    restrict: 'E',
    templateUrl: coinTpl,
    controller: coinController,
    controllerAs: 'vm',
    bindToController: true,
    scope: {
      selected: '@',
      expanded: '@',
      name: '@',
      iconSrc: '@',
      iconClass: '@',
      select: '='
    }
  };

  return directive;

  function coinController ($scope) {
    'ngInject';
    // TODO: migrate to conf
    let self = this;

      this.selectCurrent = function () {
        self.select(self.name);
      self.selected = true;
    };

    $scope.$on('reset', function() {
      self.selected = false;
    });

  }


}

export default coinComponent;

The error I get is: TypeError: self.changeCounterPrice is not a function at coininput.directive.js:68 at Scope.$digest (angular.js:18210) at Scope.$apply (angular.js:18480) at bootstrapApply (angular.js:1952) at Object.invoke (angular.js:5040) at doBootstrap (angular.js:1950) at bootstrap (angular.js:1970) at Object.angular.bootstrap (ocLazyLoad.js:760) at HTMLDocument.<anonymous> (index.bootstrap.js:18) at mightThrow (jquery.js:3583)

(The same for postSelect)

Stack trace: (anonymous) @ angular.js:14642 (anonymous) @ angular.js:11102 $digest @ angular.js:18228 $apply @ angular.js:18480 bootstrapApply @ angular.js:1952 invoke @ angular.js:5040 doBootstrap @ angular.js:1950 bootstrap @ angular.js:1970 angular.bootstrap @ ocLazyLoad.js:760 (anonymous) @ index.bootstrap.js:18 mightThrow @ jquery.js:3583 process @ jquery.js:3651 setTimeout (async)
(anonymous) @ jquery.js:3689 fire @ jquery.js:3317 fireWith @ jquery.js:3447 fire @ jquery.js:3455 fire @ jquery.js:3317 fireWith @ jquery.js:3447 ready @ jquery.js:3920 completed @ jquery.js:3930

What is super weird, is that for select works just fine! (two way bound function, by reference from the isolated scope of coinInputDirective to coinDirective)

Faisal
  • 27,816
  • 7
  • 82
  • 93
Oleg Belousov
  • 9,633
  • 12
  • 68
  • 119
  • Bindings in Angular v1.6 get attached to a controller after it's been instantiated. So one of possible reasons to cause the issue is that your `changeCounterPrice` is somehow invoked before the `coinInputController` gets instantiated. I would be easier to tell with a plunker replicating the issue. – xReeQz Aug 13 '17 at 21:15

0 Answers0