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();
}
Related
I would like a function I am spying on not execute until some condition is true.
Here is an example:
function openInfoDialog(id) {
let scope = $scope.$new(true);
scope.dataLoading = true;
api.getData(id).then(data => {
let processedData = process(data);
scope.columns = processedData.columns;
scope.data = processedData.data;
scope.dataLoading = false;
});
ngDialog.open({
//various dialog params,
scope
});
}
In my test I am trying to verify how the data is returned by the process function. The only way I can see to check that is to spy on ngDialog.open and check what is was called with.
However in my test ngDialog is being called before the process function finishes, meaning scope.data is undefined.
Is there any way I can tell ngDialog to wait until the then block is complete in the test? Or wait until scope.dataLoading is true.
To be clear, I do not want to change the functionality in my controller, I need to test what is currently written above.
Thanks for any help.
Spy on api.getData and mimic a promise return which is sync.
it('should process data', function () {
var mockedDataObject = { ... };
spyOn(api, 'getData').and.returnValue({
then: function () { // Mimic a promise
return mockedDataObject;
}
});
openInfoDialog(123);
expect(api.getData).toHaveBeenCalledWith(123);
expect(scope.columns).toEqual(whateveryouexpect)
expect(scope.data).toEqual(whateveryouexpect)
expect(scope.dataLoading).toEqual(whateveryouexpect)
});
P.S. you mentioned you want to
verify how the data is returned by the process function
In order to do that appropriately, firstly bind the process function onto scope, so that it can be accessed from tests. Secondly, call that function directly and check what it returned. You are meant to test functionality in isolation.
it('should process data', function () {
var mockedDataObject = { ... };
var mockedProcessedData = scope.process(mockedDataObject);
expect(mockedProcessedData.columns).toEqual(expectedcolumns);
expect(mockedProcessedData.data).toEqual(expecteddata);
});
I have about 5 requests that are made when my view loads. This is for an edit form:
var reqA = function() {...};
var reqB = function() {...};
var reqC = function() {...};
var reqD = function() {...};
var reqE = function() {...};
Now I want reqA() and reqB() to load asynchronously and if possible return a single promise.
reqC() and reqD() should only load after reqA() and reqB() have finished and executed their promise.
reqE() should only load after reqC() and reqD().
This is what I want to do, however I have no idea how to go about it. I can load them all asynchronously or one after another by chaining promises but not in the way I want.
If your functions all use the $http provider, it is easy to accomplish. You will want to modify your functions to return the result of the $http call like this
function reqA() {
return $http.get(...);
}
Now that they are returning promises, you can easily use the $q provider to orchestrate your requests:
$q.all([reqA(), reqB()])
.then( () =>
$q.all([reqC(), reqD()])
.then(() => reqE())
);
For regular JS (non ES6):
$q.all([reqA(), reqB()])
.then(function() {
$q.all([reqC, reqD()])
.then(function() {
reqE();
});
});
If you don't want to return the result of $http in your functions, you will need to set them up to return a promise in one way or the other:
function reqA() {
var deferred = $q.defer();
... your request code ...
// call this if everything is ok
deferred.resolve();
// call this if there was an error
deferred.reject();
return deferred.promise;
}
Here is an example that you can take a look at that might make it clearer. It makes use of Promise which is globally available in almost all javascript environments (excluding IE and older node versions).
angular
.module('example', [])
.run(function($q) {
function requestOne() {
return $q.when("one")
}
function requestTwo() {
return $q.when("two")
}
function requestThree() {
return $q.when("three")
}
function requestFour() {
return $q.when("four")
}
function requestFive() {
return $q.when("five")
}
$q
.all([requestOne(), requestTwo()])
.then(function(responses){
console.log(responses[0] === "one");
console.log(responses[1] === "two");
return $q.all([requestThree(), requestFour()]);
})
.then(function(nextResponses){
console.log(nextResponses[0] === "three");
console.log(nextResponses[1] === "four")
return requestFive();
})
.then(function(lastResponse){
console.log(lastResponse === "five")
})
});
angular.element(document).ready(function() {
angular.bootstrap(document, [ 'example' ]);
});
I assume that you are using angular 1, if you aren't there Promise is available in the global scope in almost all environments.
I have the following function. It doesn't work, but the idea is that if a sessionStorage variable is already set, use it. If not, make a call to the API and then set the sessionVariable for next time.
isAdmin: function() {
// hit the first page, or refreshed.
if(sessionStorage.getItem("isAdministrator") === null) {
AzureGroupService.get({adGroup: "Programmers"}).$promise.then(function (data) {
sessionStorage["isAdministrator"] === data.isMember;
return data.isMember;
});
}
else {
return JSON.parse(sessionStorage["isAdministrator"]);
}
}
So the function might return immediately, or it might need to wait on the API. Is this even close to the right way to do this?
If so, how would I call it in a controller? Something like this?
user.isAdmin().then(function(response) {
vm.test = response.data;
});
You can use $q.when() , if the sessionStorage object is not null. Your isAdmin() function should look like this.
isAdmin: function() {
// hit the first page, or refreshed.
if(sessionStorage.getItem("isAdministrator") === null) {
return AzureGroupService.get({adGroup: "Programmers"}).$promise.then(function (data) {
sessionStorage["isAdministrator"] === data.isMember;
return data.isMember;
});
}
else {
return $q.when(JSON.parse(sessionStorage["isAdministrator"]));
}
}
Your controller should look like this , isAdmin() function will always return a promise . So u can define a success callback using .then().
user.isAdmin().then(function(response) {
vm.test = response.data;
});
I'm not really sure about this issue but it seems that sometimes when I activate $watch for a function then it doesn't work.
for example I have this simple service
angular.module('sp-app').factory('mediaSources', function() {
var storages = [];
return {
addStorage: function(storage) {
storages.push(storage);
},
getStorages: function() {
return storages;
}
}
});
and when I watch getStorage method in order to update my view it doesn't call change callback or calls only at initialization stage
$scope.$watch(function($scope) {
return mediaSources.getStorages();
}, function() {
console.log('call')
});
and I can only track changes by watching length property of returned array
return mediaSources.getStorages().length;
and I wonder because I have written similar think somewhere else within my application and it works fine.
If i interpret what you are trying to do, you should not need to set a watch on something like this, you can just use a factory like so :
angular.module('app').factory('mediaSources', function(){
var storages = {};
storages.list = [];
storages.add = function(message){
storages.list.push(message);
};
return storages;
});
then in the controller you want to receive/update the data to for instance, you would do
$scope.myControllerVar = mediaSources.list;
No need to watch over it, it should update for you.
You will have to set up watcher with equality flag as the third argument:
$scope.$watch(function($scope) {
return mediaSources.getStorages();
}, function() {
console.log('call');
}, true);
I have a little question about multiple promise.
How can I wait that all promises will be done for return the final result.
See my code :
getInfo : function(){
return promiseA.then(function(result){
info = result;
//this function have also promises
return ServiceA.functionA(info.login)
.then(function(favouriteItems){
info.favorites = favouriteItems;
return $q.when(info);
});
});
},
My aims it's to wait the result of ServiceA.functionA before return value.
Thanks
K.L
I wrote an answer to another question stating the solution to this problem using the $q.all approach.
Check it out: AngularJS: Listen to events, one after the other
You need to use $q.all()
This is a good post here on the question: stackoverflow.com/questions/21310964/angularjs-q-all
function getInfo() {
var deferred = $q.defer();
promiseA.then(function(result){
info = result;
// this function have also promises
ServiceA.functionA(info.login)
.then(function(favouriteItems){
info.favorites = favouriteItems;
// the magic
deferred.resolve(info);
// at this point, you can resolve any value
});
});
}
return deferred.promise;
}
then later you can call that function and get a promise...
var promise = getInfo();
promise.then(successCallback, errorCallback, notifyCallback);
The successCallback will only be called once resolve has been called on the deferred object.
getInfo : function() {
return promiseA.then(function(result){
return ServiceA.functionA(result.login).then(function(favouriteItems){
result.favorites = favouriteItems;
return result;
});
});
},
Use like this:
api.getInfo().then(function(result){
// use result and result.favorites
}, function(err){
// here you can be if an error occurred in promiseA or in ServiceA.functionA
})