Reputation: 263
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();
}
Upvotes: 3
Views: 1412
Reputation: 123739
Technically you could write inline provided changeColor
returns promise and inturn your service call must return a promise (which is better compared 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.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 it. One provided in the TymeJV answer, the issue there is that your controllers becomes 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 combined 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.
Upvotes: 4
Reputation: 104775
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);"
Upvotes: 6