Calling API in Angular service with variables - javascript

I'm currently trying to access the access the Google Books API, which I have successfully managed to do directly in my controller, but for the sake of best practices I am wanting to move the business logic into a directive.
HTML
<form ng-submit="search(data)">
<div class="list">
<div class="list list-inset">
<label class="item item-input">
<input type="text" placeholder="Search by Book Title" ng-model="data.isbn">
</label>
</div>
<button class="button button-block button-positive" type="submit">
Find Book
</button>
</div>
Above I'm attaching data.isbn to the search() function.
Controller.js
.controller('DashCtrl', function($scope, BookSearch) {
$scope.search = function(data) {
$scope.books = BookSearch(data).all();
};
})
I am then feeding that parameter to my BookSearch service.
Service.js
.factory('BookSearch', function ($http) {
return function(data) {
var api = "https://www.googleapis.com/books/v1/volumes?q=" + data;
// Simple GET request example :
$http.get(api).
success(function(data, status, headers, config) {
var books = data;
}).
error(function(data, status, headers, config) {
console.log(status);
});
}
return {
all: function() {
return books;
}
}
});
In the services.js I am wanting to access the Books API from Google with the search parameter and then write several functions, one of them being all() to simply return the books to $scope.books in the controller.
I'm receiving this error:
TypeError: Cannot read property 'all' of undefined
When I try to make an http call inside the all() function, then $http.get does not even execute, as I am unable to log neither success nor error case and $scope.books remains undefined.
I am trying to stick with best practices in building a neat and scalable app and learning angular the way it should be.
I've looked at so many tutorials but they only ever use $http.get calls to static JSON files without parameters from the user.
Could anyone please advise whether I am going about this the right way and where my error is?

Think you're having trouble understanding how promises work in angular. Promises are asynchronous so you can't just do $scope.books = BookSearch(data).all();. You need to wait for the response from BookSearch(data).all() and then set the $scope.books in a success callback function. Hopefully this article will help you
Change your service to
.factory('BookSearch', function ($http) {
return {
all: function(data) {
var api = "https://www.googleapis.com/books/v1/volumes?q=" + data;
// Simple GET request example :
return $http.get(api).
success(function(data, status, headers, config) {
// This is the callback function when the $http.get completes successfully so you can remove it
return data;
}).
error(function(data, status, headers, config) {
console.log(status);
});
}
}
});
and your controller to:
.controller('DashCtrl', function($scope, BookSearch) {
$scope.search = function(data) {
BookSearch(data).all().then(function(data) {
// This is the callback function when BookSearch(data).all completes successfully
$scope.books = data;
}
};
})

Related

Ionic1 Factory $http request returning promise

I have a ionic1 application which works with my API endpoints.
I've created a service which on call gets categories.
.factory('expenses', function($http) {
var expenseCategories = [];
return {
getCategories: function(){
return $http.get('URL_HERE').then(function(response){
expenseCategories = response.data.data[0];
return expenseCategories.categories;
});
}
}
})
I call the service in my controller
.controller('expenseCategoryCtrl', function($scope, $state, expenses) {
$scope.categories = expenses;
$scope.data = $scope.categories.getCategories();
console.log($scope.data);
})
When console logging the response it returns a promise rather than my usual json from the API. If I call the API in the controller I can ng-repeat the collection with no issues however now it's in a service it's wrapped in this "promise" and I'm unsure how to output the collection.
I've tried
<li class="item item-toggle" ng-repeat="categories as category">
{{ category }}
</li>
but I get the error
[ngRepeat:iexp] Expected expression in form of 'item in
collection[ track by id]' but got 'categories as category'.
I believe this is a case of not understanding the promise.
hope this will work:
.controller('expenseCategoryCtrl', function($scope, $state, expenses) {
$scope.categories = expenses;
$scope.data = $scope.categories.getCategories().then(function(response){
console.log(response);
},function(error){
console.log(error);
});
console.log($scope.data);
})
Async operation doesn't work the way you're thinking. You will get data when promise accomplished. So on getCategories method you could put .then function and it will call successFn one promise fulfilled otherwise on error it calls errorFn.
$scope.categories.getCategories().then(function successFn(data){
$scope.data = data
}, function errorFn(error){ console.log(error) });
In console what you were getting was promise object returned by $http service as you directly assigned promise to $scope.data.

Watching for changes inside ng-repeat on mongoose update from different controller

I have come across a really specific problem, that I was wondering someone could help me out with.
So, I have a department controller which loads a mongodb collection and displays it inside a table with ng-repeat. Each document has a "edit" link to modify itself. The edit link, opens up a modal box which is a different controller. I make a PUT request from the modal controller and successfully update the document on my server.
The problem is as soon as I get out of the modal box, my page shows old data until i refresh the page. Is there a way to "watch" the specific row of document in the ng-repeat area. I want to make sure the specific row gets modified right away after the update operation from different controller. A solution that I found already works is reloading the whole page & controller (so that the whole collection would be reloaded with GET, but that would use extra resource and doesn’t seem like the right way to solve it).
Here is the simplified code (I have commented the places where I think the changes should happen):
department.html:
<tr dir-paginate="department in departments">
<td>{{department.id}}</td>
<td>{{department.name}}</td>
<td>{{department.office}}</td>
<td>{{department.phone}}</td>
<td> Edit </td>
</tr
departmentController:
$http({
method: 'GET',
url: '/departments'
}).success(function(data, status, headers, config) {
$scope.departments = data;
})
.error(function(data, status, headers, config) {
console.log(" Not Doneee "+ status+data+headers+config);
});
//I was wondering if I could set up a watch in modifyDept?? The modalInstance however creates a new modal with separate controller.
$scope.modifyDept = function (depID){
var modalInstance = $uibModal.open({
templateUrl: 'partials/deptModal.html',
controller: 'deptModalController',
resolve: {
depID: function () {
return depID;
}
}
});
};
modalController for 'partials/deptModal.html':
$scope.newID = angular.copy(depID);
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
$scope.modify = function () {
$http({
method: 'PUT',
url: '/departments',
headers: {
'Content-Type': 'application/json'
},
data: {
newID: $scope.newID
}
}).success(function(data, status, headers, config) {
$uibModalInstance.dismiss('cancel');
if (status === 200) {
// I believe something could also be done here to notify the parent controller (departmentController) maybe via a service?
notificationFactory.info("Successfully updated: "+$scope.newID.name)
}
})
.error(function(data, status, headers, config) {
$uibModalInstance.dismiss('cancel');
notificationFactory.error("Error: Status Code "+status+". Contact admin if issue persists.")
});
};
This is the first time I am working with angular and I have a hunch I am supposed to use $watch & a factory/service but just dont know how.. Any kind of hint or suggestions would be greatly appreciated.
Add scope: $scope to the options passed in $uibModal.open method in department contoller,
departmentController:
$scope.modifyDept = function (depID){
var modalInstance = $uibModal.open({
templateUrl: 'partials/deptModal.html',
controller: 'deptModalController',
scope: $scope,
resolve: {
depID: function () {
return depID;
}
}
});
};
Now you will have access to the scope of departmentController in the modalController. In the success callback in the $scope.modify method, you can update the department in $scope.departments with the new values.

AngularJS: Routes with Multiple resolves using Services are not firing

I am configuring an AngularJS app and am having a little trouble ensuring that my promises are firing before the controllers are loading. My understanding is that this can be done. A bit of code:
First, here's the router code:
$routeProvider
.when('/apage', {
controller: 'APageController',
templateUrl: 'app/apage/apage.view.html',
requiresLogin: true,
resolve: {
"data": function($q, data1Service, data2Service) {
var data1 = data1Service.getData();
var data2 = data2Service.getData();
return $q.all({
data1: data1.$promise,
data2: data2.$promise});
}
}
})
...
Here's one of the service functions (both are similar)...
function getData() {
var deferred = $q.defer();
$http(req)
.success(function(data, status, headers, config) {
// store data ...
deferred.resolve(1); // just a flag to say it worked
$rootScope.$apply();
})
.error(function(data, status, headers, config) {
deferred.resolve(0);
$rootScope.$apply();
});
return deferred.promise;
}
And here's the controller code...
angular
.module('app')
.controller('APageController', APageController);
APageController.$inject = [... 'data'];
function APageController(... data) {
var data1 = data.data1;
var data2 = data.data2;
...
All three are in different files but part of the same module. There must be some concept I'm overlooking. Is there anything apparent here to explain why my resolve promises are not firing?
Thank you.
If you pass in object literal to q.all it will resolve immediately. Instead pass array of promises so that it waits for it to resolve reject. Also .$promise is not needed in your case because you are already returning a promise from your service(based on the displayed code). $promise is generally attached by the object returned by $resource
i.e
return $q.all([data1Service.getData(),
data2Service.getData()]);
and expect data to be array of data resolved from above 2 calls.
or create 2 resolves:
resolve: {
"data": function($q, data1Service, data2Service) {
return data1Service.getData();
},
"data2": function($q, data1Service, data2Service) {
return data2Service.getData();
}
}
and inject data and data2.
otherwise, chain through and change the response format to expect the way you were originally trying to get.
resolve: {
"data": function($q, data1Service, data2Service) {
return $q.all([data1Service.getData(),data2Service.getData()])
.then(function(response){
return {
data1:response[0],
data2:response[1]
};
});
}
}
Do not place rootScope.apply inside your service, it will cause digest already in progress error (since angular will ). $http will automatically resolve it.
You just need this.
function getData() {
return $http(req);
}

Angular, prepopulate data with .run then pass into factory

New to angular here. I am trying to data then pass it into a factory for use. I am trying to get the events to fire in a certain order with using the $http within angular. I was pointed in the direction of using .run to do the first population, pass it into the factory, which then passes it into the controllers. The factory has a function which then would allow me to "refresh" the data being shared with the controllers.
I received some great help setting up the refresh function, but I am struggling get the data to populate initially. Here is my attempt:
.run("getDataForLevels", function($http){
var getIt = $http.get("/assets/images/generated.json")
.success(function(data){
return data;
});
return getIt;
})
.factory("UserService", function($http, getDataForLevels) {
dataFactory = getDataForLevels;
dataFactory.currentData = null;
dataFactory.update = function () {
return $http.get("/assets/images/generated.json")
.success(function(data){
dataFactory.currentData = data;
return data;
});
};
return dataFactory;
});
I then Add the dependency of UserService to the controllers and pass in the data to the controllers. I seem to be betting an error [ng:areq] on this. Any/all help would be much appreciated. Thanks!!
you can't do like this :
.run("getDataForLevels", function($http){
var getIt = $http.get("/assets/images/generated.json")
.success(function(data){
return data;
});
return getIt;
})
it only take one argument, as a function.
you can do like this:
.run(function($http,$rootScope){
$http.get("/assets/images/generated.json")
.success(function(data){
$rotScope.data=data;
});
})
.factory("UserService", function($http, $rootScope) {
dataFactory.update = function () {
return $http.get("/assets/images/generated.json")
};
return dataFactory;
});
in controller inject dependencies and do like this:
var dataFactory = $rootScope.data;
dataFactory.currentData = null;
UserService.update ()
.success(function(data, status, headers) {
console.log(data);
})
.error(function(data, status, headers, config) {
console.error('error in loading File list');
});

AngularJS - API call returning [object object]

I'm trying to call a list of articles from the NPR API. I have a working URL which returns as JSON. However, somewhere in my controller, I'm getting lost getting the object. When I console.log it to test, it returns as [object Object] and nothing else. My service looks like this:
app.factory('nprService', function($resource) {
//call npr api
return $resource('http://api.npr.org/queryid=61&fields=title,byline,text,image,all&output=JSON...
and my controller:
app.controller('ArticleListCtrl', function($scope, nprService) {
//call the service and store as a variable
$scope.article = nprService.get();
});
I've tried using query to get the result, like this, but it's returning a single JSON object so that obviously didn't work.
//call the service and store as a variable
nprService.query(function(data) {
$scope.article = data;
});
Any help would be greatly appreciated. Thanks in advance.
You need to use a promise construct to get at the data. The controller code can be re-written as:
app.controller('ArticleListCtrl', function($scope, nprService) {
//call the service and store as a variable
nprService.get().then(function(result){
$scope.article = result.data;
});
The $resource.get() function returns a promise. It should be used this way:
nprService.get().success(function(data, status, headers, config){
$scope.article = data;
}).error(function(data, status, headers, config){
console.log(status);
});
Using Angular 6 you can try this
myFunctionName(){
this.http.get(`http://www.thesoftdesign.com`)
.map(res => res.json())
.subscribe(data => {
this.data = data;
console.log('data', this.data);
});
}

Categories