Angularjs - Passing API Data between two controllers - javascript

i want to share data that i get from the lastFM API between two controllers.
I've been trying with the factory but I don't get a grip on it...
angular.module('myApp')
.factory('getData', function($http, $scope, $routeParams) {
$http.get(lastfm + "&method=artist.getInfo&mbid=" + $routeParams.mbid).success(function(data) {
console.log(data)
});
})
.controller('artistCtrl', function ($scope, $routeParams, $http, getData) {
console.log(getData.data);
})
.controller('artistInfoCtrl', function ($scope, $routeParams, $http, getData) {
console.log(getData.data);
})
So how do i manage to pull this off?

There are a couple of things:
1) know that you're dealing with asynchronous data, so do not expect to be able to read the data in the controllers immediately.
2) have your factory return a function that the controllers can call. This function should return a promise that will eventually (if all goes well) be resolved with the data;
3) the controllers can call this function that is returned by the factory and in the .then() of the promise that is returned, you can actually work with the data.
My advice is to do a little research on the terms I described above, if they are not yet familiar to you. Understanding them will enable you to achieve a lot more with angular.

Your factory has no property data, you probably need to call your factory by saying
var something = getData();
and make the success handler of the $http call return that data
$http.get(lastfm + "&method=artist.getInfo&mbid=" + $routeParams.mbid).success(function(data) {
return data;
});

Related

Patterns for AngularJS to update view in controller during recursive HTTP GET

Using AngularJS I have a historicalDataController
angular.module('armsApp').controller('historicalDataController', ($scope, $historicalFactory) => {
$scope.histogram = new Histogram('#histogram');
historicalFactory.getHistorycalData().then((data) => {
$scope.histogram.update(data);
});
});
and historicalFactory
angular.module('armsApp').factory('historicalFactory', ['$q', '$http',
function ($q, $http) {
return {
getHistoricalData() {
const deferred = $q.defer();
function loadAll(page) {
const token = cognito.getToken('id');
// let deferred = $q.defer();
return $http({
// details
})
.then((response) => {
if (response.data.pages > page) {
loadAll(page + 1);
} else {
deferred.resolve(response.data);
}
});
}
loadAll(0);
return deferred.promise;
},
The former manages my D3 histogram element and the latter handles communication to my data server.
From historicalDataController I am initiating a call to a function in historicalFactory which using recursion does serial GETs to a paginated REST endpoint and aggregates the data. In the controller I currently update the D3 histogram when the function returns with the full dataset, which functions OK.
Now I want to incrementally update the histogram with results from each GET but the callbacks are in the factory which has no access to the UI element (and I feel like view-related logic does not belong in there).
What are some good options of patterns to use here?
From factory should I write the incremental data to a variable in controller and broadcast an event to controller? This feels messy somehow.
Could I use some kind of cross-module decorator to wrap the Histogram-update function with the recursive GET function?
You'll want to use the "notification" callback to provide updates on a promise without resolving it. When one of your $http calls resolves, call deferred.notify(). In your controller, you'll want to provide "catch" and "notifiy" callbacks in your .then().
See the full documentation of $q.
https://docs.angularjs.org/api/ng/service/$q

[JavaScript][Angular]: TypeError: Cannot read property 'getCityData' of undefined

This question has been asked many times before and I've tried the answers but they do not seem to solve the problem I'm facing. I'm new to Angular and am trying to pass a value from the controller to a factory so that I can retrieve some JSON information through an API. While I'm able to get the value from my HTML to the controller, the next step is giving me a TypeError: Cannot read property 'getCityData' of undefined. My controller code is as follows:
app.controller('MainController', ['$scope', function($scope, HttpGetter) {
var successFunction = function(data) {
$scope.data = data;
}
var errorFunction = function(data) {
console.log("Something went wrong: " + data);
}
$scope.cityName = '';
$scope.getCityName = function(city) {
$scope.cityName = city;
HttpGetter.getCityData($scope.cityName, successFunction, errorFunction);
};
}]);
The factory code is as follows:
app.factory('HttpGetter', ['$http', function($http){
return {
getCityData: function(query, successFunction, errorFunction){
return $http.get('http://api.apixu.com/v1/current.json?key=MyAppKey&q=' + query).
success(successFunction).
error(errorFunction);
}
};
}]);
I've replaced my App key with the string "MyAppKey" just to be safe but my code contains the appropriate key. Also, it would be very helpful if I could get a bit of an insight on how the function invocations happen because there seem to be a lot of function callbacks happening.
Getting undefined could be because of the service not getting properly injected.
Try:
app.controller('MainController', ['$scope', 'HttpGetter', function($scope, HttpGetter)
Also as you said, to be on safer side you aren't using the right key, but anyone using your application can get the key by checking the network calls. So, ideally, one should make a call to the backend, and backend will send a call along with the secured key to the desired API endpoint and return the response data to front-end.
Can be due to ['$scope', function($scope, HttpGetter) ?
Should be ['$scope', 'HttpGetter', function($scope, HttpGetter) instead.
You used minified version and inject only $scope not HttpGetter but used as argument in controller function that's why got HttpGetter is undefiend and error shown Cannot read property 'getCityData' of undefined
So you should inject HttpGetter in your minified version ['$scope', 'HttpGetter'
use like:
app.controller('MainController', ['$scope', 'HttpGetter', function($scope, HttpGetter)
instead of
app.controller('MainController', ['$scope', function($scope, HttpGetter)
And if your MyAppKey is secured and want to hide from user then you should use it in server side

Using 'Done' instead of 'Success' in Angular promises

I've got an issue where I'm using the Contentful.js library to retrieve content in an Angular app. Instead of the normal $http.get with the success(data) callback, it uses a function with done(data). I can set the $scope.lists value to the returned data, but it does not show up in the HTML for some reason.
This works for a detail view using the standard $http:
$http.get('https://cdn.contentful.com/spaces/xxxxxxx/entries?sys.id=' + $routeParams.listId + '&include=10&access_token=xxxxxxxx').success (data) ->
$scope.list = data
console.log $scope.list
This doesn't work for a list view using the done() method:
client = contentful.createClient
accessToken: 'xxxxxxxx'
space: 'xxxxxxxxx'
listControllers.controller('ListListCtrl', ['$scope', '$http', ($scope, $http) ->
$scope.lists = ""
client.entries({'content_type': 'xxxxxxxx', 'include': 1}).done (data) ->
$scope.lists = data
console.log $scope.lists
])
Any ideas?
Most probably since this library is not targetted towards AngularJS, it is not doing $scope.$apply() to trigger digest cycle and hence the html is not getting updated.
The fix would be wrap the assingment done in callback with $scope.$apply().The JavaScript fix for this would be
$scope.$apply(function() {
$scope.lists = data
});
Since i have not use this library i may be wrong here with done callback implementation.

Angular.js Call $http.get from outside controller

I have an HTTP resource that returns a JSON list of top 10 entities from a database.
I call it this way:
var filter= "john";
var myApp = angular.module('myApp', []);
myApp.controller('SearchController', ['$scope','$http', function ($scope, $http) {
$http.get('/api/Entity/Find/' + filter). //Get entities filtered
success(function (data, status, headers, config) {
$scope.entities = data;
}).
error(function () {
});
}]);
It works!
But... how can I change the filter variable in order to change the query?
Should I rewrite the whole controller to get this to work?
Update
Sorry for the lack of clarity in my question. When I asked this I couldn't undertand anything of AngularJS.
My original intent was to get the variable $http injected, without relying on creating a controller for that.
Thanks for everyone.
A likely better method
If you don't want to get it inside a controller, you could have it injected into a recipe (ex, provider, factory, service):
https://docs.angularjs.org/guide/providers
myApp.factory('getStuff', ['filter', '$http', function (filter, $http) {
//Blah
}]);
If you want to get an instance of $http outside of any angular struct, you can do what's shown below.
The method given by Dennis works; however, it does not work if called before angular has been bootstrapped. Also, it seems like Derek has an error with Dennis' method because he does not have jquery.
The solution that Exlord mentioned is better, as it does not have that problem, and is more proper:
$http = angular.injector(["ng"]).get("$http");
Explained:
The angular injector is an:
object that can be used for retrieving services as well as for dependency injection
https://docs.angularjs.org/api/ng/function/angular.injector
The function angular.injector takes the modules as a parameter and returns an instance of the injector.
So in this case you retrieve an injector for the ng module (angular's), and then retrieve the service $http.
Note:
One thing to keep in mind when using injector like this is that in my own findings it seems you need to make sure you include modules in the inject which what you are "getting" will need. For example:
angular.injector(['ng', 'ngCordova']).get('$cordovaFileTransfer')
Regarding to your question "... call $http.get from outside controller" you can do the following:
... ANYWHERE IN YOUR CODE
var $http = angular.element('html').injector().get('$http');
$http.get(...).success(...);
ANYWHERE IN YOUR CODE ...
See official docs from angular: angular $injector docs :
The get(name); method Returns an instance of the service.

Angular JS Root Scope

I am working with angular js at view layer. I need to use some global variable that will be used and modified by all the controllers method in my application:
var app = angular.module('myWebservice', ['ngCookies']).run(
function($rootScope) {
$rootScope.authToken = null; });
app.controller('UserController', function($cookieStore, $scope, $location,
$routeParams, $http, $timeout, $rootScope) {
$scope.login= function() {
$http.defaults.headers.post = {'Accept' : 'application/json',
'Content-Type' : 'application/json'};
$http.post('/myServise/api/user/login-user', {
emailId : $scope.username,
password : $scope.password
}).success(function(data, status) {
if (status == 200) {
$rootScope.authToken = data.object.authToken;
}).error(function(data, status) {
console.debug(data);});
}// Else end
};// End Login
app.controller('MerchantController', function($cookieStore, $scope, $location,
$routeParams, $http, $timeout, $rootScope) {
$scope.getRootScopeValues = function()
//$scope.getRootScopeValues = function($rootScope)
{
$scope.token = $rootScope.authToken;
console.log($scope.token);// Undefined
console.log($rootScope.authToken);// Undefined
}
});
I am new to Angular JS, I have also tried to use service but I don't know why I am not able to access global variable (authToken). Please help me I am stuck at this point ....Thanks in advance...
I am not sure how your application works but looks like you have a problem of asynchronous calls (while $http is working you are trying to access the variable which is not set yet). Please look at this answer about Asynchronous calls. Maybe this is your case? Also I should note that using $rootScope is always a bad idea. Try to avoid it. You should use service instead which is also a singleton object and always a much better and maintainable approach.
I need to use some global variable that will be used and modified by all the controllers method in my application
The best way to achieve this is using a Service, but you don't have to if you would like to leave it at the $rootScope
I have tried to use service as well but same results "Undefined"
Because the call for the authToken is asynchronous, the controller could run before the token returns from remote server.
You can add the token retrieval logic to the route definition as a 'resolve' field like this:
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/your/route', {
templateUrl: 'book.html',
controller: 'BookController',
resolve: {
token: function() {
//return a promise with the token here
}
}
})
However, this is NOT good in your case as you will have to setup the 'resolve' for every route. If you are using AngularUI Route that supports nested routes, you can simply add the 'resolve' to the root state/route. This is what I've been using in my project.
If you are not using AngularUI Route, there's another way of doing it. You can assign the returned value of the $http.post to a variable in the $rootScope e.g. $rootScope.tokenPromise.
The returned of $http.post is a promise, you can use the 'then; method to register callbacks, and these callbacks will receive a single argument – an object representing the response.
In your controller, you will have put some extra lines, you will get the token asynchronously like this
app.controller('MerchantController', function($cookieStore, $scope, $location,
$routeParams, $http, $timeout, $rootScope) {
$rootScope.tokenPromise.then( function(token){
console.log(token); //depends on the returned data structure
} )
});
In your application 'run' block, you will have to create a promise at $rootScope.tokenPromise. you can either use $http to retrieve the token and get the promise there in the run block, or create a pending promise and retrieve the token elsewhere in other places, after which you can resolve the promise.

Categories

Resources