Calling instagram API without using callback - javascript

I am consuming the Instagram API through Angular. i am consuming it without authentication.
My code looks like below. Can anyone advise if this is the best way to call this API, by using a callback. Is there a better approach by using promises? If so how would this look? and also, how can I add an error handler to this code?
factory.js
app.factory('socialMedia', ['$http', function($http){
return {
fetchInstagram: function(callback){
var url = "https://api.instagram.com/v1/users/*******/media/recent?client_id=*****&callback=JSON_CALLBACK";
$http.jsonp(url).success(function(response){
callback(response.data);
});
}
}
}]);
controller.js
app.controller("instagramCtrl", ["socialMedia", function (socialMedia) {
instagramCtrl = this;
this.instagramPosts = [];
this.loading = true;
socialMedia.fetchInstagram(function (data){
instagramCtrl.loading = false;
for(var i = 0; i < 5; i++){
instagramCtrl.instagramPosts.push(data[i]);
}
});
}]);

Don't use callbacks, it's the past. $http service returns promise object, which is much more convenient option (error handling, promise chaining):
app.factory('socialMedia', ['$http', function($http){
return {
fetchInstagram: function() {
var url = "https://api.instagram.com/v1/users/*******/media/recent?client_id=*****&callback=JSON_CALLBACK";
return $http.jsonp(url).then(function(response) {
return response.data;
});
}
}
}]);
Make sure you return Promise from fetchInstagram method.
Then in controller you would use it like this:
socialMedia.fetchInstagram().then(function(data) {
$scope.data = data;
});

Related

AngularJS and angular factory

I have asked similar question before, this time I am stuck with recording data to the blockchain using Angular js and Angular Factory. Please see the code below and advise me where I am wrong
app.js
var app = angular.module('application', [])
app.controller('appController',function($scope, appFactory) {
$('success_create').hide()
$scope.recordData = function(){
appFactory.recordData($scope.data, function(data){
$scope.recordData = data
$("success_create").show()
})}}
app.factory('appFactory',function($http) {
var test = []
factory.recordData = function(data, errorCallback) {
test = data.field1+"-"+data.field2
$http.get('/record_data'+data).then(function(output) {
if (output) {
callback(output)
}).catch(function(error) {
errorCallback(error) })}
return factory
There are so many errors in you're code, that I was considering not to awnser.
But as I felt the need to help you, take the code below as a guide.
var app = angular.module('application', [])
app.controller('appController', function($scope, appFactory) {
// Use angular.element instead of the jQuery `$` selector
angular.element('success_create').hide();
$scope.recordData = function()
{
// The factory returns a promise,
// so you can do the same just as you would with $http
appFactory.recordData($scope.data).then(function(response) {
$scope.recordData = response.data;
angular.element("success_create").show()
});
}
});
app.factory('appFactory',function($http) {
// You define the var as array, but you assign a string later on
// So instead of var test = [] use var test = "" or just var test;
var test = ""; // Or var test;
var factory = {
recordData: function (data, errorCallback)
{
test = data.field1 + "-" + data.field2;
var promise = $http.get('/record_data' + data).then(function(output) {
return output.data;
});
// $http returns a promise, return this to your controller
// You can use the data it returns just like you
// would with the $http method
return promise;
}
}
// In your original code, you return the factory. But you never
// Defined the factory.
return factory;
});
Try out these simply tutorials to learn more about controllers, services ** and promises
https://www.w3schools.com/angular/angular_controllers.asp
https://www.w3schools.com/angular/angular_services.asp
https://docs.angularjs.org/api/ng/service/$q
** Confused about Service vs Factory
#Tabz: modified your code.
app.controller(appController,function($scope, appFactory) {
$("success_create").hide();
$scope.recordData = function(){
appFactory.recordData($scope.data, function(data){
$scope.recordData = data
$("success_create").show();
})
}
})
app.factory("appFactory", function($http) {
factory.recordData = function(data, errorCallback)
$http.get('/record_data'+data).then(function(output) {
if (output)
callback(output)
}).catch(function(error) {
errorCallback(error)
})};
return factory

how to make a function reusable between two or more controllers in angularjs

I have a question as I am new to angularjs. I have searched alot but unable to understand the concept. I have a basic concept that there should be one controller for one view but we can have multiple controllers for a single view. I have below two controllers in a single view and there is one function let say for example addComma function which I have to use in both controllers but I want to write it one time to make it reusable between all controllers let say 5,6 controllers on the same view. So basically the question is how to make a function in a controller global between all controllers of same view or other views so that I can use it anywhere in my application. Appologies if it is a dumb question I am having a hard time understanding the concepts of angularjs.
app.controller("GetChargesController", function ($scope, GetService, $rootScope) {
$scope.Title = "Charges Details List";
$rootScope.loading = true;
// Calling Serivce Method here
$scope.GetChargesDetails = GetService.GetAll("CommonApi", "GetChargesDetails").then(function (d) {
$scope.ChargesDetails = d;
$rootScope.loading = false;
});
// add comman function goes here
$scope.addComma = function(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
});
app.controller("GetPaymentsController", function ($scope, GetService, $rootScope) {
$scope.Title = "Payments Details List";
$rootScope.loading = true;
// Calling Serivce Method here
$scope.GetPaymentsDetails = GetService.GetAll("CommonApi", "GetPaymentsDetails").then(function (d) {
$scope.PaymentsDetails = d;
$rootScope.loading = false;
});
// add comman function goes here
$scope.addComma = function (x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
});
Below is the generic service I've written to get any kind of data from database(using asp.net web api). As I've read that angular services can hold data and we don't need to call the database again and again when we are moving back and forth on links let's say for example I have these 3 links on a page. Home Link, PaymentCharges Link, Orders Link. so Home view will open by default. And when I click on PaymentCharges Link the call will go get data from database and render its view but when I click back on Home Link there should be no call going to database to get the data for the home page or when I click forward again on PaymentCharges Link the second time there should be no call going to database but what I see on firebug console option it is calling the function and going to database to get the data.I think we need to use cache to hold the data to not send the call to database.
app.factory("GetService", function ($http) {
var thisService = {};
// get all data from database
thisService.GetAll = function (controllername, methodName) {
var promise = $http({
method: 'GET',
url: '/api/'+controllername + '/' + methodName
})
.then(function (response) {
return response.data;
},
function (response) {
return response.data;
});
return promise;
};
});
Create an utils service and add the addComma function there. Inject the utils service and reuse the addComma in the controllers
app.factory('utils', function() {
return {
addComma: function() {}
}
});
app.controller("GetChargesController", function ($scope, utils) {
$scope.addComma = utils.addComma;
});
app.controller("GetPaymentsController", function ($scope, utils) {
$scope.addComma = utils.addComma;
});
Several ways to manage the service issue:
Re-use the promises by storing them in the service:
// create an object to store promises
var promises = {};
thisService.GetAll = function(controllername, methodName) {
promises[controllername] = promises[controllername] || {};
// if the particular promise doesn't already exist create it as a property of above object
if (!promises[controllername][methodName]) {
promises[controllername][methodName] = $http({...}).then(...;
}
// now return the specific stored promise
return promises[controllername][methodName];
};
or store the data and use $q to return a different promise that resolves with the stored data when it already exists. Make sure to inject $q for this approach
var data = {};
thisService.GetAll = function(controllername, methodName) {
data[controllername] = data[controllername] || {};
if (data[controllername][methodName]) {
// data exists for this controller and method, no need for new request
return $q.resolve(data[controllername][methodName]);
} else {
return $http({...}).then(function(response) {
// store the data for next use
var newData = response.data;
data[controllername][methodName] = newData;
return newData;
});
}
};
Why don't you just store the data in the service?
You could do:
.then(function (resp) {
thisService.controllerName.data = resp.data
}
Then you can reference the variable in your code using GetService.controllerName.data
Instead I would suggest you to use something like this which might make it little more generic. Check the below code
appResources-Factory will contain all the resources that are needed to be exposed.
indexServices-Factory will contain all your services corresponding to your index controller.
(function () {
use strict';
var commonServices = angular.module('commonServices', ['ngResource']);
commonServices.factory('appResources', ['$resource',
function ($resource) {
var indexResource=$resource('/api/index/:id');
return {
indexResource:indexResource
}
}]);
commonServices.factory('indexService',['appResources',
function (appResources){
var getAllIndexes =function(sucesscallback){
appResources.indexResource.query({}).$promise.then(
//success
function( value ){/*success events and Data is present in the 'value'*/
sucesscallback(value);
},
//error
function( error ){/*failure events*/}
)
}
}
]);
});
Controller Method calling the service and getting the value in $scope variables like
(function () {
'use strict';
angular.module('saniehhaApp')
.controller('indexController', indexController);
indexController.$inject = ['$location', 'indexService'];
function indexController($location, index,indexService) {
/* jshint validthis:true */
indexService.getAllIndexes(function(sucesscallback){
$scope.value=sucesscallback;
})
//$locaton.qwets=....
}
})();

manage factory JSON $http data with 2 controllers

I'm trying to get a factory JSON response, save it in a variable, in order to be ready to be called from 2 different controllers.
Here bellow I paste the code I'm using:
storyFactory.js
var story = angular.module('story.services', []);
story.factory('storyAudio', [ '$http', function ($http) {
var json = {};
function getJSON(story_id, callback) {
$http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
}).success(function (data) {
json = data;
callback(data);
});
};
return {
getSubaudios: function(story_id, callback) {
getJSON(story_id, function(result) {
callback(result);
});
},
getTopbar: function(callback) {
callback(json);
}
};
}]);
StoryCtrl.js
var storyCtrl = angular.module('story', ['story.services']);
storyCtrl.controller('storyCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data = this;
data.story = {};
storyAudio.getSubvideos($stateParams.story_id, function(response) {
data.story = response;
});
}]);
TopbarCtrl.js
var topbarCtrl = angular.module('topbar', ['story.services']);
topbarCtrl.controller('topbarCtrl', [ 'CONFIG', '$stateParams', 'storyAudio', function(CONFIG, $stateParams, storyAudio) {
var data2 = this;
data2.story = {};
storyAudio.getTopbar(function(response) {
data2.story = response;
});
}]);
The problem is in my TopbarCtrl response I'm receiving an empty data2.story when I call it in the HTML.
The reason is because it doesn't have a callback of the $http response, so it prints the var json with the actual status, that is an empty object.
How could I load the second controller when the variable has content?
Thanks in advice.
I think the best you can do in this case is load the data via getSubaudios and provide a reference to the data for other controllers to use. Something like this...
story.factory('storyAudio', function($http) {
var factory = {
story: {}
};
factory.getSubaudios = function(story_id) {
return $http.get('https://api.domain.co/get/' + story_id).then(function(response) {
return angular.extend(factory.story, response.data);
});
};
return factory;
})
Using angular.extend() instead of directly assigning a value to the factory's story property maintains any references that may be established before the data is loaded.
Then you can load the data via
storyCtrl.controller('storyCtrl', function(storyAudio) {
var data = this;
storyAudio.getSubaudios($stateParams.story_id).then(function(story) {
data.story = story;
});
})
and directly reference the story data by reference in your controller
topbarCtrl.controller('topbarCtrl', function(storyAudio) {
this.story = storyAudio.story;
})
I think I'm understanding correctly, but let me know if not.
There are two issues I'm seeing. The first is that there is a typo in your StoryCtrl.js file. You are calling "storyAudio.getSubvideos" but the function is called "getSubaudios" in your factory.
Even with that typo fixed, the issue could still technically happen. It all really depends on how quickly the promise returns from the first call. Unfortunately, promises are asynchronous, so there is no guarantee that the "json" variable will get set before the second controller tries to get it.
In order to resolve this, you need to ensure that the first call is finished before trying to access the "json" variable you have on the service. There are probably a few different ways to do this, but one that comes to mind is to actually return and store the promise in the service like so...
var dataPromise;
function getSubaudios(story_id){
if(!dataPromise){
dataPromise = $http({
url: 'https://api.domain.co/get/' + story_id,
method: "GET"
});
}
return dataPromise;
}
return {
getSubaudios: getSubAudios
};
Then in your controllers, you can just call the service and use .then to get the data out of the promise when it returns...
storyAudio.getSubaudios($stateParams.story_id).then(function(response){
data.story = response; //or data2.story = response;
});
Here is a plunkr example. I've used the $q library to simulate a promise being returned from an $http request, but it should illustrate the idea.
Similar to Phil's answer. (Angular extend, or angular copy keeps the references the same in both controllers. If you don't want to put watchers in both controllers to keep track if the value changes.) Several methods here:
Share data between AngularJS controllers.
You could also bind the object you are returning directly to the update-function. That way the references stay intact.
storyServices.factory('storyAudio', ['$http', function($http) {
return {
data: { json: '' },
getSubaudios: function(story_id) {
$http.get('http://jsonplaceholder.typicode.com/posts/' + story_id)
.then(function(response) {
this.data.json = response.data.body;
}.bind(this));
}
};
}]);
var storyCtrl = angular.module('story').controller('storyCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data = storyAudio.data;
storyAudio.getSubaudios(2);
}]);
var topbarCtrl = angular.module('story').controller('topbarCtrl', ['$scope', 'storyAudio', function($scope, storyAudio) {
$scope.data2 = storyAudio.data;
}]);
Plunk here: http://plnkr.co/edit/auTd6bmPBRCVwI3IwKQJ?p=preview
I added some scopes to show what happens.
Sidenote:
I think it's straight out dishonest to name your non-controller "storyCtrl" and then assign it a controller of its own:
var storyCtrl = angular.module(...); // Nooo, this is not a controller.
storyCtrl.controller(...); // This is a controller! Aaaah!
Another sidenote:
.success() is the old way of doing things. Change to .then(successCallback) today! I dare to say it's the standard convention for promises.
https://docs.angularjs.org/api/ng/service/$http#deprecation-notice

Execute function after another AngularJS

I need to execute a function which fetches data after a kind of login function who provides the sessionId. This sessionId is necessary for the second function.
app.controller('TestController',
function ($scope, dbObjectsDAO, loginService){
var sessionID = loginService.getSessionID(); //Login function
var self = this;
this.items = [];
this.constructor = function() {
dbObjectsDAO.getAll(sessionID).then(function(arrObjItems){
$scope.items = arrObjItems;
});
};
this.constructor(); //get the data
return this;
});
I tried several variations like:
loginService.getSessionID().then(function(sessionID){
this.constructor(); //also with just constructor();
});
But I always receive errors (in the case above: Illegal constructor).
So how can I manage to execute one function after another ? Maybe a callback structure would help here but I have no clue how to realize it.
EDIT
Here is the code for the login:
app.service('loginService', function($http, $q) {
this.getSessionID = function()
{
return $http({
method: "GET",
url: "http://localhost:8080/someRequestDoneHere"
}).then(function(response)
{
return response.data.sessionId; // for example rYBmh53xbVIo0yE1qdtAwg
});
};
return this;
});
Does your getSessionID() function return a promise? If so you want code like this:
app.controller('TestController',
function ($scope, dbObjectsDAO, loginService){
var sessionID;
var vm = this;
vm.items = [];
loginService.getSessionID()
.then(function(sid) {
sessionID = sid;
return dbObjectsDAO.getAll(sessionID);
})
.then(function(arrObjItems){
vm.items = arrObjItems;
});
});
So your login service returns a promise which resolves to the session id. You can save that in a variable for use elsewhere, and also use it to trigger fetching the items.
I also changed your self to vm as that naming is an Angular convention, and stored the items in vm.items rather than directly in the scope.
Edit:
Your login code already returns a promise, not a session id. return inside a then is simply going to return a new promise that resolves to the value you are returning.
There are several ways to chain multiple $http requests. If they are independent of each other just fire off a bunch of requests and use $q.all to handle when they have all completed.
var promise1 = $http(something)
.then(function(response) { vm.data1 = response.data; return vm.data1; });
var promise2 = $http(something)
.then(function(response) { vm.data2 = response.data; return vm.data2; });
$q.all([promise1, promise2], function(values) {
// here can safely use vm.data1 or values[0] as equivalent
// and vm.data2 or values[1].
});
If one request depends on the result of another you could even do this:
var promise1 = $http(something)
.then(function(response) {
vm.data1 = response.data;
return { method:'GET', url: response.data.link}
});
var promise2 = promise1.then($http)
.then(function(response) { vm.data2 = response.data; return vm.data2; });
Your template needs to declare the controller using the 'controller as something' syntax:
<div ng-controller="TestController as test" ng-bind="test.items"></div>
Have you tried to nest the second function, like this ? without the constructor call ?
loginService.getSessionID().then(function(sessionID){
dbObjectsDAO.getAll(sessionID).then(function(arrObjItems){
$scope.items = arrObjItems;
});
});
Mb you have wrong scope in
..then(function(sessionID){...}) ?
you can try some this like this:
var vm=this;
loginService.getSessionID().then(function(sessionID){
vm.constructor();
});

Recursive queries with promises in AngularJS

I have a recursive query that needs to potentially make further queries based on the results. I would ideally like to be able to construct a promise chain so that I know when all of the queries are finally complete.
I've been using the example from this question, and I have the following method:
this.pLoadEdges = function(id,deferred) {
if (!deferred) {
deferred = $q.defer();
}
$http.post('/Create/GetOutboundEdges', { id: id }).then(function(response) {
var data = response.data;
if (data.length > 0) {
for (var i = 0; i < data.length; i++) {
var subID = data[i].EndNode;
edgeArray.push(data[i]);
self.pLoadEdges(subID, deferred);
}
} else {
deferred.resolve();
return deferred.promise;
}
});
deferred.notify();
return deferred.promise;
}
Which I then start elsewhere using:
self.pLoadEdges(nodeID).then(function() {
var edgedata = edgeArray;
});
And of course I intend to do some more stuff with the edgeArray.
The problem is that the then() function is trigged whenever any individual path reaches an end, rather than when all the paths are done. One particular pathway might be quite shallow, another might be quite deep, I need to know when all of the pathways have been explored and they're all done.
How do I construct a promise array based on this recursive query, ideally so that I can use $q.all[] to know when they're all done, when the number of promises in the promise array depends on the results of the query?
I'm not 100% positive what the end result of the function should be, but it looks like it should be a flat array of edges based on the example that you provides. If that's correct, then the following should work
this.pLoadEdges = function(id) {
var edges = [];
// Return the result of an IIFE so that we can re-use the function
// in the function body for recursion
return (function load(id) {
return $http.post('/Create/GetOutboundEdges', { id: id }).then(function(response) {
var data = response.data;
if (data.length > 0) {
// Use `$q.all` here in order to wait for all of the child
// nodes to have been traversed. The mapping function will return
// a promise for each child node.
return $q.all(data.map(function(node) {
edges.push(node);
// Recurse
return load(node.EndNode);
});
}
});
}(id)).then(function() {
// Change the return value of the promise to be the aggregated collection
// of edges that were generated
return edges;
});
};
Usage:
svc.pLoadEdges(someId).then(function(edgeArray) {
// Use edgeArray here
});
You need $q.all function:
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
Update 1
Check this demo: JSFiddle
The controller can be like following code (well, you may want to put it in a factory).
It loads a list of users first, then for each user, load the posts of this user. I use JSONPlaceholder to get the fake data.
$q.all accepts an array of promises and combine them into one promise. The message All data is loaded is only displayed after all data is loaded. Please check the console.
angular.module('Joy', [])
.controller('JoyCtrl', ['$scope', '$q', '$http', function ($scope, $q, $http) {
function load() {
return $http.get('http://jsonplaceholder.typicode.com/users')
.then(function (data) {
console.log(data.data);
var users = data.data;
var userPromises = users.map(function (user) {
return loadComment(user.id);
});
return $q.all(userPromises);
});
}
function loadComment(userId) {
var deferred = $q.defer();
$http.get('http://jsonplaceholder.typicode.com/posts?userId=' + userId).then(function (data) {
console.log(data);
deferred.resolve(data);
});
return deferred.promise;
}
load().then(function () {
console.log('All data is loaded');
});
}]);
Update 2
You need a recursive function, so, check: JSFiddle.
The code is below. I use round to jump out of the recursion because of the fake API. The key is here: $q.all(userPromises).then(function () { deferred.resolve(); });. That tells: Please resolve this defer object after all promises are resolved.
angular.module('Joy', [])
.controller('JoyCtrl', ['$scope', '$q', '$http', function ($scope, $q, $http) {
var round = 0;
function load(userId) {
return $http.get('http://jsonplaceholder.typicode.com/posts?userId=' + userId)
.then(function (data) {
var deferred = $q.defer();
console.log(data.data);
var posts = data.data;
if (round++ > 0 || !posts || posts.length === 0) {
deferred.resolve();
} else {
var userPromises = posts.map(function (post) {
return load(post.userId);
});
$q.all(userPromises).then(function () {
deferred.resolve();
});
}
return deferred.promise;
});
}
load(1).then(function () {
console.log('All data is loaded');
});
}]);
You can try building up an array of returned promises and then use the $.when.apply($, <array>) pattern. I've used it before to accomplish a similar thing to what you're describing.
More info on this SO thread.
UPDATE:
You probably also want to read the docs on the apply function, it's pretty neat.

Categories

Resources