AngularJS - understanding scopes - javascript

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

Related

Angular not recognising my controller

I cant understand why but angular is giving me the following error and Reading more about it online I understand others have the same error when they have some kind of typo in naming the controller or modules. So I have checked config, service and html code 100 times and I believe they all look fine;
I can't understand what am I missing? or what to look for!
Many Thanks in Advance.
Error:
ng:areq; Bad Argument
Argument 'LoyaltyController' is not
Description
AngularJS often asserts that certain values will be present and truthy using a helper function. If the assertion fails, this error is thrown. To fix this problem, make sure that the value the assertion expects is defined and truthy.
MY Loyalty Controller
(function () {
'use strict';
angular
.module('app')
.controller('LoyaltyController', LoyaltyController);
LoyaltyController.$inject = ['navigationService','loyaltyService', '$scope', 'ionicMaterialInk', 'ionicMaterialMotion'];
function LoyaltyController(navigationService, loyaltyService, dealService, $scope, ionicMaterialInk, ionicMaterialMotion) {
// Set Header
$scope.$parent.$parent.$parent.showHeader();
$scope.$parent.$parent.$parent.clearFabs();
$scope.$parent.$parent.$parent.setExpanded(false);
$scope.$parent.$parent.$parent.setHeaderFab(false);
var vm = this;
vm.loyalty = [];
loyaltyService.getUserLoyalty()
.success(function (data, status, headers, config) {
vm.loyalty = data;
});
vm.menuItems = [];
navigationService.getAllNavigations()
.success(function (data, status, headers, config) {
vm.menuItems = data;
$timeout(function () {
// Set Motion
ionicMaterialMotion.fadeSlideInRight();
// Set Ink
ionicMaterialInk.displayEffect();
}, 100);
});
// Delay expansion
$timeout(function () {
ionicMaterialMotion.slideUp({
selector: '.slide-up'
});
}, 300);
};
})();
You forget to inject dealService But you mentioned it in controller method.
LoyaltyController.$inject = ['navigationService','loyaltyService', '$scope', 'ionicMaterialInk', 'ionicMaterialMotion'];
function LoyaltyController(navigationService, loyaltyService, dealService, $scope, ionicMaterialInk, ionicMaterialMotion) {
^
|
here
You have to inject according to your controller method parameter or you have to declare controller method parameter according to your injector Whetever language you choose both have to be equal and according to order .
LoyaltyController.$inject = ['navigationService','loyaltyService','dealService','$scope', 'ionicMaterialInk', 'ionicMaterialMotion'];
The issue was that I forgot to attach the controller and service to my main html.
also as Anik pointed out I was calling the dealService without injecting it in my controller. another issue was the timeout function, I had to more it up in the file for it to work.

AngularJS: Throws Unknown Provider error

I have looked over many threads of this error, yet following the instructions on 2 of them, it still throws the same error, here is my service:
angular.module('sccateringApp')
.service('httpcalls', function ($scope, $http) {
var BackEndBaseURL = "methods/server.php";
return {
..
}
});
And here is my controller:
angular.module('sccateringApp')
.controller('newCategoryController', ['httpcalls', '$scope', function (httpcalls, $scope) {
$scope.submitForm = function(){
alert();
}
}]);
I can't really identify what the problem is, since I already included the service per se as a dependency of the controller. Any help will be much appreciated!
Update: Phil is right, the real provider error comes from the dependency on $scope, credits to him when he post it.
It looks like you are creating a factory and not a service.
You don't need to return anything from a service, but declare things in the this (it is a prototype instanciation, like a class).
sccateringApp.service('httpcalls', function ($scope, $http) {
var BackEndBaseURL = "methods/server.php";
this.someMethod = function() { ... }
this.someProperty = ...
});
Otherwise, just replace module.service by module.factory

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

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/

Jasmine angular - mocking http request

I've just read a lot of articles about mocking $http and something is wrong with my code. I still have error: No pending request to flush !
My method from controllers.js looks similar to this (browserDebugMode, webRoot, commentsAction are global variables - it wasn't mi idea to make it global :D)
$scope.getComments = function(){
if (browserDebugMode) {
$http({
method : "GET",
url : webRoot+commentsAction,
params : {action: "list"},
})
.success(function(data, status) {
//...
})
.error(function(data, status) {
//...
});
}
}
And now test for it:
var browserDebugMode = true;
var webRoot = "http://localhost/name";
var commentsAction = '/commentsMobile.php';
describe('myApp', function() {
var scope,
httpBackend,
http,
controller;
beforeEach(angular.mock.module('myApp'));
describe('NewsDetailCtrl', function() {
beforeEach(inject(function ($rootScope, $controller, $httpBackend, $http) {
scope = $rootScope.$new();
httpBackend = $httpBackend;
http = $http;
httpBackend.when("GET", webRoot+commentsAction).respond([{}]);
controller = $controller('NewsDetailCtrl', {
'$scope': scope, 'GlobalService': globalService, $http: $http
});
}));
it('checks if AJAX is done', function () {
httpBackend.expectGET(webRoot+commentsAction).respond([{}]);
scope.getComments()
httpBackend.flush();
});
});
});
And please don't ask for PHP script :) I was pushed to do it.
I just want to check if I can test $http, nothing more. I don't know what I do wrong. I tested other things in that controller and it was okay, I looked if getComments() is fired with console.log and it's fired. Something must be wrong with configuring it.
Your code under test and the unit tests execute in different contexts, so they will have different global objects and therefore the browserDebugMode that exists in your tests is different to the one in your actual code.
The controller should inject $window (Angular's wrapper around the window object) and then check the browserDebugMode property of that:
if ($window.browserDebugMode) {
// actual code
}
The tests should also inject $window and then set the browserDebugMode property of that:
beforeEach(inject(function ($window) {
$window.browserDebugMode = true;
}));
Now both the controller and tests will reference the same global object, the if condition should evaluate true, and the $http call should execute.

$scope is undefined in Controller

I have the worlds simplest controller which i want access to $scope but it is "undefined" and I cannot, for the life of me work out why, however all the functions are called corectly, the DataService, etc is working perfectly, just i have no access to $scope?!
controller code is below
app = angular.module("windscreens", []);
app.controller('DamageCtrl', function($scope, DataService) {
$scope.setDamageLocation = function(location) {
DataService.getDamage().setLocation(location);
};
$scope.isLocation = function(requiredLocation) {
return DataService.getDamage().getLocation() === requiredLocation;
};
$scope.progress = function() {
};
});
To access a property on the scope from your HTML template you only need to use the property name, you don't need to write $scope with it.
Example:
<button ng-click="progress()"></button>
In your javascript you will only have access to the $scope inside the controller and its functions. If you call an external resource, for example: DataService module, it won't have access to the $scope unless you pass it as an argument explicitly.
I managed to get it working using the alternative syntax. As detailed below, still not sure why it wasn't working but hey hum
app.controller('DamageCtrl', ['$scope', 'DataService',
function($scope, DataService) {
$scope.setDamageLocation = function(location) {
DataService.getDamage().setLocation(location);
};
$scope.isLocation = function(requiredLocation) {
return DataService.getDamage().getLocation() === requiredLocation;
};
$scope.progress = function() {
console.log($scope);
};
}
]);

Categories