How to call multiple functions / chain of calls using ng-click
Hi I have a button that launches two functions. Is there a way for updateDetails to only run after changeColor has finished? Currently updateDetails is pulling the old config with the old color, not the changed color.
ng-click="changeColor(color.code); updateDetails()"
Here's a 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's the value for changeColor () in ColorsCtrl:
$scope.changeColor = function(newColorCode){
//swap previous selected color with new
configuratorService.addOption($scope.modelCode, newColorCode, function(configuration){
$scope.configuration = configuration;
});
}
}
Here is the updateDetails in HeaderCtrl
$scope.updateDetails = function(){
$scope.summaryDetails = getSummaryDetails();
}
source to share
Technically, you could write inline on the condition that it changeColor
returns a promise and your service call should return a promise (which is better compared to the traditional looping around callbacks).
First change your service to return a promise like: -
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 opinion:
ng-click="changeColor(color.code).then(updateDetails)"
Demo example
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.com/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 this. One is presented in TymeJV's answer: The problem is that your controllers are tightly coupled and can affect reuse, and you will need to do additional layouts to add a non-existent method updateDetails
during testing ColorsCtrl
.
Although this approach in my answer has its own problem, because now your opinion should know something (that the method returns a promise) that it shouldn't. There are other ways to better deal with this scenario, one simple approach is to use eventing or the pub / sub pattern that notifies another controller that something has happened here, do whatever you want to do. This design will make it more loosely coupled and more reusable and testable.
You can only use the angular event bus like $ broadcast or $ emit as applicable, combined with $ on to accomplish this task, or you can create a pub / sub design .
With a simple event, you simply do the following: -
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 the event and register the updateDetails method:
//....
$scope.updateDetails = function(){
$scope.summaryDetails = getSummaryDetails();
}
//...
$scope.$on("Configuration_Udpated", $scope.updateDetails)
Note: - Try using promises rather than passing callbacks to the service. You can find many articles online regarding promise templates .
source to share
Since changeColor
- the async operation is not, you cannot wait for a row. You will need 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 you don't always want to call the update, pass the param parameter:
$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);"
source to share