$scope.$emit not work in callback? - angularjs

I have a code to authentication where I want call an event after receive some data, but "$scope.$emit" not working in the callback of "User.me()" and I not understand.
Anybody can explain me?
$scope.login = function () {
OAuth.getAccessToken($scope.user).then(function () {
$scope.$emit('event:here:work');
User.me({}, {}, function (data) {
$scope.$emit('event:here:NOT-WORK');
});
$scope.$emit('event:here:work');
}, function (response) {
// error //
});
};
I try:
$scope.$emit('event'); // but, not work
$scope.$broadcast('event'); // but, not work
$rootScope.$emit('event'); // but, not work
$rootScope.$broadcast('event'); // work!!!!
Now, the "why" I not know?

Use $scope.$broadcast instead.

Related

How to get karma/jasmine unit tests working with a callback after a promise?

I am writing an app in AngularJS 1.5, JavaScript and Cordova.
I want to write a unit test that will check to see if some code was executed after a promise.
Here is my codepen: https://codepen.io/aubz/pen/yrxqxE
I am not sure why but the unit test keeps saying this error:
Expected spy attemptGeoClocking to have been called.
It's strange because the console log prints out so the function is actually being called.
it('if location services are on, proceed', function () {
spyOn(CordovaDiagnostics, 'getLocationServicesStatus').and.callFake(function () {
return Promise.resolve(true);
});
spyOn(Clocking, 'attemptGeoClocking').and.callFake(function () {});
Clocking.geolocationClocking();
expect(Clocking.attemptGeoClocking).toHaveBeenCalled();
});
function geolocationClocking() {
CordovaDiagnostics
.getLocationServicesStatus()
.then(attemptGeoClocking)
.catch(function () {});
}
function attemptGeoClocking() {
console.log(' here ');
}
Basically you're spying on the wrong functions. Let me rename a few things so it's more clear what you're doing:
function Clocking(CordovaDiagnostics) {
return {
geolocationClocking: geolocationClocking,
attemptGeoClockingOUTER: attemptGeoClockingINNER//private API
};
function geolocationClocking() {
CordovaDiagnostics
.getLocationServicesStatus()
.then(attemptGeoClockingINNER)
.catch(function () {});
}
function attemptGeoClockingINNER() {
console.log(' here ');
}
}
And in the test:
spyOn(Clocking, 'attemptGeoClockingOUTER').and.callFake(function () {
console.log('calling fake')
});
As you can see, your code is spying on the OUTER
but geolocationClocking is never calling the OUTER, it's using the INNER:
CordovaDiagnostics
.getLocationServicesStatus()
.then(attemptGeoClockingINNER)
You'll need to rework your code in such a way that it's using the same function internally as to the one you're stubbing in your test.
Here's a working codepen: https://codepen.io/anon/pen/xeyrqy?editors=1111
Note: I've also replaced Promise.resolve with $q.when and added $rootScope.$apply(), this is needed to resolve the promises.
Adding the changes I made here, in case the codepen would ever disappear:
I've changed the factory to a service (while not necessary, I prefer using services in this case):
myApp.service("Clocking", Clocking);
function Clocking(CordovaDiagnostics) {
this.geolocationClocking = function() {
CordovaDiagnostics
.getLocationServicesStatus()
.then(() => this.attemptGeoClocking())
.catch(function () {});
}
this.attemptGeoClocking = function() {
console.log(' here ');
}
}

AngularJS Share Data Between Controllers Using Service And Watch For Updates

Simple task here, but not sure about the mistake.
My service:
app.factory('Progress', function () {
var data = {
progressPercentageLoaded: 0
};
return {
getProgress: function () {
return data.progressPercentageLoaded;
},
setProgress: function (progress) {
data.progressPercentageLoaded = progress;
}
};
});
I have one controller that is uploading a file, this one sets the progress value.
//in my controller
$scope.progressPercentageLoaded = {progress:0};
//a few lines down
function (evt) {
console.log(evt);
$scope.progressPercentageLoaded.progress = evt.loaded;
Progress.setProgress($scope.progressPercentageLoaded.progress);
My second controller should simply watch the service for changes and update the view, but it stays stuck at zero even though I confirm the upload is happening and that evt.loaded is changing.
$scope.progress = 0;
$scope.$watch(function () { return Progress.getProgress(); }, function (newValue, oldValue) {
if (newValue !== oldValue) {
$scope.progress = Math.min(100, parseInt(100 * newValue / $scope.size));
if($scope.progress == 100)
{
$scope.progressModalInstance.close();
window.location.reload();
}
}
});
That is, $scope.progress in second controller should update with the value of evt.loaded in the first controller, but it doesn't.
Thanks in advance for any guidance.
EDIT: I even added the third watch parameter as true but that didn't help either.
EDIT 2 : The code above actually works to the best of my knowledge, I believe something else was causing a problem as when I reverted the code to the above after editing it due to the answers, it suddenly worked like it should. Sorry about this.
using $rootScope.$broadcast will work better for this
app.factory('Progress', function ($rootScope) {
var data = {
progressPercentageLoaded: 0
};
return {
getProgress: function () {
$rootScope.$broadcast('Event');
return data.progressPercentageLoaded;
},
setProgress: function (progress) {
data.progressPercentageLoaded = progress;
}
};
});
In your second controller instead of using watch
something like
$rootScope.$on('Event', function(){
//your logic here
})
You are losing the reference because you are watching the Int value that you are updating every time, hence you are changing the reference. You have to watch the whole object progressPercentageLoaded.
You have to pass true as the last parameter of the $watch function so that the equality check is angular.equals. Otherwise, only reference equality is checked.
I'm not 100% sure, but Angular may not be aware of the file upload event. Try calling $apply:
$scope.$apply(function() {
Progress.setProgress($scope.progressPercentageLoaded.progress);
});

angular async operations success, fail, finally

I am trying to retrofit spinners into my app.
I'm expecting to set a loading=true variable when I start async events, and set it false when the call returns.
Then in my view I can do
<span><i class="fa fa-spinner" if-show="vm.loading"></i><span>
I was hoping to find async calls of the form success, failure, finally.
The first controller I opened up makes a call in a form I don't understand. I don't even know what to call it, so I have no idea how to research and explore it.
$scope.login = function () {
if ($scope.form.$valid) {
authService.login($scope.loginData).then(function (response) {
$location.path("/dashboard");
},
function (err) {
toastr.error(err.error_description);
});
}
};
What I see here is an if statement, followed by a comma, followed by a function.
Uhh... is that some form of try/catch I've not encountered before?
I can't just add a finally on the end...
The reason I'm asking the question here is because I don't even know how to research this.
Ultimately the question I'm trying to answer is: what form of async call can I use so that I have a place to first activate the spinner, and then deactivate it?
Ah. OK. It's a standard promise - just confusingly formatted. I overlooked the .then that's on the same line.
$scope.login = function () {
if ($scope.form.$valid) {
$scope.loading = true;
authService.login($scope.loginData)
.then(function (response) {
$location.path("/dashboard");
},
function (err) {
toastr.error(err.error_description);
})
.finally(function(){
$scope.loading = false;
}
);
}
}
Found it here:
How to always run some code when a promise is fulfilled in Angular.js

Execute function after $mddialog.cancel() finishes - AngularJS

I've been looking at the documentation for $mdDialog.cancel() but haven't found how to call a function after if finishes.
I want to do something like this
$mdDialog.cancel().then(function() {
// Do some stuff in here
});
Is this possible?
I thought of making a seperate function that returned a promise but not sure how to do that,
closeDialog().then(function(){
// Do something
});
closeDialog = function() {
$mdDialog.cancel();
return promise;
}
Your original approach was almost correct. You just need to return $mdDialog.cancel() from closeDialog function:
closeDialog().then(function() {
// Do something
});
closeDialog = function() {
return $mdDialog.cancel();
}

Testing window.postMessage directive

I'm having trouble testing my directive which enables cross-document messaging by registering a message handler:
.directive('messaging', function ($window, MyService) {
return {
link: function () {
angular.element($window).on('message', MyService.handleMessage);
}
};
})
All I want to unit test is that when this directive is compiled, and window.postMessage('message','*') is called, my message handler should be called:
http://jsfiddle.net/mhu23/L27wqn14/ (including jasmine test)
I'd appreciate your help!
Michael
Your are using original window API, you are not mocking it, so the method postMessage will keep it's asynchronous behavior. Knowing that, tests should be written in an asynchronous way. In JSFiddle you have Jasmine 1.3, so test should look kinda like this:
it('should ....', function () {
var done = false;
spyOn(MyService,'handleMessage').andCallFake(function () {
// set the flag, let Jasmine know when callback was called
done = true;
});
runs(function () {
// trigger async call
$window.postMessage('message','*');
});
waitsFor(function () {
// Jasmine waits until done becomes true i.e. when callback be called
return done;
});
runs(function () {
expect(MyService.handleMessage).toHaveBeenCalled();
});
});
Check the docs about testing async with Jasmine 1.3. And here is a working JSFiddle.
It would be a bit easier in Jasmine 2.x:
it('should ....', function (done) {
spyOn(MyService,'handleMessage').and.callFake(function () {
expect(MyService.handleMessage).toHaveBeenCalled();
done();
});
$window.postMessage('message','*');
});
Also, I have to mention, that you have to change how you add a listener from this
angular.element($window).on('message', MyService.handleMessage);
to that
angular.element($window).on('message', function (e) {
MyService.handleMessage(e);
});
because .on registers a function itself, it won't be used as a method attached to the MyService, so you won't be able to spy on it.

Resources