$.getJSON call from angular.element(document).ready or initController - javascript

I am using the following snippet to get the data from server to load the initial content of the page.
(function () {
'use strict';
angular
.module('app')
.controller('MyController', MyController);
MyController.$inject = ['UserService', '$rootScope', '$scope', '$cookieStore', 'AuthenticationService'];
function MyController(UserService, $rootScope, $scope, $cookieStore, AuthenticationService) {
/*Discussion Board related Functions*/
angular.element(document).ready(function () {
// Get Current User
$rootScope.globals = $cookieStore.get('globals') || {};
$scope.currentUser = AuthenticationService.GetAuthData($rootScope.globals.currentUser.authdata);
$.getJSON(
"http://localhost/getSomeServerData.php",
{ userName: $scope.currentUser },
$scope.getSomeDataResponse
);
});
$scope.getSomeDataResponse = function (jason) {
var servRet = jason;
alert("On Load Called-2");
};
}
})();
However, the response function $scope.getSomeDataResponse is not getting called.
Please let me know what is wrong with this approach.

However, the response function $scope.getSomeDataResponse is not
getting called.
Please let me know what is wrong with this approach.
The issue is, you are referencing getSomeDataResponse before it is being declared. Also, you are using jQuery's getJSON() to get the data from server using HTTP GET request in your angularJS code.
This is particularly not a recommended practise. If you include jQuery in your page, AngularJS will use jQuery instead of jqLite when wrapping elements within your directives, otherwise it'll delegable to jqLite(which might not work in all cases). To be at a safer side, use angular's $http.get() service instead of $.getJSON()
$http.get("http://localhost/getSomeServerData.php",{ userName: $scope.currentUser})
.then(function(jason){
//success handler function
var servRet = jason;
alert("On Load Called-2");
},function(err){
//error handler function
alert(err);
})
Ofcourse you'd need to inject $http service in your controller to make it work. Have a look at this thread for other possible alternatives.
Cheers!

First, you have to define your result function before using.
Second, you have to use $.getJSON correctly. So this worked for me after fixing
$scope.getSomeDataResponse = function (jason) {
var servRet = jason;
alert("On Load Called-2");
};
/*Discussion Board related Functions*/
angular.element(document).ready(function () {
// Get Current User
$rootScope.globals = $cookieStore.get('globals') || {};
$scope.currentUser = AuthenticationService.GetAuthData($rootScope.globals.currentUser.authdata);
$.getJSON(
"http://localhost/getSomeServerData.php",
{ userName: $scope.currentUser }
).always($scope.getSomeDataResponse);
the callback method was different in jQuery. use fail and done callbacks by looking at the jQuery documentation
http://api.jquery.com/jquery.getjson/

Related

Using http requests, promises, ng-options, and factories or services together

I'm trying to retrieve a list of options from our database and I'm trying to use angular to do it. I've never used services before but I know that's going to be the best way to accomplish what I want if I'm going to use data from my object in other controllers on the page.
I followed a couple tutorials and put together a factory that makes an http request and returns the data. I've tried several ways of doing it, but for some reason nothing is happening. It's like it never runs the factory function and I can't figure out why.
Factory:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {};
locaService.locations = {};
var resorts = {};
locaService.getLocations=
function() {
$http.get('/url/url/dest/').success(function (data) {
locaService.locations = data;
});
return locaService.locations;
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
$scope.resort;
$scope.locations= locaService.getLocations();
}]);
I just want the data to be returned and then assigned to the $scope.locations to be used for ng-options in the view. Then I want my other function to run on click for the next field to be populated by the variable resort. How would I do this? Any help would be great! Thanks!
$http service returns a promise, and your function should return that promise. Basically your getLocations function should be something like the following
locaService.getLocations=
function() {
return $http.get('/url/url/dest/');
};
Then in your controller you should retrieve the options using this promise:
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
Using jquery in controllers or manipulating dom elements in controllers is not a good practice, you can apply styles and css classes directly in views using ng-style or ng-class.
Here is an example how all it should look wired up:
resortModule= angular.module('resortApp',[]);
resortModule.factory('locaService',['$http', function ($http){
var locaService= {
locations: {}
};
var resorts = {};
locaService.getLocations= function() {
return $http.get('/url/url/dest/');
};
return locaService;
//This is a function I would like to run in addition to the first one so multiple variables would be stored and accessible
/*getResorts:
function(destination) {
$http.get('/url/url/dest/' + destination.id).success(function (data) {
resorts = data;
});
return resorts;
}*/
}]);
resortModule.controller('queryController',['$scope', 'locaService', function($scope, locaService) {
/* Apply these styles in html using ng-style
$scope.checkConditional= function (){
if($("#location").val() == ""){
$("#location").css('border','2px solid #EC7C22');
}
};
$scope.selectCheck= function (){
$("#location").css('border','2px solid #ffffff');
$(".conditional-check").hide();
};
*/
$scope.resort;
locaService.getLocations()
.then(
function(locations) // $http returned a successful result
{$scope.locations = locations;}
,function(err){console.log(err)} // incase $http created an error, log the returned error);
}]);

Cannot inject factory into controller in Jasmine (requireJS)

I have some issues injecting my factory into testing spec. I am using requireJS to inject controllers and factories etc.
define(['controller', 'loginFactory', 'angular', 'angularMocks'],
function(ctrl, loginFactory, angular){
var scope,
OnBoardingCtrl;
describe('Controller: OnBoarding', function () {
beforeEach(angular.mock.inject(function ($rootScope, $controller, $location) {
angular.module('app');
scope = $rootScope.$new();
OnBoardingCtrl = $controller(ctrl, {
'$scope': scope,
'loginFactory': loginFactory,
});
}));
it('Should check endpoint', inject(function ($http, $httpBackend) {
var successCallback = jasmine.createSpy();
var url = 'login?un=test&pw=test';
var response = {"token":1}
$httpBackend.expectGET(url)
.respond(200, response);
$http.get(url).success(successCallback);
expect(successCallback).not.toHaveBeenCalled();
$httpBackend.flush();
expect(successCallback.token).toBe(1);
}));
});
}
);
How ever I keep getting TypeError: 'undefined' is not an object (evaluating 'successCallback.token) For reference my LoginFactory looks like this:
var LoginFactory = function ($q, $http) {
return {
getData: function (url) {
var deferred = $q.defer();
$http.get('http://local/'+url)
.then(function () {
deferred.resolve(true);
}, function () {
deferred.resolve(false);
});
return deferred.promise;
}
};
};
LoginFactory.$inject = ['$q', '$http'];
factories.factory('LoginFactory', LoginFactory);
return LoginFactory;
Thanks in advance!
As you mentioned already, successCallback.token is the point where your code is breaking because jasmine.createSpy() returns a function but it has no clue about your token from your mock response served by $httpBackend. From the official docs,
When there is not a function to spy on, jasmine.createSpy can create a “bare” spy.
This spy acts as any other spy – tracking calls, arguments, etc.
But there is no implementation behind it. Spies are JavaScript objects
and can be used as such.
So removing this line expect(successCallback.token).toBe(1); will resolve the error but if you actually want to verify the mock response from $httpBackend, you need to do that in success callback on $http (or) in the promise object's then method like below
$http.get(url).success(function(response){
expect(response.token).toBe(1);
});
Note that here I just modified your code to show the example, ideally you don't code the HTTP call separately in your test case, you invoke the actual function making the HTTP call and verify the expectations.
For more details on $httpBackend, have a look at this link.

Scope changes from inside a Promise

I'm using BreezeJS with AngularJS but I'm having a difficult time understanding how to get Promises to work with $scope. Whenever I try to submit my form its not showing the validation errors until I click it for a 2nd time. I realize I could call $scope.$apply() but I read its not best practice? Here is my code:
app.controller("MainController", ["$scope", "$q", "datacontext", function ($scope, $q, datacontext) {
datacontext.manager.fetchMetadata();
$scope.errors = [];
$scope.addDamp = function () {
var item = datacontext.manager.createEntity("Damp", {
name: $scope.newDamp
});
var tes = datacontext.manager.saveChanges()
.then(function () {
alert("yay");
})
.fail(function (error, a, b, c) {
var arr = [];
error.entitiesWithErrors.map(function (entity) {
entity.entityAspect.getValidationErrors().map(function (validationError) {
arr.push(validationError.errorMessage);
});
});
$scope.errors = arr;
datacontext.manager.rejectChanges();
});
};
}]);
What is the best way to go about handling scope changes that come from inside of a Promise?
Yes, you're going to need $scope.apply here, because the promise isn't coming out of a core Angular call (such as $http, which would have handled the .apply() itself behind the scenes). In fact, the Breeze/Angular example on the BreezeJS page (http://www.breezejs.com/samples/todo-angular) includes a $scope.apply() after its data retrieval:
datacontext.getAllTodos()
.then(success)
.fail(failed)
.fin(refreshView);
function refreshView() {
$scope.$apply();
}
It's a bad practice to toss $scope.apply() about where you don't need it. But when you're handling promises created outside of Angular itself, it's going to come up.

Loading one dataset using data from another in Angular $watch

I am creating a messaging service that needs to do the following 1.) Load a messsage from our messages service, get the recipient's ids, and then load the recipients' info from a users service. I've tried both using the messages service callback, and also creating a watcher on the message object, without much success. The service works, but it doesn't assign the result to the $scope correctly. Here's the controller. All of the services are working correctly:
function MessageCtrl ($scope, $http, $location, $routeParams, Messages, Members) {
if ($routeParams.mid) { // Checks for the id of the message in the route. Otherwise, creates a new message.
$scope.mid = $routeParams.mid;
$scope.message = Messages.messages({mid: $scope.mid}).query();
$scope.$watch("message", function (newVal, oldVal, scope) {
if (newVal.data) {
$scope.recipients = Members.members({uids: newVal.data[0].uids}).query();
}
}, true);
} else {
$scope.create = true;
}
// Events
$scope.save = function () { };
$scope.preview = function () { };
$scope.send = function () { };
}
The correct way to use query is to perform the action in the callback that is passed in query function. In other words$scope.message should be assigned in the callback. Also you don't need a $watch. You can call the other service within the callback directly. But to keep it clean please use deferred
http://docs.angularjs.org/api/ngResource.$resource
http://docs.angularjs.org/api/ng.$q

AngularJS - understanding scopes

I'm confused, I have this module which routes to different controllers:
var mainModule = angular.module('lpConnect', []).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/home', {template:'views/home.html', controller:HomeCtrl}).
when('/admin', {template:'views/admin.html', controller:AdminCtrl}).
when('/connect', {template:'views/fb_connect.html', controller:MainAppCtrl}).
otherwise({redirectTo:'/connect'});
}]);
and a Common service like so:
mainModule.factory('Common', ['$rootScope', '$http', function (scope, http) {
var methods = {
changeLanguage:function (langID) {
http.get('JSON/langs/' + langID + '/captions.json').success(function (data) {
scope.lang = data;
});
},
initChat:function () {
console.log(scope); // full object
console.log(scope.settings); // undefined
}
};
//initiate
http.get('JSON/settings/settings.json').success(function (data) {
scope.settings = data;
methods.changeLanguage(scope.settings.lang);
});
return methods;
}]);
the app loads and gets (through XHR) the settings object, and I can see the settings reflects in my DOM. (captions for example)
Now when I call the initChat method from my HomeCtrl I get an undefined value when trying to access the scope.settings property ... what's strange is that when I log the scope I can see the settings object ... What am I missing?
Update: I found out that what I'm doing wrong is calling my method directly from the controller body:
function HomeCtrl($scope, $location, Common) {
...
Common.initChat()
...
}
if I change the call to be triggered by a click all works fine, but I do need this code to run when the page loads, What is the right approach?
It's a simple problem, I think: You're calling initChat in your scope before the $http call retrieves scope.settings.
Couple of things.
http is async and that is your main problem (as Andy astutely pointed out)
ng-init is not recommended for production code, initializing in controllers is better
initializing your scope.settings = {} or a decent default may help you, once xhr is done then your settings will be available.
hope this helps
--dan

Categories