Using ngCsv with an external API - javascript

So I have an external API which I'm trying to access and extract the data in it using JSONP and $resource service. I want to have a button from ngCsv which makes the request then when the data request completed exports the array to a csv file. but when I click the button it saves an empty csv file because the request takes about 11s to complete. I want to click the button and when the data was ready and received completely, export the csv file.
Here's my app.js
// Initializing Application
angular.module('angelApp',['ngRoute','ngResource','ngSanitize','ngCsv'])
.config(function ($locationProvider,$routeProvider) {
$locationProvider
.html5Mode({
enabled: true,
requireBase:false
})
.hashPrefix('!');
$routeProvider
.when('/',{
templateUrl: 'displays/main.html',
controller: 'mainController'
})
.when('/extract',{
templateUrl: 'displays/extract.html',
controller: 'extractController'
})
.when('/about',{
templateUrl: 'displays/about.html',
})
.otherwise({redirectTo: '/'});
})
// Defining Controllers
// Main page Controller
.controller('mainController',['$scope',function ($scope) {
$scope.home = "Home Page!"
}])
// Extract Page Controller
.controller('extractController',['$scope','apiExtractService',function ($scope,apiExtractService) {
$scope.extract = function () {
return extractedData = apiExtractService.apiExtract();
}
}])
// Adding Services To the application
.service('apiExtractService',['$resource',function ($resource) {
this.apiExtract = function () {
var apiData = $resource("APIADDRESS",{callback: "JSON_CALLBACK"},{get:{method: "JSONP"}});
return apiData.get({filter: "FILTER",access_token:"TOKEN"});
}
}])
Here's my extract.html route.
<div class="row">
<div class="col-md-6 col-md-offset-3">
<button type="button" ng-csv="extract()" filename="test.csv">Export</button>
</div>
</div>
Thank you

As you are using $resource which returns a promise. So you need to catch the return value and return to your controller function as below
// Extract Page Controller
.controller('extractController',['$scope','apiExtractService',function ($scope,apiExtractService) {
$scope.extract = function () {
return apiExtractService.apiExtract().then(function(results){
return results;
});
}
}])

Edit:
Someone basically said the same thing before me, but I'll leave this here just in case it helps anyone :)
When you want to take action after your request has been resolved, it's a perfect case to use promises.
Make your apiExtractService.apiExtract method return a promise and take advantage of it. For example:
function apiExtract(){
var deferred = $q.defer();
$http("get", "sampleUrl", optionalData)
.success(function (response) {
deferred.resolve(response);
})
.error(function (err) {
deferred.reject(err);
});
return deferred.promise;
}
and in your controller:
.controller('extractController',['$scope','apiExtractService',function ($scope,apiExtractService) {
$scope.extract = function () {
apiExtractService.apiExtract()
.then(function(response){
return response;
}, function(err){
console.log("something went wrong");
});
}

You're returning a $resource in the apiExtract function, which uses promises. Instead of returning the promise to the extract function, you should return the actual content, which can be done using angularJS's $resource.$promise attribute.
$scope.extract = function() {
apiExtractService.apiExtract().$promise.then(function(extractedData){
return extractedData;
});
};

I too had same issue. I was getting an empty file even after using a promise. I was able to resolve it by passing in the Array instead of entire API response JSON.
API response was
{
"responseStatus": "001",
"orderList":
[{
"id": 33, "status": null, "lastUpdatedDate": null, "lastUpdatedUser": null,
"orderId": 1469830, "amount": 96, "discount": 6, "isPaid": "Y"
}]
}
In the controller
.controller('extractController', ['$scope', 'apiExtractService', function ($scope, apiExtractService) {
$scope.extract = function () {
apiExtractService.apiExtract()
.then(function (response) {
//You have to return an array not full JSON
return response.orderList;
}, function (err) {
console.log("something went wrong");
});
}
}

Related

Angular scope items not applies after item change

I have items service,items list controller, and item details controller:
.state('dashboard.items', {
url: '/items',
templateUrl: '/js/components/dashboard/items/items.html',
controller:'itemsListCtrl'
})
.state('dashboard.items.details', {
url: '/:id',
templateUrl: '/js/components/dashboard/items/itemDetails.html',
controller: 'itemDetailsCtrl',
resolve:{
items: function (ItemService) {
if(!ItemService.items)
ItemService.getAll().then(function (res) {
ItemService.items = res.data;
});
}
}
})
app.factory('ItemService', function ($http) {
var itemsFactory = {};
itemsFactory.getAll = function () {
return $http.get('/items');
}
itemsFactory.update = function () {
itemsFactory.items[0].name = "sadasd";
}
return itemsFactory;
})
app.controller('itemsListCtrl', function($scope, $state, ItemService){
if(!ItemService.items) {
ItemService.getAll().then(function (res) {
ItemService.items = res.data;
$scope.items = ItemService.items;
});
}else{
$scope.items = ItemService.items;
}
})
app.controller('itemDetailsCtrl', function ($scope, items, ItemService) {
$scope.item = ItemService.items[0];
$scope.item.name = "abc" ;
$scope.update = function(){
ItemService.update();
}
})
I have ng-click button which invokes the edit() function.
I made it simple for the example, when doing the update, and edit the item name, the item that presents in the list doesnt change.
I dont know what I miss here. The list sits in the service, and both controllers use it for their purposes.
What am I doing wrong? What is best practice for this scenario?
Update 1
Found something weird. When I edit the item in the controller initialization, it changes the original value globally. When it happens via the edit() method, it doesn't. What happnes?
Thanks.
$http.get returns a promise that it will return your data, so in .then you can do your stuff and it will execute when done(async)
$http.get('/items').then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
return response.data;//this is your data
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});

Using $http in Angular Service - Cannot read property 'post' of undefined

I'm trying to learn how to use Angular right, by having all my business logic in services.
When I do a post request in a service, I get the following error:
Cannot read property 'post' of undefined
Here is some code:
UrlApp.controller('UrlFormCtrl', UrlFormCtrl);
UrlApp.factory('addUrlService', addUrlService);
function UrlFormCtrl($scope, $http) {
console.log('Url Form Controller Initialized');
$scope.addUrl = addUrlService.bind(null, $http);
}
function addUrlService($scope, $http){
console.log('initializing addUrlService');
return $http.post('urls/create', {'test':'test'}).then(function(response){
return response.data;
});
}
I'm just getting the hang of Angular, so I'm not entirely sure what I'm doing wrong. See any problems?
Firstly, you don't need to inject $scope in your service.
Secondly, you don't need to inject $http service in your controller.
Thirdly, you need to inject the service in your controller.
Finally, addUrlService service is returning a promise meaning it will make a request when service is instantiated. You may want to return a function instead or an object containing several functions.
So I would change your code to:
UrlApp.controller('UrlFormCtrl', UrlFormCtrl);
UrlApp.factory('AddUrlService', AddUrlService);
function UrlFormCtrl($scope, AddUrlService) {
$scope.addUrl = AddUrlService.addUrl;
}
function AddUrlService($http) {
function addUrl() {
return $http.post('urls/create', {
'test': 'test'
}).then(function (response) {
return response.data;
});
}
return {
addUrl: addUrl
};
}
Can you try like this
UrlApp.controller('UrlFormCtrl', UrlFormCtrl);
UrlApp.factory('addUrlService', addUrlService);
function UrlFormCtrl($scope,addUrlService) {
console.log('Url Form Controller Initialized');
$scope.addUrl = addUrlService;
}
function addUrlService($http){
console.log('initializing addUrlService');
return $http.post('urls/create', {'test':'test'}).then(function(response){
return response.data;
});
}

Avoid saving returned data to ngResource

I have an ngResource object like this:
[...].factory('Event', ['$resource', function ($resource) {
return $resource('/events/:id', {id: '#id'}, {
resume: {url: '/events/:id/resume'},
signUpload: {url: '/events/:id/sign-upload'},
});
}]);
But when I call myModel.$resume(); or myModel.$signUpload() the returned data gets automatically saved to my model. However, the returned data is not my model attributes, but actually another completely different return.
I need to avoid auto-saving the returned data from the server. Is there anything out-of-the-box to do that? I couldn't find it here: https://docs.angularjs.org/api/ngResource/service/$resource
Thanks
For this case you can try to not use resource, but create service.
app.service('eventService', ['$http, $q', function ($http, $q) {
this.signUpload = function(eventId) {
var defer = $q.defer();
$http.get('/events/' + eventId + '/sign-upload')
.then(function(result) {
defer.resolve(result.data);
})
.catch(function(err) {
defer.reject(new Error(err));
});
return defer.promise;
}
// same for other function
}]);
Inject this service in controller, and just do eventService.signUpload(eventId);

Angular Multiple Resolves in Router

i'm trying to resolve more than one service to my controller, and the 2nd service is dependant on the 1st resolving first as it requires some data to be included to make the request.
Below is what I would like to do, and how I think it ** should ** work, however, I can't seem to access the data returned in the 1st resolveData request.
Any suggestions or ideas would be greatly appreciated
.when('/maps/:id', {
templateUrl: 'partials/maps/view',
controller: 'MapViewCtrl',
authenticate: true,
resolve: {
resolveData: function ($route, MapService) {
var data = MapService.showfull({id: $route.current.params.id});
return data.$promise;
},
resolveMoreData: function($route, Service, resolveData){
var returnData = Service.get({id: resolveData.id});
return returnData.$promise;
}
}
})
The values resolved in a route definition cannot be dependend on each other. They are intended to be used by the controller for that route.
See this part of the $routeProvider source for a reference:
function updateRoute() {
// ...
var locals = angular.extend({}, next.resolve);
angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value, null, null, key);
});
// ...
// Here, we use $q.all(), which converts array of the promises
// into a single promise. That input array contains independent
// promises.
return $q.all(locals);
}
You could fix that in the couple of ways:
Move the resolveMoreData logic into the controller
Create a single dependency (either as a resolved dependency, or a service) which would combine those two promises into one.
The second option could look like:
resolve: {
data: function ($route, MapService, Service) {
var deferred = $q.defer();
MapService
.showfull({id: $route.current.params.id})
.then(function success(data) {
return Service.get({id: data.id});
}, function error(reason) {
deferred.reject(reason);
})
.then(function success(data) {
deferred.resolve(data);
}, function error(reason) {
deferred.reject(reason);
});
return deferred.promise;
}
}
(note the code above is an example only, I haven't ran it).
you should inject the dependency in a brackets [] or using the comment
/* #ngInject */
.when('/maps/:id', {
templateUrl: 'partials/maps/view',
controller: 'MapViewCtrl',
authenticate: true,
resolve: {
resolveData: function ($route, MapService) {
var data = MapService.showfull({id: $route.current.params.id});
return data.$promise;
},
resolveMoreData: ['$route', 'Service','resolveData', function($route, Service, resolveData){
var returnData = Service.get({id: resolveData.id});
return returnData.$promise;
}]
}
})

Content doesn't update on route change

When I change route, from say /set/1 to /set/2, then it still shows the information from /set/1 until I manually refresh the page, I've tried adding $route.refresh to the ng-click of the links to these pages, but that didn't work either. Any ideas?
Below is the routing code, this works fine, all routing is done via links, just <a> tags that href to the route.
angular.module('magicApp', ['ngRoute']).config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'pages/home.html'
}).when('/set', {
redirectTo: '/sets'
}).when('/set/:setID', {
controller: 'SetInformationController',
templateUrl: 'pages/set.html'
}).when('/card', {
redirectTo: '/cards'
}).when('/card/:cardID', {
controller: 'CardInformationController',
templateUrl: 'pages/card.html'
}).when('/sets', {
controller: 'SetListController',
templateUrl: 'pages/sets.html'
}).when('/cards', {
controller: 'CardListController',
templateUrl: 'pages/cards.html'
}).when('/search/:searchterm', {
controller: 'SearchController',
templateUrl: 'pages/search.html'
}).otherwise({
redirectTo: '/'
});
}]);
Below is the code for the SetListController, it uses the routeParams to grab the correct information from a service, which works, when I go to /set/1 then it returns the right information, if I then go back then go to /set/2 it still shows the information from set 1, until I refresh the page.
.controller('SetInformationController', function($scope, $routeParams, $route, SetInformationService, CardSetInformationService) {
$scope.set = [];
$scope.cards = [];
function init() {
SetInformationService.async($routeParams.setID).then(function(d) {
$scope.set = d;
});
CardSetInformationService.async($routeParams.setID).then(function(d) {
$scope.cards = d;
})
}
init();
})
The HTML itself has no reference to the controller, or anything like that, just the objects in the scope, namely set and cards.
I figured it out! The problem wasn't actually with the routing it was with my service, here was the service before:
.factory('SetInformationService', function($http) {
var promise;
var SetInformationService = {
async: function(id) {
if ( !promise ) {
// $http returns a promise, which has a then function, which also returns a promise
promise = $http.get('http://api.mtgdb.info/sets/' + id).then(function (response) {
// The then function here is an opportunity to modify the response
console.log("Set Information");
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
}
// Return the promise to the controller
return promise;
}
};
return SetInformationService;
})
where it should have been:
.factory('SetInformationService', function($http) {
var promise;
var SetInformationService = {
async: function(id) {
// $http returns a promise, which has a then function, which also returns a promise
promise = $http.get('http://api.mtgdb.info/sets/' + id).then(function (response) {
// The then function here is an opportunity to modify the response
console.log("Set Information");
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return SetInformationService;
})

Categories

Resources