I've created a Factory to retrieve customers:
customerModule.factory("CustomerFactory", ["$http", "DataService", function ($http, dataService) {
var self = this;
self.page = 1;
self.query = "";
var queryFn = function (query) {
self.query = query;
$http.get(dataService.baseURI + "/customers", {
params: {
query: self.query,
page: self.page,
pageSize: 50
}
}).success(function (data) {
return data.result;
});
};
return {
query: queryFn
};
}]);
But when I use it in my controller, it returns undefined, I'm not sure what I'm doing wrong here?
$scope.$watch("query", function () {
if ($scope.query && $scope.query.length > 3) {
console.log(customerFactory.query($scope.query));
}
else if ($scope.query.length < 4) {
$scope.customers = [];
}
});
If you take a close look at your queryFn function you will notice that you're not actually returning anything from it. This means that queryFn will return undefined.
So the first step to fixing your code is to put return in front of $http.
Second step is fixing the way you are calling customerFactory.query function. You expect it to return the value immediately, but $http actually creates an XHR request, and XHR requests are async by default, so you cannot expect customerFactory.query to return the result right away.
If you haven't learned of promises than this is the time to take a look at them, because they are used all over the place, in angular world.
Assuming that you have put return in front of $http, the call to customerFactory.query will actually return a promise object. That promise object has a .then method. This method should be used as:
customerFactory.query($scope.query)
.then(function(customers){
console.log(customers);
});
Related
I have a piece of Code in angularjs. If I hard code the value of http response it is displaying the response when I use the http method in angularjs it is not displaying. Whenever request sends to server i get error function. I dont know where I am wrong. Here is the code
(function() {
'use strict';
angular
.module('MyApp', ['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
.controller('DemoCtrl', DemoCtrl);
function DemoCtrl($timeout, $q, $log, $http, $scope) {
var self = this;
self.simulateQuery = false;
self.isDisabled = false;
self.repos = loadAll();
self.querySearch = querySearch;
self.selectedItemChange = selectedItemChange;
self.searchTextChange = searchTextChange;
function querySearch(query) {
var results = query ? self.repos.filter(createFilterFor(query)) : self.repos,
deferred;
if (self.simulateQuery) {
deferred = $q.defer();
$timeout(function() {
deferred.resolve(results);
}, Math.random() * 1000, false);
return deferred.promise;
} else {
return results;
}
}
function searchTextChange(text) {
$log.info('Text changed to ' + text);
}
function selectedItemChange(item) {
$log.info('Item changed to ' + JSON.stringify(item));
}
function loadAll() {
$log.info('test');
var repos;
repos = [];
$http.get('http://melkban24.ir/city/json/2').success(function(response) {
$scope.repos = response.data;
});
return repos.map(function(repo) {
repo.value = repo.nameCity.toLowerCase();
$log.info(repo.value);
return repo;
});
}
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.value.indexOf(lowercaseQuery) === 0);
};
}
}
})();
$http.get() is asynchronous, so the .success() callback won't be called until after your function has returned. That means loadAll() cannot return the data. Try not to confuse $scope.repos with the local variable repos as they are quite different things.
Don't use the deprecated .success() method at all. Use .then() as it will return a promise which is compatible with other uses of promises in angular.
Move the map code inside the .then callback and if you want loadAll() to return anything make it return the promise that .then() returns. That way anything that calls loadAll() can wait on the promise to complete.
function loadAll() {
return $http.get('http://melkban24.ir/city/json/2').then(function(result){
var repos = result.data.data;
return repos.map(function (repo) {
repo.value = repo.nameCity.toLowerCase();
return repo;
});
$scope.repos = repos;
});
}
Now you have two ways to get at the data: it will appear as the repos value in the scope once it has been retrieved. If used in an angular template the page will show the new data. Or call loadAll() and use the promise to get at the returned data:
loadAll().then(function(repos) { ... });
You should also consider including code for the case where $http.get() fails. Pass it an error callback as well.
Also, as #Rakeschand points out in the comments, the next step should be to move all the $http code out of the controller and into a service. You still end up calling a function that returns a promise, but code to convert the received data into the data you actually want can be removed from the controller.
I am trying to understand the difference between those two http calls in my factory:
One: function(code) {
return $http.get('api/team/' + code)
.then(function(resp) {
return resp.data;
});
}
}
And
Two: function(code) {
var promise = $http.get('api/team/' + code);
promise.then(function(resp) {
return resp.data;
});
return promise;
}
If I use Two in resolve in config:
resolve: {
testt: ['$route','MyService',
function($route, MyService) {
return MyService.Two($route.current.params.code);
}]
}
Then I can see the data in my ng-view. If I use One, I dont see the data in ng-view.
My controller:
.controller('TeamDetailsCtrl',
['MyService','testt',
function(MyService,testt) {
var self = this;
self.team = testt.data;
}]);
So, what is the difference?
Best Regards
One: When $http.get() is done, it'll resolve and continue into the then. This returns another promise, filled with the result of the then function result.data. One returns the second promise.
Two: Returns the original promise from $http.get() with the result result, (in which you haven't transformed result into result.data). The correct syntax for two could be:
Two: function(code) {
var promise = $http.get('api/team/' + code);
var promise2 = promise.then(function(resp) {
return resp.data;
});
return promise2;
}
One returns a promise, which will eventually return resp.data.
If you use One, resp.data === testt in your controller. testt.data doesn't work because resp.data.data doesn't work.
If you want to use One, change self.team = testt.data to self.team = testt.
I have a factory to get an array with all my clientes from the database.
Then i need to filter this array by the person id and show only it's data in a single page.
I have a working code already, but it's only inside a controller and I want to use it with a factory and a directive since i'm no longer using ng-controller and this factory already make a call to other pages where I need to show client data.
This is what i tried to do with my factory:
app.js
app.factory('mainFactory', function($http){
var getCliente = function($scope) {
return $http.get("scripts/php/db.php?action=get_cliente")
.success( function(data) {
return data;
})
.error(function(data) {
});
};
var getDetCliente = function($scope,$routeParams) {
getCliente();
var mycli = data;
var myid = $routeParams.id;
for (var d = 0, len = mycli.length; d < len; d += 1) {
if (mycli[d].id === myid) {
return mycli[d];
}
}
return mycli;
};
return {
getCliente: getCliente,
getDetCliente: getDetCliente
}
});
app.directive('detClienteTable', function (mainFactory) {
return {
restrict: "A",
templateUrl: "content/view/det_cliente_table.html",
link: function($scope) {
mainFactory.getDetCliente().then(function(mycli) {
$scope.pagedCliente = mycli;
})
}
}
});
detClient.html
<p>{{pagedCliente.name}}</p>
<p>{{pagedCliente.tel}}</p>
<p>{{pagedCliente.email}}</p>
[...more code...]
The problem is, I'm not able to get any data to show in the page, and also, i have no errors in my console.
What may be wrong?
Keep in mind I'm learning AngularJS.
Basically you need to implement a promise chain as look into your code looks like you are carrying getCliente() promise to getDetCliente method. In that case you need to use .then function instead of using .success & .error which doesn't allow you to continue promise chain. There after from getDetCliente function you again need to use .then function that gets call when getCliente function gets resolved his promise. Your code will reformat it using form it and return mycli result.
Code
var getCliente = function() {
return $http.get("scripts/php/db.php?action=get_cliente")
.then( function(res) { //success callback
return res.data;
},function(err){ //error callback
console.log(err)
})
};
var getDetCliente = function(id) {
return getCliente().then(function(data){
var mycli = data;
var myid = id;
for (var d = 0, len = mycli.length; d < len; d += 1) {
if (mycli[d].id === myid) {
return mycli[d];
}
}
return mycli;
})
};
Edit
You shouldn't pass controller $scope to the service that will make tight coupling with you directive and controller, Also you want to pass id parameter of your route then you need to pass it from directive service call
link: function($scope) {
mainFactory.getDetCliente($routeParams.id).then(function(mycli) {
$scope.pagedCliente = mycli;
})
}
You are treating getCliente as a synchronous call in getDetCliente. Interestingly in your directive you understand that the getDetCliente is asynchronous. Change getCliente to this and treat it as an asynchronous call when you call it in getDetCliente:
var getCliente = function($scope) {
return $http.get("scripts/php/db.php?action=get_cliente");
};
UPDATE
I am currently doing this, and I'm not sure why it works, but I don't think this is the correct approach. I might be abusing digest cycles, whatever those are.
First, I want to have the array navigationList be inside a service so I can pass it anywhere. That service will also update that array.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigationList = [];
var getNavigation = function() {
ExtService.getUrl('navigation.json').then(function(data) {
angular.copy(data.navigationList, navigationList);
});
}
return{
getNavigation: getNavigation,
navigationList: navigationList,
}
}]);
Then in my controller, I call the service to update the array, then I point the scope variable to it.
ChapterService.getNavigation();
$scope.navigationList = ChapterService.navigationList;
console.log($scope.navigationList);
But this is where it gets weird. console.log returns an empty array [], BUT I have an ng-repeat in my html that uses $scope.navigationList, and it's correctly displaying the items in that array... I think this has something to do with digest cycles, but I'm not sure. Could anyone explain it to me and tell me if I'm approaching this the wrong way?
I have a main factory that runs functions and calculations. I am trying to run
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
return: {
navigation: navigation
}
I did a console.log on the data before it gets returned and it's the correct data, but for some reason, I can't return it..
The ExtService that has the getUrl method is just the one that's typically used (it returns a promise)
In my controller, I want to do something like
$scope.navigation = ChapterService.navigation.getNavigationData();
in order to load the data from the file when the app initializes,
but that doesn't work and when I run
console.log(ChapterService.navigation.getNavigationData());
I get null, but I don't know why. Should I use app.run() for something like this? I need this data to be loaded before anything else is done and I don't think I'm using the best approach...
EDIT
I'm not sure if I should do something similar to what's being done in this jsfiddle, the pattern is unfamiliar to me, so I'm not sure how to re purpose it for my needs
My code for ExtService is
app.factory('ExtService', function($http, $q, $compile) {
return {
getUrl: function(url) {
var newurl = url + "?nocache=" + (new Date()).getTime();
var deferred = $q.defer();
$http.get(newurl, {cache: false})
.success(function (data) {
deferred.resolve(data);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
});
EDIT 2
I'd like to separate the request logic away from the controller, but at the same time, have it done when the app starts. I'd like the service function to just return the data, so I don't have to do further .then or .success on it...
You are using promises incorrectly. Think about what this means:
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
getNavigationData is a function that doesn't return anything. When you're in the "then" clause, you're in a different function so return data only returns from the inner function. In fact, .then(function(data) { return data; }) is a no-op.
The important thing to understand about promises is that once you're in the promise paradigm, it's difficult to get out of it - your best bet is to stay inside it.
So first, return a promise from your function:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json');
}
}
return {
navigation: navigation
}
}])
Then use that promise in your controller:
app.controller('MyController', function($scope, ExtService) {
ExtService
.navigation
.getNavigationData()
.then(function(data) {
$scope.navigation = data;
});
})
Update
If you really want to avoid the promise paradigm, try the following, although I recommend thoroughly understanding the implications of this approach before doing so. The object you return from the service isn't immediately populated but once the call returns, Angular will complete a digest cycle and any scope bindings will be refreshed.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
// create an object to return from the function
var returnData = { };
// set the properties of the object when the call has returned
ExtService.getUrl('navigation.json')
.then(function(x) { returnData.nav = x });
// return the object - NB at this point in the function,
// the .nav property has not been set
return returnData;
}
}
return {
navigation: navigation
}
}])
app.controller('MyController', function($scope, ExtService) {
// assign $scope.navigation to the object returned
// from the function - NB at this point the .nav property
// has not been set, your bindings will need to refer to
// $scope.navigation.nav
$scope.navigation = ExtService
.navigation
.getNavigationData();
})
You are using a promise, so return that promise and use the resolve (.then) in the controller:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json'); // returns a promise
});
}
return: {
navigation: navigation
}
}
controller:
ChapterService
.navigation
.getNavigationData()
.then(function (data) {
// success
$scope.navigation = data;
}, function (response) {
// fail
});
This is a different approach, I don't know what your data looks like so I am not able to test it for you.
Controller
.controller('homeCtrl', function($scope, $routeParams, ChapterService) {
ChapterService.getNavigationData();
})
Factory
.factory('ChapterService', [ 'ExtService', function(ExtService) {
function makeRequest(response) {
return ExtService.getUrl('navigation.json')
}
function parseResponse(response) {
retries = 0;
if (!response) {
return false;
}
return response.data;
}
var navigation = {
getNavigationData: function () {
return makeRequest()
.then(parseResponse)
.catch(function(err){
console.log(err);
});
}
}
return navigation;
}])
I have a problem when calling a service created using .factory in my controller.
The code looks like the following.
Factory (app.js):
.factory('Database',function($http){
return {
getDatabase: function(){
var database = {};
$http.get('http://localhost:3001/lookup').
success(function(data){
database.companyInfo = data.info.companyInfo;
});
}).
error(function(data){
console.log('Error' + data);
});
return database;
}
};
})
Controller:
angular.module('webClientApp')
.controller('MainCtrl', function (Database,Features,$scope,$http) {
$scope.databaseString = [];
$scope.quarters = ['Q1','Q2','Q3','Q4'];
$scope.years = ['2004','2005','2006','2007','2008','2009','2010',
'2011','2012','2013','2014'];
$scope.features = Features.getFeatures();
$scope.database = Database.getDatabase();
console.log($scope.database);
Now when I inspect the element in Firebug I get the console.log($scope.database) printed out before the GET statement result. $scope.database is shown as an Object {} with all the proprieties in place.
However if I try to use console.log($scope.database.companyInfo) I get an undefined as result, while instead I should get that data.info.companyInfo' that I passed from theDatabase` service (in this case an array).
What is the problem here? Can someone help me?
(If you need clarifications please let me know..)
The $http.get() call is asynchronous and makes use of promise objects. So, based on the code you provided it seems most likely that you are outputting the $scope.database before the success method is run in the service.
I build all my service methods to pass in a success or failure function. This would be the service:
.factory('Database',function($http){
return {
getDatabase: function(onSuccuess,onFailure){
var database = {};
$http.get('http://localhost:3001/lookup').
success(onSuccess).
error(onFailure);
}
};
})
This would be the controller code:
angular.module('webClientApp')
.controller('MainCtrl', function (Database,Features,$scope,$http) {
$scope.databaseString = [];
$scope.quarters = ['Q1','Q2','Q3','Q4'];
$scope.years = ['2004','2005','2006','2007','2008','2009','2010',
'2011','2012','2013','2014'];
$scope.features = Features.getFeatures();
Database.getDatabase(successFunction,failureFunction);
successFunction = function(data){
$scope.database = data.info.companyInfo;
console.log($scope.database);
});
failureFunction = function(data){
console.log('Error' + data);
}
Change your code in the following way:
.factory('Database',function($http){
return {
getDatabase: function(){
return $http.get('http://localhost:3001/lookup');
}
};
})
Then get the response in controller(Promise chain)
Database.getDatabase()
.then(function(data){
//assign value
})
.catch(function(){
})