I Have the following code in which I'm trying to get a JSON file and attach its contents to my $scope.
The first console.log returns the result that I need, but the second returns undefined.
What would be the correct way to write this so that the data is stored?
'use strict';
var myApp = angular.module('myApp.view1', ['ngRoute']);
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}]);
myApp.controller('View1Ctrl', [
'$scope',
'$http',
function($scope, $http) {
$http.get('view1/info.json')
.then(function(res){
$scope.data = res.data.info
console.log($scope.data)
});
console.log($scope.data)
}]);
AngularJS's $http.get()'s then() callback is called asynchronously.
That means that in the callback, it will work, but not in the second one.
The reason for this is with something called Promises. I definitely suggets looking into them. Here is kind of what happens in order:
$http.get('view1/info.json')
That tells javascript I want to get the JSON data. But it does not actually get it yet. Then, it does that console log.
console.log($scope.data)
Well $scope.data is undefined. Nothing has set it yet! Then, javascript actually gets the JSON data. When it gets the data, the function within the then is called. After this,
$scope.data = res.data.info
console.log($scope.data)
$scope.data is defined, which is why this console.log works. Read more about promises for more info!
Here is a good starting point: https://www.youtube.com/watch?v=wA0gZnBI8i4
Well, your data is stored. But not at the time your second console.log() gets executed. The "then" promise/callback will run after your second console.log(). If you need $scope.data in your templates, this should work. Angulars digest cycle will be run after the callback of $https.
Related
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
I'm trying to take advantage of the (new in 1.5.0) feature that adds the resolve map to the scope of the route. However, it seems like the map isn't actually getting added to the scope until after the controller has loaded.
Here's a very simple module that demonstrates the issue (from this plunk):
var app = angular.module("app", ['ngRoute']);
app.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider.otherwise({
controller: "editController",
controllerAs: "ec",
templateUrl: "edit.html",
resolve: {
resolveData: function() {
return {
foo: "bar",
blah: "halb"
}
}
}
});
}
]);
app.controller("editController", ["$scope", function($scope) {
// undefined
console.log($scope.$resolve);
setTimeout(function() {
// the object is there, including $scope.$resolve.resolveData
console.log($scope.$resolve);
}, 0)
}]);
If you watch the console, you'll note that $scope.$resolve is undefined when the controller is created. However, it's there immediately afterwards, as demonstrated by the setTimeout.
I can't find anything in the documentation that suggests this should be the case. Is this a bug? Or am I just not getting something fundamental about how angular works?
In case someone else comes across this - it was already reported on the angular github and resolved as intended behavior. $scope.$resolve isn't really meant to be used in controllers. You are supposed to inject it, or use $route.current.locals.
(Or you can wrap the code using $scope.$resolve in a $timeout.)
Notice your object is much bigger than you expect, and actually your object is on $resolve. This is mostly explained in the docs, however, could be more elaborate with examples...
the resolve map will be available on the scope of the route, under
$resolve
No need to dig into this, when you resolve, the named object becomes an injectable you can then place on $scope, in this case, resolveData. Observe the following...
app.controller('editController', ['$scope', 'resolveData', function($scope, resolveData) {
console.log(resolveData);
// -- $scope.data = resolveData; // -- assign it here
}]);
Plunker - updated demo
Investigation of why you are getting undefined is due to the nature of awaiting digest cycles in the framework. An acceptable workaround to get a handle on $resolve would include injecting and wrapping the call in a $timeout, which forces a digest cycle in which your object will be available in the asynchronous callback. If this approach is not ideal, forego injecting $timeout and either call $route.current.locals directly for your resolved object, or inject your object as a named parameter of which it will have resolved immediately as the example above demonstrates.
This is the code in the controller
cat1=[];
$.getJSON('categories/1/', function(data) {
cat1 = data; //returns a JSON
});
//cat2..4 are some JSONs
$scope.pictures=[cat1,cat2,cat3,cat4,cat5];
The problem is that seems like cat1=[] and cat1=data are different variables, cause pictures[cat1] always returns []
Am I doing it wrong?
Because $.getJSON is an async request and is still processing when you try and log. Also, don't use jQuery with Angular, use Angular's $http (this way a $digest cycle is triggered and everything stays in sync):
$http.get("categories/1/").success(function(data) {
cat1 = data; //returns a JSON
$scope.pictures=[cat1,cat2,cat3,cat4,cat5];
});
Dont forget to add $http as a dependency in your controller:
app.controller("myCtrl", ["$scope", "$http", function($scope, $http) {
}]);
I'm trying to get some JSON data to pass between controllers. I get some JSON with $http, set my callback with a $q defer and assign the result to my $rootScope.productList.
Everything is working, but when I add a $watch on $rootScope.productList, it returns me undefined. Do you have any solution about this?
My $watch inside a controller:
$rootScope.watch('productList', function(newVal, oldVal) {
$scope.filters = $rootScope.productList;
console.log($rootScope.productList);
});
and inside another controller, I get my data. I replaced the $http by a timeout to reproduce the behaviour.
$timeout(function() {
$rootScope.productList = $scope.productList;
console.log($rootScope.productList);
}, 500);
http://plnkr.co/edit/7wWxXgq5BARYgm0tYVgf
I tried with a $watch, but I'd take any workaround.
The problem here is that watch() is not a recognized function - what you probably want is $watch().
In other words, you were just missing the $ next to 'watch' in your original code.
Try this:
$rootScope.$watch('productList', function(newVal, oldVal) {
$scope.filters = $rootScope.productList;
console.log($rootScope.productList);
})
If you need to be able to transfer data between controllers, you should be implementing a service in angular. The $rootScope wasn't created for you to set your data.
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.