Angular.js - Digest is not including $scope member changes - angularjs

I have a service that includes:
newStatusEvent = function(account, eventId, url, deferred, iteration) {
var checkIteration;
checkIteration = function(data) {
if (iteration < CHECK_ITERATIONS && data.Automation.Status !== 'FAILED') {
iteration++;
$timeout((function() {
return newStatusEvent(account, eventId, url, deferred, iteration);
}), TIME_ITERATION);
} else {
deferred.reject('failure');
}
};
url.get().then(function(data) {
if (data.Automation.Status !== 'COMPLETED') {
checkIteration(data);
} else {
deferred.resolve('complete');
}
});
return deferred.promise;
};
runEventCheck = function(account, eventId, modalInstance, state) {
newStatusEvent(account, eventId, urlBuilder(account, eventId),
$q.defer(), 0)
.then(function() {
scopeMutateSuccess(modalInstance, state);
}, function() {
scopeMutateFailure(modalInstance);
})["finally"](function() {
modalEventConfig.disableButtonsForRun = false;
});
};
var modalEventConfig = {
disableButtonsForRun: false,
statusBar: false,
nodeStatus: 'Building',
statusType: 'warning'
}
function scopeMutateSuccess(modalInstance, state){
/////////////////////////////////////////////////
//THE SCPOPE DATA MEMBERS THAT ARE CHANGED BUT
//CURRENT DIGEST() DOES NOT INCLUDE THE CHANGE
modalEventConfig.statusType = 'success';
modalEventConfig.nodeStatus = 'Completed Successfully';
//////////////////////////////////////////////////
$timeout(function() {
scopeMutateResetValues();
return modalInstance.close();
}, TIME_CLOSE_MODAL);
state.forceReload();
}
modalEventConfig.scopeMutateStart = scopeMutateStart;
modalEventConfig.close = scopeMutateResetValues;
return {
runEventCheck: runEventCheck,
modalEventConfig: modalEventConfig
};
And here is the controller:
angular.module('main.loadbalancer').controller('EditNodeCtrl', function($scope, $modalInstance, Configuration, LoadBalancerService, NodeService, StatusTrackerService, $state, $q) {
NodeService.nodeId = $scope.id;
$q.all([NodeService.getNode(), LoadBalancerService.getLoadBalancer()]).then(function(_arg) {
var lb, node;
node = _arg[0], lb = _arg[1];
$scope.node = node;
return $scope.save = function() {
$scope.modalEventConfig.scopeMutateStart();
return NodeService.updateNode({
account_number: lb.customer,
ip: node.address,
port: node.port_number,
label: node.label,
admin_state: node.admin_state,
comment: node.comment,
health_strategy: {
http_request: "" + node.healthMethod + " " + node.healthUri,
http_response_accept: "200-299"
},
vendor_extensions: {}
}).then(function(eventId) {
return StatusTrackerService.runEventCheck(lb.customer, eventId,
$modalInstance, $state);
});
}
});
$scope.modalEventConfig = StatusTrackerService.modalEventConfig;
The issue I am having is in the service. After a successful resolve in newStatusEvent and scopeMutateSuccess(modalInstance, state); runs... the modalEventConfig.statusType = 'success'; and modalEventConfig.nodeStatus = 'Completed Successfully'; changes aren't reflected in the view.
Normally, this would be because a digest() is needed to make angular.js aware of a change. However, I have verified in the stack(chromium debugger) that a digest() was called earlier in the stack and is still in effect when the scope members are mutated in function scopeMutateSuccess(modalInstance, state);
What is weird, if I add $rootScope.$apply() after modalEventConfig.nodeStatus = 'Completed Successfully';...then Angular.js will complain a digest() is already in progress...BUT...the view will successfully update and reflect the new changes in from the scope members nodeStatus and statusType. But, obviously this is not the answer/appropriate fix.
So, the question is why isn't the digest() that is currently running from the beginning of the stack(stack from chromium debugger) making angular.js aware of the scope changes for modalEventConfig.statusType = 'success' and modalEventConfig.nodeStatus = 'Completed Successfully'? What can I do to fix this?

$scope.modalEventConfig = StatusTrackerService.modalEventConfig; is a synchronous call, you need treat things asynchronously .
You need wait on promise(resolved by service) at calling area also, i.e. in the controller .

Fixed it.
function scopeMutateSuccess(modalInstance, state){
/////////////////////////////////////////////////
//THE SCPOPE DATA MEMBERS THAT ARE CHANGED BUT
//CURRENT DIGEST() DOES NOT INCLUDE THE CHANGE
modalEventConfig.statusType = 'success';
modalEventConfig.nodeStatus = 'Completed Successfully';
//////////////////////////////////////////////////
$timeout(function() {
scopeMutateResetValues();
state.forceReload();
return modalInstance.close();
}, TIME_CLOSE_MODAL);
}
I am using ui-router and I do a refresh with it useing $delegate. I place state.forceReload(); in the $timeout...the scope members update as they should. I have no idea why exactly, but I am glad this painful experience has come to a end.

Related

AngularJS How to execute function after some function finish?

I have some page which contain register with facebook button which I set hidden with ng-hide="fbLoggedIn" and form input which I set hidden with ng-show="fbLoggedIn"
My goal is register with facebook button will hide if fbLoggedIn set to true and form input will show if fbLoggedIn set to true.
register facebook button ng-click="registerFb()" execute this function
$scope.registerFB = function () {
authService.fbLogin();
$scope.fbLoggedIn = authService.fb_logged_in();
console.log($scope.fbLoggedIn); //this show false even `fb_access_token` not null
}
Here is my authService.fbLogin and authService.fb_logged_in function
authService.fbLogin = function () {
var FB = window.FB;
FB.login(function(response) {
console.log(response);
if (response.authResponse) {
sessionService.set('fb_id', response.authResponse.userID);
sessionService.set('fb_access_token', response.authResponse.accessToken);
sessionService.set('fb_expiration_date', new Date(new Date().getTime() + response.authResponse.expiresIn * 1000).toISOString());
//console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Good to see you, ' + response.name + '.');
console.log(response);
});
} else {
console.log('User cancelled login or did not fully authorize.');
//console.log(response);
}
});
};
authService.fb_logged_in = function () {
if(sessionService.get('fb_access_token') != null){
return true;
}else {
return false;
}
};
In other function I try to check if fb_access_token is not null, just to make sure something wrong with my logic, and the result is true.
With above debuggin I can say that $scope.fbLoggedIn = authService.fb_logged_in(); execute before authService.fbLogin(); finish.
So how I can execute $scope.fbLoggedIn = authService.fb_logged_in(); after authService.fbLogin(); finish? maybe how to achieve my goal?
Alright. This can be achieved using promise. I don't know the parameters you have included in your autService service, so I will be making a factory of the same name with the new parameters that you might need to add.
Hence, according to me, this is how your factory should be.
angular.module('YourModuleName').factory('authService',['$http','$q',function($http,$q){
var obj = {};
obj.fbLogin = function () {
var defer = $q.defer();
var FB = window.FB;
FB.login(function(response) {
console.log(response);
if (response.authResponse) {
sessionService.set('fb_id', response.authResponse.userID);
sessionService.set('fb_access_token', response.authResponse.accessToken);
sessionService.set('fb_expiration_date', new Date(new Date().getTime() + response.authResponse.expiresIn * 1000).toISOString());
FB.api('/me', function(response) {
console.log(response);
defer.resolve('Good to see you, ' + response.name + '.');
});
}
else {
defer.reject('User cancelled login or did not fully authorize.');
}
});
return defer.promise;
}
obj.fb_logged_in = function () {
if(sessionService.get('fb_access_token') != null){
return true;
}else {
return false;
}
};
return obj;
}])
And thus, the function call from the controller should be as follows.
$scope.registerFB = function () {
authService.fbLogin().then(function(response){
$scope.fbLoggedIn = authService.fb_logged_in();
console.log($scope.fbLoggedIn);
},function(error){
console.error("Error : ",error);
});
}
Note: CODE NOT TESTED.
Hence it would solve the problem with the best practices of angularJS
use the $rootscope to assign values they provide event emission/broadcast and subscription facility.

Delay loading data in Angular JS

I have code like this
(function (app) {
app.controller('productListController', productListController)
productListController.$inject = ['$scope', 'apiService', 'notificationService', '$ngBootbox', '$filter'];
function productListController($scope, apiService, notificationService, $ngBootbox, $filter) {
$scope.products = [];
$scope.page = 0;
$scope.pagesCount = 0;
$scope.getProducts = getProducts;
$scope.keyword = '';
$scope.search = search;
$scope.deleteProduct = deleteProduct;
$scope.selectAll = selectAll;
$scope.deleteMultiple = deleteMultiple;
function deleteMultiple() {
var listId = [];
$.each($scope.selected, function (i, item) {
listId.push(item.ID);
});
var config = {
params: {
checkedProducts: JSON.stringify(listId)
}
}
apiService.del('/api/product/deletemulti', config, function (result) {
notificationService.displaySuccess('Deleted successfully ' + result.data + 'record(s).');
search();
}, function (error) {
notificationService.displayError('Can not delete product.');
});
}
$scope.isAll = false;
function selectAll() {
if ($scope.isAll === false) {
angular.forEach($scope.products, function (item) {
item.checked = true;
});
$scope.isAll = true;
} else {
angular.forEach($scope.products, function (item) {
item.checked = false;
});
$scope.isAll = false;
}
}
$scope.$watch("products", function (n, o) {
var checked = $filter("filter")(n, { checked: true });
if (checked.length) {
$scope.selected = checked;
$('#btnDelete').removeAttr('disabled');
} else {
$('#btnDelete').attr('disabled', 'disabled');
}
}, true);
function deleteProduct(id) {
$ngBootbox.confirm('Are you sure to detele?').then(function () {
var config = {
params: {
id: id
}
}
apiService.del('/api/product/delete', config, function () {
notificationService.displaySuccess('The product hase been deleted successfully!');
search();
}, function () {
notificationService.displayError('Can not delete product');
})
});
}
function search() {
getProducts();
}
function getProducts(page) {
page = page || 0;
var config = {
params: {
keyword: $scope.keyword,
page: page,
pageSize: 20
}
}
apiService.get('/api/product/getall', config, function (result) {
if (result.data.TotalCount == 0) {
notificationService.displayWarning('Can not find any record.');
}
$scope.products = result.data.Items;
$scope.page = result.data.Page;
$scope.pagesCount = result.data.TotalPages;
$scope.totalCount = result.data.TotalCount;
}, function () {
console.log('Load product failed.');
});
}
$scope.getProducts();
}
})(angular.module('THTCMS.products'));
So my problem is when i loading data the application take me some time to load data.
I need load data as soon as
Is the any solution for this?
Since you are loading data via api call, there will be a delay. To handle this delay, you should display a loading screen. Once the data is loaded, the loading screen gets hidden and your main screen is visible. You can achieve this using $http interceptors.
See : Showing Spinner GIF during $http request in angular
The api-call is almost certainly causing the delay. Data may be received slowly via the api-call so you could display any sort of loading text/image to notify the use that the data is being loaded.
If u want the data ready at the time when controller inits, u can add a resolve param and pass the api call as a $promise in the route configuration for this route.

$scope.$watch does not seem to watch factory variable

I'm a beginner to angularjs. In my NFC project, I want to be able to GET from the server data based on a changing patientId.
However, I am not able to see my $watch execute correctly, even though I see that the patientId changes each time I scan a new NFC tag.
var nfc = angular.module('NfcCtrl', ['PatientRecordsService'])
nfc.controller('NfcCtrl', function($scope, NfcService, PatientRecordsService) {
$scope.tag = NfcService.tag;
$scope.patientId = NfcService.patientId
$scope.$watch(function() {
return NfcService.patientId;
}, function() {
console.log("Inside watch");
PatientRecordsService.getPatientRecords(NfcService.patientId)
.then(
function(response) {
$scope.patientRecords = response
},
function(httpError) {
throw httpError.status + " : " +
httpError.data;
});
}, true);
$scope.clear = function() {
NfcService.clearTag();
};
});
nfc.factory('NfcService', function($rootScope, $ionicPlatform, $filter) {
var tag = {};
var patientId = {};
$ionicPlatform.ready(function() {
nfc.addNdefListener(function(nfcEvent) {
console.log(JSON.stringify(nfcEvent.tag, null, 4));
$rootScope.$apply(function(){
angular.copy(nfcEvent.tag, tag);
patientId = $filter('decodePayload')(tag.ndefMessage[0]);
});
console.log("PatientId: ", patientId);
}, function() {
console.log("Listening for NDEF Tags.");
}, function(reason) {
alert("Error adding NFC Listener " + reason);
});
});
return {
tag: tag,
patientId: patientId,
clearTag: function () {
angular.copy({}, this.tag);
}
};
});
Not sure what I'm missing here - please enlighten me!
Update
Per rakslice's recommendation, I created an object to hold my data inside the factory, and now the html (with some server side delay) correctly displays the updated values when a new NFC tag is scanned.
var nfc = angular.module('NfcCtrl', ['PatientRecordsService'])
nfc.controller('NfcCtrl', function($scope, NfcService) {
$scope.tagData = NfcService.tagData;
$scope.clear = function() {
NfcService.clearTag();
};
});
nfc.factory('NfcService', function($rootScope, $ionicPlatform, $filter, PatientRecordsServi\
ce) {
var tagData = {
tag: null,
patientId: null,
patientRecords: []
};
$ionicPlatform.ready(function() {
nfc.addNdefListener(function(nfcEvent) {
//console.log(JSON.stringify(nfcEvent.tag, null, 4));
$rootScope.$apply(function() {
tagData.tag = nfcEvent.tag;
tagData.patientId = $filter('decodePayload')(tagData.tag.ndefMessage[0]);
PatientRecordsService.getPatientRecords(tagData.patientId)
.then(
function(response) {
tagData.patientRecords = response
},
function(httpError) {
throw httpError.status + " : " +
httpError.data;
});
});
console.log("Tag: ", tagData.tag);
console.log("PatientId: ", tagData.patientId);
}, function() {
console.log("Listening for NDEF Tags.");
}, function(reason) {
alert("Error adding NFC Listener " + reason);
})
});
return {
tagData: tagData,
clearTag: function() {
angular.copy({}, this.tagData);
}
};
});
Your code doesn't update the patientId value in the returned NfcService, only the local variable patientId inside the factory function.
Try saving a reference to the object you're returning in the factory function as in a local variable and use that to update the patientId.
For instance, change the creation of the object to put it in a local variable:
var nfcService = {
tag: tag,
patientId: patientId,
clearTag: function () {
angular.copy({}, this.tag);
}
};
...
return nfcService
and then change the patientId update to change the value in the object through the variable.
nfcService.patientId = $filter('decodePayload')(tag.ndefMessage[0]);
Update:
The basic fact about JavaScript that you need to understand is that when you assign one variable to another, if the first variable had a primitive data value the second variable gets a copy of that value, so changing the first variable doesn't affect the second variable after that, but if the first variable had an object reference the second variable gets pointed at that same object that the first variable is pointed at, and changing the object in the first variable after that will affect what you see through the second variable, since it's looking at the same object.
A quick experiment in the browser JavaScript console should give you the idea:
> var a = 1;
> a
1
> var b = a;
> b
1
> a = 5;
> a
5
> b
1
vs.
> var a = {foo: 1}
> var b = a
> a.foo = 5
> a.foo
5
> b.foo
5

UI not updating when property changed within $interval callback

I have a service which defines a background $interval. It watches local storage to see if some server updates didn't get made due to connectivity problems. When it finds some, it periodically tries to contact the server with the updates, deleting them from local storage when it succeeds.
Upon success, the callback also updates some properties of an angular view/model. Those changes should cause the UI to update, and elsewhere in the code of various controllers they do.
But within the $interval callback in that background service the changes do not cause the UI to update. I thought it might be some kind of failure to $apply, so I added a $rootScope.$apply() call. But that caused an error because a $digest was already in progress.
That tells me $digest is running after the callback executes -- which is what I would've expected -- but it's not causing the UI to update.
I then did a $rootScope.$emit() within the service, after the view/model was updated, and listened for the event within the controller where the UI should've updated. Within the listener I can see that the view/model the controller is based on was successfully updated by the service.
Here is the service:
app.factory('bkgndUpdates', function($interval, $q, $rootScope, dataContext, localStorage) {
var _interval = null;
var _service = {
get isRunning() { return _interval != null },
};
_service.start = function() {
if( _interval != null ) return;
_interval = $interval(function() {
// check to ensure local storage is actually available
// and has visits to process
var visits = localStorage.visits;
if( !visits || (visits.length == 0) ) return;
var recorded = [];
var promises = [];
var offline = false;
for( var idx = 0; idx < visits.length; idx++ )
{
var visit = visits[idx];
promises.push(dataContext.doVisitAction("/Walk/RecordVisit", visit)
.then(
function(resp) {
offline &= false;
var home = dataContext.findHomeByID(visit.addressID);
if( home != null ) {
// these values should cause a map icon to switch from yellow to blue
// elsewhere in the app that's what happens...but not here.
home.visitInfo.isDirty = false;
home.visitInfo.inDatabase = true;
home.visitInfo.canDelete = true;
home.visitInfo.followUpID = resp.FollowUpID;
}
recorded.push(visit.addressID);
},
function(resp) {
// something went wrong; do nothing, as the item is already
// in local storage
offline |= true;
})
);
}
$q.all(promises).then(
function(resp) {
for( var idx = 0; idx < recorded.length; idx++ )
{
localStorage.removeVisitByID(recorded[idx]);
}
if( !localStorage.hasVisits ) {
$interval.cancel(_interval);
_interval = null;
}
$rootScope.$emit("visitUpdate");
},
function(resp) {
alert("some promise failed");
});
}, 30000);
}
_service.stop = function() {
$interval.cancel(_interval);
_interval = null;
}
return _service;
});
Here's the controller for the map:
app.controller("mapCtrl", function ($scope, $rootScope, $location, dataContext) {
$scope.dataContext = dataContext;
$scope.mapInfo = {
center:dataContext.center,
zoom: dataContext.zoom,
pins: dataContext.pins,
};
$scope.$on('mapInitialized', function(evt, map){
$scope.dragend = function() {
$scope.dataContext.center = $scope.map.getCenter();
$scope.dataContext.getMapData($scope.map.getBounds());
}
$scope.zoomchanged = function() {
$scope.dataContext.zoom = $scope.map.getZoom();
$scope.dataContext.getMapData($scope.map.getBounds());
}
$scope.dataContext.getMapData($scope.map.getBounds())
.then(
function() {
$scope.mapInfo.pins = $scope.dataContext.pins;
},
function() {});
});
$scope.click = function() {
dataContext.pinIndex = this.pinindex;
if( dataContext.activePin.homes.length == 1)
{
dataContext.homeIndex = 0;
$location.path("/home");
}
else $location.path("/unit");
};
$rootScope.$on("visitUpdate", function(event) {
// I added this next line to force an update...even though the
// data on both sides of the assignment is the same (i.e., it was
// already changed
$scope.mapInfo.pins = $scope.dataContext.pins;
event.stopPropagation();
});
});
Here's the (partial) template (which relies on ngMap):
<div id="mapframe" class="google-maps">
<map name="theMap" center="{{mapInfo.center.toUrlValue()}}" zoom="{{mapInfo.zoom}}" on-dragend="dragend()" on-zoom_changed="zoomchanged()">
<marker ng-repeat="pin in mapInfo.pins" position="{{pin.latitude}}, {{pin.longitude}}" title="{{pin.streetAddress}}" pinindex="{{$index}}" on-click="click()"
icon="{{pin.icon}}"></marker>
</map>
</div>
So why isn't the UI updating?

$watch not updating scope variable

First I want to say that I am a complete beginner in AngularJS and just attempting to understand the basic concepts. I have a background in Java and PHP.
I am building a part of a website. Right now the angular app only consists of opening and closing 2 drop down menus registrationDropDown and loginDropDown. I want them to work so that only one can be open at a time ie. if I open one, and the other is already open, the older one is forced to close.
I have a service to manage the variables that determine whether the drop downs should be open or closed and 2 controllers, one for login and one for registration, both include $watch for the respective variables.
THE PROBLEM
I want the app to work so that only one of the drop downs can be open at one time.
JSFIDDLE: http://jsfiddle.net/F5p6m/3/
angular.module("ftApp", [])
.factory('dropDownService', function () {
var loginDropDownStatus = false;
var registrationDropDownStatus = false;
return {
getLoginDropDownStatus: function () {
return loginDropDownStatus;
},
showLoginDropDown: function () {
console.log("showing login drop down");
registrationDropDownStatus = false;
loginDropDownStatus = true;
console.log("loginDropDownStatus" + loginDropDownStatus + "registrationDropDownStatus" + registrationDropDownStatus);
},
hideLoginDropDown: function () {
console.log("hiding login drop down");
loginDropDownStatus = false;
console.log("loginDropDownStatus" + loginDropDownStatus);
},
getRegistrationDropDownStatus: function () {
return registrationDropDownStatus;
},
showRegistrationDropDown: function () {
console.log("showing registration drop down");
registrationDropDownStatus = true;
loginDropDownStatus = false;
console.log("registrationDropDownStatus" + registrationDropDownStatus);
},
hideRegistrationDropDown: function () {
console.log("hiding registration drop down");
registrationDropDownStatus = false;
console.log("registrationDropDownStatus" + registrationDropDownStatus);
}
};
}) .controller("LoginDropDownController", function ($scope, dropDownService) {
$scope.loginDropDownStatus = dropDownService.getLoginDropDownStatus();
$scope.$watchCollection('loginDropDownStatus', function(newValue, oldValue) {
console.log("watcher is working");
console.log("value is " + newValue + oldValue);
console.log("LOGIN new value is " + newValue);
$scope.loginDropDownStatus = newValue;
});
$scope.toggleDropDown = function () {
if ( $scope.loginDropDownStatus == false ) {
dropDownService.showLoginDropDown();
dropDownService.hideRegistrationDropDown();
$scope.loginDropDownStatus = true;
} else if ( $scope.loginDropDownStatus == true ) {
dropDownService.hideLoginDropDown();
$scope.loginDropDownStatus = false;
}
};
})
.controller("RegistrationDropDownController", function ($scope, dropDownService) {
$scope.registrationDropDownStatus = dropDownService.getRegistrationDropDownStatus();
$scope.$watch('registrationDropDownStatus', function(newValue, oldValue) {
console.log("watcher is working");
console.log("value is " + newValue + oldValue);
console.log("new value is " + newValue);
$scope.registrationDropDownStatus = newValue;
});
$scope.toggleDropDown = function () {
if ( $scope.registrationDropDownStatus == false ) {
dropDownService.showRegistrationDropDown();
dropDownService.hideLoginDropDown();
$scope.registrationDropDownStatus = true;
} else if ( $scope.registrationDropDownStatus == true ) {
dropDownService.hideRegistrationDropDown();
$scope.registrationDropDownStatus = false;
}
};
})
Edit:
Here is probably the shortest option:
angular.module("ftApp", [])
.controller("ctrl", function ($scope) {
$scope.toggle = function(menu){
$scope.active = $scope.active === menu ? null : menu;
}
})
FIDDLE
One controller, no service.
Previous Answer:
I think you have quite a bit of code to get something very simple done. Here is my solution:
angular.module("ftApp", [])
.service('dropDownService', function () {
this.active = null;
this.toggle = function(menu){
this.active = this.active === menu ? null : menu;
}
})
.controller("LoginDropDownController", function ($scope, dropDownService) {
$scope.status = dropDownService;
$scope.toggleDropDown = function () {
dropDownService.toggle("login");
};
})
.controller("RegistrationDropDownController", function ($scope, dropDownService) {
$scope.status = dropDownService;
$scope.toggleDropDown = function () {
dropDownService.toggle("reg");
};
})
FIDDLE
You can make it even shorter by only using one controller. You wouldn't even need the service then.
You are overcomplicating things. All you need your service to hold is a property indicating which dorpdown should be active.
Then you can change that property's value from the controller and check the value in the view to determine if a dropdown should be shown or hidden.
Something like this:
<!-- In the VIEW -->
<li ng-controller="XyzController">
<a ng-click="toggleDropdown()">Xyz</a>
<div ng-show="isActive()">Dropdown</div>
</li>
/* In the SERVICE */
.factory('DropdownService', function () {
return {
activeDropDown: null
};
})
/* In the CONTROLLER */
controller("XyzDropdownController", function ($scope, DropdownService) {
var dropdownName = 'xyz';
var dds = DropdownService;
$scope.isActive = function () {
return dropdownName === dds.activeDropdown;
};
$scope.toggleDropdown = function () {
dds.activeDropdown = (dds.activeDropdown === dropdownName) ?
null :
dropdownName;
};
})
See, also, this short demo.
Based on what exactly you are doing, there might be other approaches possible/preferrable:
E.g. you could use just on controller to control all dropdowns
or you could use two instances of the same controller to control each dropdown.
See my updated fiddle. I simplified the code and removed the service. Because you just used two variables to control visibility, you don't need a service nor $watch. You need to keep variables in the $rootScope, otherwise changes in a controller is not visible to another controller due to isolated scopes.
angular.module("ftApp", [])
.controller("LoginDropDownController", function ($scope, $rootScope) {
$rootScope.loginDropDownStatus = false;
$scope.toggleDropDown = function () {
if ($rootScope.loginDropDownStatus == false) {
$rootScope.registrationDropDownStatus = false;
$rootScope.loginDropDownStatus = true;
} else if ($rootScope.loginDropDownStatus == true) {
$rootScope.loginDropDownStatus = false;
}
};
}).controller("RegistrationDropDownController", function ($scope, $rootScope) {
$rootScope.registrationDropDownStatus = false;
$scope.toggleDropDown = function () {
if ($rootScope.registrationDropDownStatus === false) {
$rootScope.loginDropDownStatus = false;
$rootScope.registrationDropDownStatus = true;
} else if ($scope.registrationDropDownStatus === true) {
$rootScope.registrationDropDownStatus = false;
}
};
})
This code can be simplified further. I'll leave that to you.

Resources