最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to invoke multiple functionschained calls using ng-click - Stack Overflow

programmeradmin2浏览0评论

Hi I have a button that fires off two functions like so. Is there a way for the updateDetails to run only after changeColor has finished? The updateDetails is currently pulling an old configuration with the old color, and not the changed color.

ng-click="changeColor(color.code); updateDetails()"

Here is the view:

<div ng-controller="ColorsCtrl">
  <div ng-controller="HeaderCtrl">
    <div byo-header>
    </div>

    <div ng-repeat="color in colors" class="row">
      <div class="small-12 columns">
            <div ng-controller="ButtonsController">
                <div class="button-group">
              {{ isSelected(color.code) }}
                <button ng-click="changeColor(color.code); updateDetails()" type="radio" class="button" ng-model="selectedColor" btn-radio="color.code">{{color.name}}</button>
            </div>
            </div>  
      </div>
    </div>
    <br/>
    <br/>
    <div class="row">
        <div class="small-4 small-offset-4 columns">
        <!-- Proceed to packages if available -->
            <a ng-if="hasPackage" href="#/packages/{{modelCode}}" class="button">Packages</a>
        <!-- Proceed to options if packages not available -->
        <a ng-if="!hasPackage" href="#/options/{{modelCode}}" class="button">Options</a>
        </div>
    </div>    
  </div>
</div>

Here is changeColor() in the ColorsCtrl:

$scope.changeColor = function(newColorCode){
        //swap previous selected color with new
        configuratorService.addOption($scope.modelCode, newColorCode, function(configuration){
            $scope.configuration = configuration;               
        });

    }

  }

Here is updateDetails in the HeaderCtrl

    $scope.updateDetails = function(){
        $scope.summaryDetails = getSummaryDetails();
    }

Hi I have a button that fires off two functions like so. Is there a way for the updateDetails to run only after changeColor has finished? The updateDetails is currently pulling an old configuration with the old color, and not the changed color.

ng-click="changeColor(color.code); updateDetails()"

Here is the view:

<div ng-controller="ColorsCtrl">
  <div ng-controller="HeaderCtrl">
    <div byo-header>
    </div>

    <div ng-repeat="color in colors" class="row">
      <div class="small-12 columns">
            <div ng-controller="ButtonsController">
                <div class="button-group">
              {{ isSelected(color.code) }}
                <button ng-click="changeColor(color.code); updateDetails()" type="radio" class="button" ng-model="selectedColor" btn-radio="color.code">{{color.name}}</button>
            </div>
            </div>  
      </div>
    </div>
    <br/>
    <br/>
    <div class="row">
        <div class="small-4 small-offset-4 columns">
        <!-- Proceed to packages if available -->
            <a ng-if="hasPackage" href="#/packages/{{modelCode}}" class="button">Packages</a>
        <!-- Proceed to options if packages not available -->
        <a ng-if="!hasPackage" href="#/options/{{modelCode}}" class="button">Options</a>
        </div>
    </div>    
  </div>
</div>

Here is changeColor() in the ColorsCtrl:

$scope.changeColor = function(newColorCode){
        //swap previous selected color with new
        configuratorService.addOption($scope.modelCode, newColorCode, function(configuration){
            $scope.configuration = configuration;               
        });

    }

  }

Here is updateDetails in the HeaderCtrl

    $scope.updateDetails = function(){
        $scope.summaryDetails = getSummaryDetails();
    }
Share Improve this question edited Jan 3, 2015 at 19:03 PSL 124k21 gold badges256 silver badges243 bronze badges asked Dec 30, 2014 at 21:44 user3562751user3562751 2631 gold badge5 silver badges15 bronze badges 3
  • Technically you could write inline if your call returns a promise, what operation are you doing inside configuratorService.addOption ? – PSL Commented Dec 30, 2014 at 22:11
  • @PSL I am doing a put – user3562751 Commented Dec 30, 2014 at 22:19
  • Instead of passing a callback if you return a promise from your service you could write it inline in the view. I can show an exmaple if you want. – PSL Commented Dec 30, 2014 at 22:21
Add a ment  | 

2 Answers 2

Reset to default 6

Since changeColor is an async operation - no, you can't wait inline. You'd have to call the function in the callback:

$scope.changeColor = function(newColorCode){
    //swap previous selected color with new
    configuratorService.addOption($scope.modelCode, newColorCode, function(configuration){
        $scope.configuration = configuration;  
        $scope.updateDetails();             
    });
}

If this function is used else where and you don't always want to call update, pass a flag param:

$scope.changeColor = function(newColorCode, shouldUpdate){
    //swap previous selected color with new
    configuratorService.addOption($scope.modelCode, newColorCode, function(configuration){
        $scope.configuration = configuration;  
        if (shouldUpdate) $scope.updateDetails();             
    });
}

ng-click="changeColor(color.code, true);"

Technically you could write inline provided changeColor returns promise and inturn your service call must return a promise (which is better pared to traditional passing around callbacks anyways).

First change your service to return a promise, an example:-

 function configuratorService($http){
      this.addOption = function(modelCode, colorCode){
          return $http.post("Addoption", {modelCode:modelCode, colorCode:colorCode})
                 .then(function(response){ 
                   //logic to get configuration or whatever
                   return response.data
          });
     }
  }

In ColorsCtrl:

$scope.changeColor = function(newColorCode){
     //return promise   
    return configuratorService.addOption($scope.modelCode, newColorCode)
         .then( function(configuration){
            $scope.configuration = configuration;     
            /*You could even return the data by doing the following*/          
            //return $scope.configuration = configuration;
        });
    }
 }

In HeaderCtrl:

$scope.updateDetails = function(){//<-- if you are returning data from changeColor you could even use it here
    $scope.summaryDetails = getSummaryDetails();
}

and finally in your view:

ng-click="changeColor(color.code).then(updateDetails)"

An example Demo

angular.module('app', []).controller('HeaderCtrl', function($scope) {
  $scope.updateDetails = function() {
    //check the console
    console.log('UpdateDetails');
    $scope.summaryDetails = "Summary Details after option changeColor";
  }
}).controller('ColorsCtrl', function($scope, configuratorService) {
  $scope.changeColor = function(newColorCode) {
    //swap previous selected color with new
    return configuratorService.addOption().then(function(configuration) {
      //check the console
      console.log('Option Added');
      $scope.configuration = configuration;
    });
  }
}).service('configuratorService', function($timeout) {
  this.addOption = function() {
    //Justa proxy for an ansyn operation and return call
    return $timeout(function() {
      return "Color Updated"
    }, 1000);
  }
});
<script src="https://ajax.googleapis./ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">

  <div ng-controller="ColorsCtrl">
    <div ng-controller="HeaderCtrl">

      <button ng-click="changeColor().then(updateDetails)">Update</button>
      {{summaryDetails}}
    </div>
  </div>

There are many other options and better ways to do it. One provided in the TymeJV answer, the issue there is that your controllers bees tightly coupled with one another and could affect re-usability and you would need to do extra mocks to add the non existent method updateDetails while testing ColorsCtrl.

While this approach in my answer has its own problem because now your view needs to know something (that the method returns promise) which it should not. There are other ways to handle this scenario in a better way: One simple approach is to use eventing or a pub/sub pattern which notifies other controller that something has happened here do whatever you want to do. Such kind of design will make it more loosely coupled and more reusable and testable.

You can make use of angular event bus itself, like $broadcast or $emit as per applicability bined with $on ,to perform this task or you could create a pub/sub design yourself.

With simple eventing you would just do this:-

In ColorsCtrl:

    $scope.changeColor = function(newColorCode){
        configuratorService.addOption($scope.modelCode, newColorCode, function(configuration){
            $scope.configuration = configuration;
            //BroadCast an event, this will notify any child scopes that has subscribed to this event.   
            $scope.$broadCast("Configuration_Udpated");                           
          });
       }
   }

In HeaderCtrl subscribe to an event and register updateDetails method:

//....
$scope.updateDetails = function(){
    $scope.summaryDetails = getSummaryDetails();
}


//...
$scope.$on("Configuration_Udpated", $scope.updateDetails)

Side note:- Try to make use of the promises rather than passing callbacks to the service. You can find a lot of articles on the web regarding promise patterns.

发布评论

评论列表(0)

  1. 暂无评论