I have this controller that has several functions that call each other. On success, I want to return something to be displayed (located in the last function). For some reason, without errors, the return is not working but the console.log is. Can someone please tell me why the return does not work and give me a solution please. Thanks so much!
.controller("dayController", function(){
.controller("weatherController", function(){
this.currentWeatherToDisplay = function(){
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(gotLocation,initialize);
}
else{
alert("Device does not support geolocation");
initialize();
}
};
var currentLocation;
//get the location coords
function gotLocation(pos){
var crd = pos.coords;
currentLocation = loadWeather(crd.latitude+','+crd.longitude);
initialize();
}
function initialize(){
if(!currentLocation){
loadWeather("Washington, DC");
}
else{
loadWeather(currentLocation);
}
}
function loadWeather(location){
$.simpleWeather({
location: location,
woeid: '',
unit: 'f',
success: function(weather) {
var html = weather.temp+'°'+weather.units.temp;
console.log(html);
return html;
},
error: function(error) {
console.log(error);
return error;
}
});
}
});
Well, mmmm you are use some jQuery plugin to get the weather given a current location, and like almost every jQuery plugins this use callbacks call to works (success, and error) first one i recommend you to rewrite this method to something like this:
function loadWeather(location){
var defered = $q.defer();
$.simpleWeather({
location: location,
woeid: '',
unit: 'f',
success: function(weather) {
var html = weather.temp+'°'+weather.units.temp;
console.log(html);
defered.resolve(html);
},
error: function(error) {
console.log(error);
defered.reject(error);
}
});
return defered.promise;
}
Also you must inject the $q dependency to the controller, like this:
module.controller("weatherController", function($q){...}
or this
module.controller("weatherController", ['$q',function($q){...}
I recommend the last by minyfication improvement in angular, when you return a promise like the function loadWeather, you must understand some basic principles about the the $q (based kriskoval Q library), a promise is a expected value in the future, an have a method then to work with that data (its a very short concept), that means:
function gotLocation(pos){
var crd = pos.coords;
loadWeather(crd.latitude+','+crd.longitude)
.then(function(html){
//html contain the expected html from loadWeather defered.resolve(html)
currentLocation = html;
})
.catch(function(error){
//error contain the expected error by execute defered.reject(error)
// maybe gonna try to initialize here
initialize();
})
}
This must work, remember to change the initialize function to some like this:
function initialize(){
var promise;
if(!currentLocation){
promise = loadWeather("Washington, DC");
}
else{
promise = loadWeather(currentLocation);
}
promise.then(function(html){
// some logic with succesful call
}, function(error) {
// some logic with error call
})
}
Related
services.factory('profilFactory',['$q','$http',function($q,$http){
var factory2 =
{
profils : {},
getProfils : function(){
$dfd = $q.defer();
$http.get('data.json')
.success(function(data,status){
this.profils = data.profil;
$dfd.resolve(this.profils);
})
.error(function(data,status) {
$dfd.reject('erreur recuperation des profils');
});
return $dfd.promise;
},
getProfil : function(idProfil){
var profil={};
var profils = {};
factory2.getProfils().then(function(data){
profils= data;
console.log(profils);//all right until here profils has values
});
console.log(profils);// now profils is empty :\ and the foreach will not execute
angular.forEach(profils, function(value, key){
if(value.id == idProfil){
profil= value;
}
});
return profil;
}
};
return factory2;
}]);
This is a screenshot of the problem : method "getProfil"
To answer your question, "Why are the variables empty in the factory", it's because you are using a console.log statement in a location where the data has not yet been loaded from the server. To learn more, Google this: "angularjs http get promises"
services.factory('profilFactory',['$q','$http',function($q,$http){
var factory2 =
{
profils : {},
getProfils : function(){
$dfd = $q.defer();
$http.get('data.json')
.success(function(data,status){
this.profils = data.profil;
$dfd.resolve(this.profils);
})
.error(function(data,status) {
$dfd.reject('erreur recuperation des profils');
});
return $dfd.promise;
},
getProfil : function(idProfil){
var profil={};
var profils = {};
// Run a function to get data, "THEN" we run a function to process the data:
factory2.getProfils().then(function(data){
// Data has now been loaded so we can process it and return it.
profils = data;
angular.forEach(profils, function(value, key){
if(value.id == idProfil){
profil= value;
}
});
return profil;
});
console.log(profils); // This is EMPTY because it runs immediately after
// the factory2.getProfils() function which may need several seconds to
// load data. That's why "profils" is empty. The data hasn't loaded at
// this point.
//
// No data will be available at this level of the code. Don't try to access
// "profils" here! Only in your .then() function above.
}
};
return factory2;
}]);
Your console.log statement is outside of the callback. That is the issue. You need to console.log in the callback or use a watcher to update it when it gets changed. For future reference you should always copy and paste your code here.
I have this function:
// Gets our sport
get: function (slug, kit) {
// If we have no slug, exit the function
if (!slug)
return $q.reject('No slug supplied.');
// Try to get our sport
return moltin.categories.get(slug).then(function (response) {
// If we have a kit
if (kit) {
// Assign our sport to our kit
kit.team.sport = response.slug;
}
// Return our response
return response;
});
}
As you can see I am using $q.reject() if my required parameter is not set.
The problem is, in a $state resolve method, if I invoke the function like this:
resolve: {
sport: ['$stateParams', 'kit', 'SimpleDesignerSharedSportService', function ($stateParmas, kit, sharedSport) {
// Get our slug
var slug = $stateParmas.sport || kit.team.sport;
// Get our sport
return sharedSport.get(slug, kit);
}]
}
my view will not be shown (it is just blank). But if I change my resolve to this:
resolve: {
sport: ['$stateParams', 'kit', 'SimpleDesignerSharedSportService', function ($stateParmas, kit, sharedSport) {
// Get our slug
var slug = $stateParmas.sport || kit.team.sport;
// Get our sport
return sharedSport.get(slug, kit).then(function (response) {
return response;
}, function (error) {
return null;
});
}],
pageTitle: ['PageHead', function (service) {
service.setTitle('Kudos Sports - Create your kit');
}]
}
it works. I don't want to have to specify a success and fail method. Is there anyway I can get around it?
Resolves expect a promise so that it can resolve it before loading the page. There are a few issues in your code that I will help you identify and solve, however I strongly suggest you take this short course on Udacity and brush up on your understanding of promises.
Firstly, we need to refactor your get method to return a promise once your data is resolved.
// Gets our sport
get: function (slug, kit) {
var deferred = $q.defer();
// If we have no slug, exit the function
if (!slug)
deferred.reject('No slug supplied.');
// Try to get our sport
moltin.categories.get(slug).then(function (response) {
// If we have a kit
if (kit) {
// Assign our sport to our kit
kit.team.sport = response.slug;
}
// Return our response
deferred.resolve(response);
});
return deferred.promise;
}
As you can see, you can't just return the data in the promise. You have to create another promise and return that once there is data.
The next step is to consume this function within a resolve. You seems to have used $stateParmas instead of $stateParams so we correct that as well. Also, it is bad practice to name your services differently in different places. Keeping consistent with naming conventions makes debugging much easier.
resolve: {
sport: ['$stateParams', 'kit', 'SimpleDesignerSharedSportService', function ($stateParams, kit, SimpleDesignerSharedSportService) {
// Get our slug
var slug = $stateParams.sport || kit.team.sport;
// Get our sport
return SimpleDesignerSharedSportService.get(slug, kit);
}]
}
Now the get method returns a promise that only resolves once there is a response from fetching the categories and you once again have control of the data flow.
I have installed the malhar-angular-dashboard module. I do not want to hard code my widgetDefinitions, so I created a service witch will return the array with the widget objects. I am using a rest service to return data such as the widget names, titles, etc. The problem is I get this error and do not know how to resolve :
TypeError: widgetDefs.map is not a function
SERVICE DATA
.factory('widgetRestService',['$http','UrlService','$log','$q',
function($http,UrlService,$log,$q){
var serviceInstance = {};
serviceInstance.getInfo = function(){
var request = $http({method: 'GET', url: '/rest/widgets/getListInfoDashboards'})
.then(function(success){
serviceInstance.widgets = success.data;
$log.debug('serviceInstance.widgets SUCCESS',serviceInstance.widgets);
},function(error){
$log.debug('Error ', error);
$log.debug('serviceInstance.widgets ERROR',serviceInstance.widgets);
});
return request;
};
serviceInstance.getAllWidgets = function () {
if (serviceInstance.widgets) {
return serviceInstance.widgets;
} else {
return [];
}
};
return serviceInstance;
}]);
widgetDefinitions service
.factory('widgetDefinitions',['widgetRestService','$log','$q','$http',function(widgetRestService,$log,$q,$http) {
var widgetDefinitions = [];
return widgetRestService.getInfo().then(function (data) {
var widgets = widgetRestService.getAllWidgets();
$log.debug('widgetsDefs ', widgets);
for (var i = 0; i < widgets.length; i++) {
widgetDefinitions.push(widgets[i]);
}
$log.debug('widgetDefinitions ', widgetDefinitions);
return widgetDefinitions;
});
});
Console
TypeError: widgetDefs.map is not a function
widgetDefs: [Object,Object,Object]
widgetDefinitions: [Object,Object,Object]
Note
If I hard-code my widgetDefinitions-service returned array like this it works, if I returned with my rest service doesn`t work(widgetDefs.map is not a function):
[
{
name:'widgetList',
title:'title1'
},
{
name:'widgetPie',
title:'title2'
},
{
name:'widgetTable',
title:'title3'
}
]
Original issue in github has been commented on and closed.
Here's the comment from github:
#pitong Make sure the dashboard options contains a property named widgetDefinitions and it's an array, even if it's empty. Also make sure the the browser version you're using supports the map function for arrays. This map function for array became the ECMA-262 standard in the 5th edition so your browser version might not have it supported.
I'm trying to get the following findTimelineEntries function inside an Angular controller executing after saveInterview finishes:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId}, function() {
$scope.findTimelineEntries();
});
};
The save action adds or edits data that also is part of the timeline entries and therefore I want the updated timeline entries to be shown.
First I tried changing it to this:
$scope.saveInterview = function() {
var functionReturned = $scope.interviewForm.$save({employeeId: $scope.employeeId});
if (functionReturned) {
$scope.findTimelineEntries();
}
};
Later to this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
$scope.saveInterview.done(function(result) {
$scope.findTimelineEntries();
});
And finaly I found some info about promises so I tried this:
$scope.saveInterview = function() {
$scope.interviewForm.$save({employeeId: $scope.employeeId});
};
var promise = $scope.saveInterview();
promise.done(function() {
$scope.findTimelineEntries();
});
But somehow the fact that it does work this way according to http://nurkiewicz.blogspot.nl/2013/03/promises-and-deferred-objects-in-jquery.html, doesn't mean that I can use the same method on those $scope.someFuntcion = function() functions :-S
Here is a sample using promises. First you'll need to include $q to your controller.
$scope.saveInterview = function() {
var d = $q.defer();
// do something that probably has a callback.
$scope.interviewForm.$save({employeeId: $scope.employeeId}).then(function(data) {
d.resolve(data); // assuming data is something you want to return. It could be true or anything you want.
});
return d.promise;
}
I'm just getting started with Jasmine and trying to set up some tests for the first time. I have a Backbone collection. I figured I would get my collection as part of the beforeEach() method, then perform tests against it.
I have a test json object that I used while I prototyped my app, so rather than mocking an call, I'd prefer to reuse that object for testing.
Here's my code so far (and it is failing).
describe("Vehicle collection", function() {
beforeEach(function() {
this.vehicleCollection = new Incentives.VehiclesCollection();
this.vehicleCollection.url = '../../json/20121029.json';
this.vehicleCollection.fetch();
console.log(this.vehicleCollection);
});
it("should contain models", function() {
expect(this.vehicleCollection.length).toEqual(36);
console.log(this.vehicleCollection.length); // returns 0
});
});
When I console.log in the beforeEach method -- the console look like this ...
d {length: 0, models: Array[0], _byId: Object, _byCid: Object, url: "../../json/20121029.json"}
Curiously when I expand the object (small triangle) in Chrome Developer Tools -- my collection is completely populated with an Array of vehicle models, etc. But still my test fails:
Error: Expected 0 to equal 36
I'm wondering if I need to leverage the "waitsFor()" method?
UPDATE (with working code)
Thanks for the help!
#deven98602 -- you got me on the right track. Ultimately, this "waitsFor()" implementation finally worked. I hope this code helps others! Leave comments if this is a poor technique. Thanks!
describe("A Vehicle collection", function() {
it("should contain models", function() {
var result;
var vehicleCollection = new Incentives.VehiclesCollection();
vehicleCollection.url = '/json/20121029.json';
getCollection();
waitsFor(function() {
return result === true;
}, "to retrive all vehicles from json", 3000);
runs(function() {
expect(vehicleCollection.length).toEqual(36);
});
function getCollection() {
vehicleCollection.fetch({
success : function(){
result = true;
},
error : function () {
result = false;
}
});
}
});
});
Just glancing at your code, it looks to me like fetch has not yet populated the collection when you run the expectation.
You can use the return value from fetch to defer the expectation until the response is received using waitsFor and runs:
beforeEach(function() {
this.vehicleCollection = new Incentives.VehiclesCollection();
this.vehicleCollection.url = '../../json/20121029.json';
var deferred = this.vehicleCollection.fetch();
waitsFor(function() { return deferred.done() && true });
});
it("should contain models", function() {
runs(function() {
expect(this.vehicleCollection.length).toEqual(36);
});
});
I haven't actually tried this can't guarantee that it will work as-is, but the solution will look something like this. See this article for more on asynchronous testing with Jasmine.
the collection.fetch() is asyn call that accepts success and error callbacks
var result;
this.collection.fetch({success : function(){
result = true;
}})
waitsFor(function() {
return response !== undefined;
}, 'must be set to true', 1000);
runs(function() {
expect(this.vehicleCollection.length).toEqual(36);
console.log(this.vehicleCollection.length); // returns 0
});