I have set up a PouchDb service in Angular/Ionic which works fine within the service, but fails when I try to pass the data I retrieve from PouchDB service to my controller.
this is my service
angular.module('myApp', [])
.service('DBService', function($q) {
var items;
var db;
var self = this;
this.initDB = function() {
return db = new PouchDB('simpleDB', {
adapter: 'websql'
});
};
this.storeData = function() {
return $q.when(
db.put({
_id: 'mydoc',
title: 'some text'
}).then(function (response) {
// handle response
console.log('done')
}).catch(function (err) {
console.log(err);
})
)
};
this.getData = function(){
return $q.when(
db.get('mydoc').then(function (doc) {
// handle doc
console.log(doc); // I see the data in console
}).catch(function (err) {
console.log(err);
})
)
}
})
and this is the controller
angular.module('myApp', [])
.controller('mainCtrl', function($scope, DBService, $q) {
$scope.getData = function(){
DBService.initDB()
DBService.getData().then(function(data){
console.log(data)
})
}
when I use then() I get error TypeError: Cannot read property 'then' of undefined.
Can anyone help me figure out how I can pass the data correctly from my service to the controller?
I was lucky to find the error quickly! I was not returning the data in my service so I changed it to
this.getData = function(){
return $q.when(
db.get('mydoc').then(function (doc) {
// handle doc
return doc; // I added this
console.log(doc); // I see the data in console
}).catch(function (err) {
console.log(err);
})
)
}
and the reason for the then() returning undefined was that accidentally I had omitted return $q.when( from the second line of the this.getData function! Now I have my data in the controller just as I needed!
Related
I have this array I am getting through the following method:
var url= *url defined here*;
$scope.ViewProfile = function () {
$http.get(url)
.success(function (response) {
$scope.ProfileList = response;
$scope.FavNumbers = $scope.ProfileList[0].FavNumbers;
})
.error(function () {
});
}
I am required to edit the Fav Numbers list on the UI. and post it back to another url through http post url method. What I am stuck is with the concept of asynchronous calls, due to which I am unable to retrieve the favorite numbers list to be available for editing. Please help!
I have tried a method of using promises as follows:
app.factory('myService', function($http) {
var myService = {
async: function(url) {
var promise = $http.get(url).then(function (response) {
console.log(response);
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return myService;
});
In my controller I am doing:
angular.module('JuryApp').controller('mycontroller', ['myService', function (myService) {
myService.async(url).then(function(d) {
$scope.data = d;
});
app.controller('MainCtrl', function( myService,$scope) {
// Call the async method and then do stuff with what is returned inside our own then function
myService.async().then(function(d) {
$scope.data = d;
});
});
But I keep getting the error 'd is not defined'. It keeps giving an error of some sort, where the debugger goes into an infinite loop or something.
You are overcomplicating it, I think. Async calls are actually pretty simple:
You're service:
app.factory("myService", ["$http", function($http) {
var MyService = {
getData: function(url) {
return $http.get(url); //$http returns a promise by default
}
};
return MyService;
})];
Your controller:
angular.module('JuryApp').controller('mycontroller', ['myService', function (myService) {
$scope.FavNumbers = [];
var url = "http://my.api.com/";
myService.getData(url).then(function(response) {
$scope.FavNumbers = response.data[0].FavNumbers;
});
}]);
That's all that you need to do.
Can someone please tell me the best way to run tests on my controller function getData and the factory function too. I've very confused and don't know where to start. How would you write tests for the code below?
myApp.controller('myController', ['$scope', 'myFactory', function ($scope, myFactory) {
$scope.getData = function(id) {
var promise = myFactory.GetData('/dta/GetData?Id=' + id);
promise
.then(function (success) {
$scope.result = success;
}, function (error) {
$scope.error = true;
});
}
});
myApp.factory('myFactory', ['$http', function ($http) {
return {
GetData: function (url) {
return $http.get(url)
.then(function (response) {
return response.data;
}, function (error) {
return error;
});
}
}
}]);
You'll want to test each component in isolation (that's what unit tests are for). So something like this for the controller
describe('myController test', () => {
let scope, myFactory;
beforeEach(() => {
myFactory = jasmine.createSpyObj('myFactory', ['GetData']);
module('your-module-name');
inject(function($rootScope, $controller) {
scope = $rootScope.$new();
$controller('myController', {
$scope: scope,
myFactory: myfactory
});
});
});
it('getData assigns result on success', inject(function($q) {
let id = 1, success = 'success';
myFactory.GetData.and.returnValue($q.when(success));
scope.getData(id);
expect(myFactory.GetData).toHaveBeenCalledWith('/dta/GetData?Id=' + id);
scope.$digest(); // resolve promises
expect(scope.result).toBe(success);
}));
it('getData assigns error on rejections', inject(function($q) {
myFactory.GetData.and.returnValue($q.reject('error'));
scope.getData('whatever');
scope.$digest();
expect(scope.error).toEqual(true);
}));
});
For your factory, you would create a separate describe and inject and configure $httpBackend. There are plenty of example in the documentation.
FYI, you should omit the error handler in your factory, ie
return $http.get(url).then(response => response.data);
or if you don't like ES2015
return $http.get(url).then(function(response) {
return response.data;
});
as you are currently converting a failed request into a successful promise.
In fact, I'd go a bit further to make your GetData factory more useful than a mere $http wrapper
GetData: function(id) {
return $http.get('/dta/GetData', {
params: { Id: id }
}).then(function(res) {
return res.data;
});
}
I found this plnkr link on the web but I need use it with 2 or 3 more ajax calls which doesn't require an argument from the first ajax call. How can I do it with error handling?
var app = angular.module("app", []);
app.service("githubService", function($http, $q) {
var deferred = $q.defer();
this.getAccount = function() {
return $http.get('https://api.github.com/users/haroldrv')
.then(function(response) {
// promise is fulfilled
deferred.resolve(response.data);
return deferred.promise;
}, function(response) {
// the following line rejects the promise
deferred.reject(response);
return deferred.promise;
});
};
});
app.controller("promiseController", function($scope, $q, githubService) {
githubService.getAccount()
.then(
function(result) {
// promise was fullfilled (regardless of outcome)
// checks for information will be peformed here
$scope.account = result;
},
function(error) {
// handle errors here
console.log(error.statusText);
}
);
});
http://plnkr.co/edit/kACAcbCUIGSLRHV0qojK?p=preview
You can use $q.all
var promises=[
$http.get(URL1),
$http.get(URL2),
$http.get(URL3),
$http.get(URL4)
];
$q.all(promises).then(function(response){
console.log('Response of Url1', response[0]);
console.log('Response of Url2', response[1]);
console.log('Response of Url3', response[2]);
console.log('Response of Url4', response[3]);
}, function(error){
});
I have forked your plunkr with $q
First you should make deferred variable local for each ajax call you want to return a promise. So, you have to create 2-3 functions (as many as your ajax calls) and keep them in an array. Then you should use:
$q.all([ajax1,ajax2,ajax3]).then(function(values){
console.log(values[0]); // value ajax1
console.log(values[1]); // value ajax2
console.log(values[2]);}); //value ajax3
example:
function ajax_N() {
var deferred = $q.defer();
http(...).then((response) => {
deferred.resolve(response);
}, (error) => {
deferred.reject(error);
});
return deferred.promise;
}
$q.all([
ajax_1,ajax_2,ajax_3
]).then(function(values) {
console.log(values);
return values;
});
Use $q.all in this case. It will call getAccount and getSomeThing api same time.
var app = angular.module("app", []);
app.service("githubService", function($http, $q) {
return {
getAccount: function () {
return $http.get('https://api.github.com/users/haroldrv');
},
getSomeThing: function () {
return $http.get('some thing url');
}
};
});
app.controller("promiseController", function($scope, $q, githubService) {
function initData () {
$q.all([githubService.getAccount(), githubService.getSomeThing()])
.then(
function (data) {
$scope.account = data[0];
$scope.someThing = data[1];
},
function (error) {
}
);
}
});
I'm trying to work out why the response of this service isn't saving to $scope.counter. I've added a function to my service fetchCustomers(p) which takes some parameters and returns a number, which I'd like to save to $scope.counter.
service
angular.module('app')
.factory('MyService', MyService)
function MyService($http) {
var url = 'URL'';
return {
fetchTotal: function(p) {
return $http.get(url, { params: p })
.then(function(response) {
return response.data.meta.total;
}, function(error) {
console.log("error occured");
})
}
}
}
controller
$scope.counter = MyService.fetchTotal(params).then(function(response) {
console.log(response);
return response;
});
For some reason though, this isn't working. It's console logging the value, but not saving it to $scope.counter. Any ideas?
If I understand your question correctly, you're setting $scope.counter to a promise, not the response.
MyService.fetchTotal(params).then(function(response) {
// Anything dealing with data from the server
// must be put inside this callback
$scope.counter = response;
console.log($scope.counter); // Data from server
});
// Don't do this
console.log($scope.counter); // Undefined
My factory does not seem to execute my $http.get. Here's my controller:
app.factory("myService", function($http) {
var myService = {
retrieve: function(id, type) {
var retrievedData = {
device: {},
childDevices: [],
error: {}
};
.
.
.
$http.get(url, headers)
.success(function(data, status) {
// some data post-processing
// some logs
})
.error(function(data, status) {
// some data post-processing
// some logs
});
return retrievedData;
};
return myService;
});
The logs within the $http.get do not print.
I read somewhere I need to use promise, but most examples I saw return $http.get directly. I don't want to return $http.get right away as I need to make some modification on the data in the factory rather than in the controller.
Thanks.
in angular js promise is used for returning response which come from backend. in your code retrievedData return before response come from backend. that's why angular use promise. so when response come from backend it execute success block and success block resolve the promise using deferred.resolve(data) . when error occure then error block get execute and it rejected the promise deferred.reject(err) .but when it resolve the promise it return the response data.
Service Code:
app.factory("myService", function ($http,$q) {
var myService = {
retrieve: function (id, type) {
var deferred = $q.defer();
var retrievedData = {
device: {},
childDevices: [],
error: {}
};
$http.get(url, headers).then(function (data) {
// after success this block execute
deferred.resolve(data);
},
function (err) {
//after error this block execute
deferred.reject(err);
});
return deferred.promise;
}
};
return myService;
});
controller code :
//calling retrive function
myService.retrive(id,type).then(fucntion(data){
...
})
.catch(function(){
...
})
Because when you return your retrievedData it hasn't been retrieved yet. That's why you should use promises:
app.factory("myService", function ($http,$q) {
var myService = {
retrieve: function (id, type) {
var deferred = $q.defer();
var retrievedData = {
device: {},
childDevices: [],
error: {}
};
$http.get(url, headers).then(function (data) {
// success do your success things here and return data
deferred.resolve(data);
},
function (err) {
deferred.reject(err);
});
return deferred.promise;
}
};
return myService;
});
// usage
myService.retrive(id,type).then(function(data){
// data retrived
})