Calling function within angular function - javascript

I'm trying to convert my $scope code to 'ControllerAs' code and I am having trouble writing a function inside my controller function.
index.html
<html ng-app="main">
<head>
<script src="angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="MainController as mainCtrl">
{{mainCtrl.message}}
{{mainCtrl.result.username}}
</body>
</html>
script.js
(function() {
angular.module("main", [])
.controller("MainController", ["$http",MainController]);
function MainController($http) {
this.message = "Hello Angular!";
this.result = callFunction($http);
var callFunction = function($http) {
return $http.get("https://api.github.com/users/robconery")
.then(onUserComplete);
};
var onUserComplete = function($response) {
return $response.data;
};
};
}());
Here is the $scope code that I am trying to convert.
(function() {
var app = angular.module("githubViewer", []);
var MainController = function($scope, $http) {
var onUserComplete = function(response) {
$scope.user = response.data;
};
var onError = function(reason) {
$scope.error = "Could not fetch the user";
};
$http.get("https://api.github.com/users/robconery")
.then(onUserComplete, onError);
$scope.message = "Hello, Angular!";
};
app.controller("MainController", ["$scope", "$http", MainController]);
}());

You are invoking callFunction before it is defined. You need to either use a function declaration or move callFunction before the invocation. Here's an example of both of those choices.
Function Declaration
(function() {
angular.module("main", [])
.controller("MainController", ["$http",MainController]);
function MainController($http) {
this.message = "Hello Angular!";
this.result = callFunction($http);
}
function onUserComplete(response) {
return response.data;
}
function callFunction($http) {
return $http.get('https://api.github.com/users/robconery')
.then(onUserComplete);
}
}());
Or:
(function() {
angular.module("main", [])
.controller("MainController", ["$http",MainController]);
var callFunction = function($http) {
return $http.get("https://api.github.com/users/robconery")
.then(onUserComplete);
};
var onUserComplete = function($response) {
return $response.data;
};
function MainController($http) {
this.message = "Hello Angular!";
this.result = callFunction($http);
}
}());
See this excellent StackOverflow answer for the differences between these two syntaxes.

Aside from the function definition problem in #Dan's answer, there is another issue: you should not bind callFunction($http) in the template, callFunction($http) returns a Promise, it doesn't evaluates to the response, even after the onUserComplete callback.
This works for me:
function callFunction($http){
return $http.get("https://api.github.com/users/robconery")
.then(onUserComplete);
}
var that = this;
function onUserComplete($response){
that.result = $response.data;
}
callFunction($http);
EDIT:
Your $scope version code works fine, because in the onUserComplete() function, you assign to $scope.result, not return $response.data. You see, when you return from onUserComplete, then() doesn't return that, it still returns the promise, that's because it needs to support chaining.

Related

Angular Controller Testing: check dependency injection is done right

Sometimes, often when refactoring a Controller - or also a Factory -, I end up passing my Controller tests but when my app is up and running it keeps crashing at some point because I forgot to add/update dependency injection.
With this I mean, let's say I have the following Controller where I used to have an oldDependency but due to refactoring I'm using a newDependency instead. I update MyCtrl.$inject with the new changes but I forget to update the dependencies passed to the MyCtrl function:
angular
.module('my-module')
.controller('MyCtrl', MyCtrl);
MyCtrl.$inject = [
'firstDependency',
'secondDependency',
'newDependency' // this has been only updated here
];
function MyCtrl(firstDependency, secondDependency, oldDependency) {
var vm = this;
// My Controller's code
// etc...
function someFunc(x) {
// here I use newDependency
return newDependency.doSomething(x);
}
}
So what happens then? I go and update MyCtrl tests, where I actually remember to update the dependency object passed to $controller():
// MyCtrl testing code
var MyCtrl = $controller('VolunteerResignCtrl', {
firstDependency: firstDependency,
secondDependency: secondDependency,
newDependency: newDependency
});
Because of this, all MyCtrl tests keep passing, so I think that nothing broke. But it actually did.
Could anyone tell me if this can be tested somehow and avoid my app failing for this reason in the future?
This seems like an issue with JS scoping rather than Angulars dependency injection. Try wrapping your test cases and the functions that you use for them in an IIFE as it they're not, JS will go up the scope until it can find a variable called newDependency which is probably why your tests are running but it's crashing in your app.
As you can see from the examples below
Without an IIFE
var myApp = angular.module('myApp', []);
myApp.factory('firstDependency', function() {
return {}
}());
myApp.factory('secondDependency', function() {
return {}
}());
myApp.factory('newDependency', function() {
return {}
}())
myApp.controller('MyCtrl', MyCtrl);
myApp.controller('TestCtrl', TestCtrl);
MyCtrl.$inject = [
'firstDependency',
'secondDependency',
'newDependency'
];
function MyCtrl(firstDependency, secondDependency, oldDependency) {
var vm = this;
vm.someFunc = function(x) {
// Will work fine as newDependency is defined in a higher scope :c
return newDependency.doSomething(x);
}
}
var firstDependency = function() {
return {}
}();
var secondDependency = function() {
return {}
}();
var newDependency = function() {
return {
doSomething: function(x) {
return x;
}
}
}()
function TestCtrl($scope, $controller) {
var test = $controller('MyCtrl', {
firstDependency: firstDependency,
secondDependency: secondDependency,
newDependency: newDependency
});
$scope.someFunc = test.someFunc("you're inside the new dependency");
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="TestCtrl">
{{someFunc}}!
</div>
</div>
With an IIFE
var myApp = angular.module('myApp', []);
myApp.factory('firstDependency', function() {
return {}
}());
myApp.factory('secondDependency', function() {
return {}
}());
myApp.factory('newDependency', function() {
return {}
}())
myApp.controller('MyCtrl', MyCtrl);
MyCtrl.$inject = [
'firstDependency',
'secondDependency',
'newDependency'
];
function MyCtrl(firstDependency, secondDependency, oldDependency) {
var vm = this;
vm.someFunc = function(x) {
//Will throw an error, but this time it's a good error because that's what you want!
return newDependency.doSomething(x);
}
}
(function(myApp) {
var firstDependency = function() {
return {}
}();
var secondDependency = function() {
return {}
}();
var newDependency = function() {
return {
doSomething: function(x) {
return x;
}
}
}()
myApp.controller('TestCtrl', TestCtrl);
function TestCtrl($scope, $controller) {
var test = $controller('MyCtrl', {
firstDependency: firstDependency,
secondDependency: secondDependency,
newDependency: newDependency
});
$scope.someFunc = test.someFunc("you're inside the new dependency");
}
})(myApp)
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="TestCtrl">
{{someFunc}}!
</div>
</div>
Name of variables are not important from js-point of view.
Following functions are SAME:
function f(SomeService) {
SomeService.test();
}
function f(NewService) {
NewService.test();
}
function f(a) {
a.test();
}
Thats the reason why you need $inject - cause you pass string their.
After you change name in $inject - you may change it in function, but you also may not change it. Doesnt matter at all.

angularJS 1.4.0 Argument 'MainController' is not a function, got undefined

(function () {
var app = angular.module("Sports", []);
var MainController = function($scope, $http) {
var onUser = function (response) {
obj = JSON.parse(response);
$scope.sport = angular.fromJson(obj);
};
$http.get("/api/SportApi/Get").success(function (response) {
obj = JSON.parse(response);
$scope.sport = angular.fromJson(obj);
});
};
app.controller("MainController", ["$scope", "$http", MainController]);
}());
So yeah, this script is not working, getting the error it can not find the "main controller as function" whats the problem?
EDIT:
the error cause is in this function:
function consoleLog(type) {
var console = $window.console || {},
logFn = console[type] || console.log || noop,
hasApply = false;
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
// The reason behind this is that console.log has type "object" in IE8...
try {
hasApply = !!logFn.apply;
} catch (e) {}
if (hasApply) {
return function() {
var args = [];
forEach(arguments, function(arg) {
args.push(formatError(arg));
});
return logFn.apply(console, args); //throws exception
};
}
Fixed you fiddle. Possibly, problem is in immediate function. Also fixed ng-app and response processing
HTML
<div ng-app="Sports">
<div ng-controller="MainController">
<table class="table table-striped table-hover">
<thead>Sport</thead>
<tr ng-repeat="x in sport">
{{sport}}
</tr>
</table>
</div>
</div>
JS
angular
.module("Sports", [])
.controller("MainController", ["$scope", "$http", function($scope, $http) {
$http.get("https://www.googleapis.com/books/v1/volumes?q=isbn:0747532699")
.success(function (response) {
console.log(response);
$scope.sport = response.items;
});
}]);
Update
Plunker version for AngularJS v1.3.x
Order matters :-
app.controller("MainController", MainController);
var MainController = function($scope, $http) {
var onUser = function (response) {
obj = JSON.parse(response);
$scope.sport = angular.fromJson(obj);
};
$http.get("/api/SportApi/Get").success(function (response) {
obj = JSON.parse(response);
$scope.sport = angular.fromJson(obj);
});
};
MainController.$inject = ['$scope','$http'];
Here is Working Fiddle, Its just a basic because I guess you have problem with finding your controller... I hope it helps you
link
(function(){
var app = angular.module("sports",[]);
app.controller("MainController", function($scope){
this.msg = 'Hello World';
});
})();
I guess you have have messed up with closure (Brackets defining self invoking functions in JS. ), which I have corrected.
And do follow definition structure proposed by Angular Docs.
For angularjs v1.4.x the success and error methods are now deprecated
// Simple GET request example :
$http.get('/someUrl').
then(function(response) {
// this callback will be called asynchronously
// when the response is available
}, function(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
then() method is the replacement for deprecated method success()
Github Reference link
API Reference link

using angular.copy to extend a service

I have made baseService in angular as following.
app.service('baseService', ['$http', function($http) {
'use strict';
this.service = "";
this.load = function(){
// implement load
}
}]);
Now i want other services to extend this service as the implementation of load method will be same for all the services. My derived service will look as follow.
app.service('jobsService', ['baseService', function( baseService ){
'use strict';
angular.copy(baseService, this);
this.service = 'jobs';
}]);
Here I am using angular.copy(baseService, this); to extend the baseService. Is this a right approach to inherit a service?
Yes... I'd say so.
You're approach is fine and I'd agree that this an appropriate solution for this. More of a best practice question rather than specifics. Here is a fully working example with both the service and factory patterns Angular provides for different development preference
fiddle
angular.module('app', []);
angular.module('app').factory('BaseFactory', [ function() {
var load = function() {
console.log('loading factory base');
}
return { 'load': load }
}]);
angular.module('app').factory('ChildFactory', ['BaseFactory', function (BaseService) {
var child = angular.copy(BaseService);
child.childLoad = function () {
console.log('loading factory child');
};
return child;
}]);
angular.module('app').service('BaseService', [function() {
this.load = function() {
console.log('loading service base');
}
}]);
angular.module('app').service('ChildService', ['BaseService', function(BaseService) {
angular.copy(BaseService, this);
this.childLoad = function() {
console.log('loading service child');
}
}]);
angular.module('app').controller('ctrl', ['$scope', 'ChildFactory', 'ChildService', function($scope, ChildFactory, ChildService) {
ChildService.load(); // 'loading service base'
ChildService.childLoad(); // 'loading service child'
ChildFactory.load(); // 'loading factory base'
ChildFactory.childLoad(); // 'loading factory child'
}]);
A simpler and fairly straightforward option will be to use John Resig's Simple JavaScript Inheritance class.
This gives you a more object oriented approach to inheriting/extending classes plus extra functionalities like constructor calls, function overrides etc.
Using your example:
'use strict';
angular.module('app').factory('BaseService', function ($http) {
var service = Class.extend({ //Class from John Resig's code
init: function (params) {
this.params = params;
},
load: function (params) {
return;
},
return service;
});
Then to extend/inherit:
angular.module('app').factory('JobService', function (BaseService) {
var service = BaseService.extend({
init: function (params) {
this._super(params);
}
});
return new service;
});
var BaseService = (function () {
var privateVar = 0;
return {
someAwesomeStuff: function () {
if (privateVar === 42) {
alert('You reached the answer!');
}
privateVar += 1;
};
};
}());
And create child service like this:
var ChildService = Object.create(BaseService);
ChildService.someMoreAwesomeStuff = function () {
//body...
};
module.factory('ChildService', function () {
return ChildService;
});
and you can use this ina controller like this:
function MyCtrl(ChildService) {
ChildService.someAwesomeStuff();
}
reference
Here is your solution (extended2Service), and a solution that i usually use (extendedService).
Personally i prefer my solution because i don't made a new istance of that object.
angular.module('app',[])
.controller('mainCtrl', ['$scope', 'baseService', 'extendedService', 'extended2Service', function($scope, baseService, extendedService, extended2Service ){
$scope.test = 8;
$scope.resultBase = baseService.addOne($scope.test);
$scope.resultExtended = extendedService.sum($scope.test, 10);
$scope.result2Extended = extended2Service.sum($scope.test, 10);
console.log(extendedService);
console.log(extended2Service);
}])
.factory('baseService', function(){
var baseService = {};
var _addOne = function(n) {
return n+1;
};
baseService.addOne = _addOne;
return baseService;
})
.factory('extendedService', ['baseService', function(baseService){
var extendedService = {};
var _sum = function(a, b){
for (var i = 0; i < b; i++) {
a = baseService.addOne(a);
}
return a;
};
extendedService.addOne = baseService.addOne;
extendedService.sum = _sum;
return extendedService;
}])
.factory('extended2Service', ['baseService', function(baseService){
var extended2Service = angular.copy(baseService);
var _sum = function(a, b){
for (var i = 0; i < b; i++) {
a = baseService.addOne(a);
}
return a;
};
extended2Service.sum = _sum;
return extended2Service;
}]);
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div ng-app="app" ng-controller="mainCtrl">
Value: {{test}} <br>
Base: {{resultBase}} <br>
resultExtended : {{resultExtended}} <br>
result2Extended: {{result2Extended}}
<div>

Angular: Update data every few seconds

Maybe this question has already been asked, but I searched and tried most of my afternoon without any success so I really hope somebody can help me with this.
I want to be able to update my $http.get() - my data - that I have set in a factory service, every few seconds.
I added some comment to my code and also left some old stuff for you guys to see what I have tried. (the old stuff is also commented out)
My code:
ovwid.factory('recentClients', [
'$http',
'$rootScope',
function ($http, $rootScope) {
var apiURL = '/plugins/data/get_client.php';
var promise;
var recentClients =
{
async: function()
{
if ( !promise )
{
// $http returns a promise, which has a 'then' function, which also returns a promise
promise =
$http.get(apiURL)
.then(function (response) {
// The then function here is an opportunity to modify the response
// The return value gets picked up by the then in the controller.
return response.data;
});
}
// Return a promise to the controller
return promise;
}
}
return recentClients;
}]);
ovwid.controller(‘client’Ctrl, [
'$scope',
'recentClients',
'$interval',
function ($scope, recentClients, $interval) {
$scope.loading = true;
function reloadData() {
// a call to the async method
recentClients().async().then(function(data) {
// console.log(data);
$scope.loading = false;
$scope.client = data;
});
}
// Initizialize function
reloadData();
// Start Interval
var timerData =
$interval(function () {
reloadData();
}, 1000);
// function myIntervalFunction() {
// var cancelRefresh = $timeout(function myFunction() {
// reloadData();
// console.log('data refres')
// cancelRefresh = $timeout(myFunction, 5000);
// },5000);
// };
// myIntervalFunction();
// $scope.$on('$destroy', function(e) {
// $timeout.cancel(cancelRefresh);
// });
}]); // [/controller]
I see several issues.
First:
if ( !promise ) is only going to return true the first time. You are assigning it to the $http call.
Secondly:
You never access the async method in your factory.
You either need to return that from factory return recentClients.async or call it from scope recentClients.async().then(..
may be it will help
function reloadData() {
// a call to the async method
$scope.loading = true;
recentClients().then(function(data) {
// console.log(data);
$scope.loading = false;
$scope.client = data;
});
}
// Start Interval
var timerData =
$interval(function () {
if(!$scope.loading){
reloadData();
}
}, 1000);
A few things :)
recentClients().then(function(data)... will not work, in your current code it should be: recentClients.async().then(function(data)
(same remark would apply to ` and ’ qoutes that can get really tricky.
This is the syntax I use for designing services:
ovwid.factory('recentClients', ['$http', '$rootScope', function ($http, $rootScope) {
var apiURL = 'aaa.api';
var recentClients = function() {
return $http.get(apiURL)
}
return {
recentClients : recentClients
};
}]);
Full example:
(just create aaa.api file with some dummy data, fire up a server and you'll see that data is changing)
<!DOCTYPE html>
<html>
<head>
<title>Sorting stuff</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script>
var ovwid = angular.module("ovwid", []);
ovwid.factory('recentClients', ['$http', '$rootScope', function ($http, $rootScope) {
var apiURL = 'aaa.api';
var recentClients = function() {
return $http.get(apiURL)
}
return {
recentClients : recentClients
};
}]);
ovwid.controller('clientCtrl', [
'$scope',
'recentClients',
'$interval',
function ($scope, recentClients, $interval) {
$scope.loading = true;
function reloadData() {
// a call to the async method
recentClients.recentClients().then(function(response) {
// console.log(data);
$scope.loading = false;
$scope.client = response.data;
});
}
// Initizialize function
reloadData();
// Start Interval
var timerData =
$interval(function () {
reloadData();
}, 1000);
}]);
</script>
</head>
<body ng-app="ovwid" ng-controller="clientCtrl">
{{ client }}
</body>
</html>
You can set up a service to perform periodic server calls for you. I had found this code somewhere awhile back and refined it a bit. I wish I could remember where I got it.
angular.module('my.services').factory('timeSrv',['$timeout',function($timeout){
//-- Variables --//
var _intervals = {}, _intervalUID = 1;
//-- Methods --//
return {
setInterval : function(op,interval,$scope){
var _intervalID = _intervalUID++;
_intervals[_intervalID] = $timeout(function intervalOperation(){
op($scope || undefined);
_intervals[_intervalID] = $timeout(intervalOperation,interval);
},interval);
return _intervalID;
}, // end setInterval
clearInterval : function(id){
return $timeout.cancel(_intervals[id]);
} // end clearInterval
}; // end return
}]); // end timeSrv
And then in your controller you'd make a call like so:
$scope.getSomethingID = timeSrv.setInterval(function($scope){
[... Do stuff here - Access another service ...]
},10000,$scope);
This will execute the passed function every 10 seconds with the scope of the controller. You can cancel it at anytime by:
timeSrv.clearInterval($scope.getSomethingID);

Subclass factory in AngularJS with member variables

I have tens of AngularJS Factories which have a lot in common. So I'm trying to make a base class and subclass it.
But I've noticed that the subclasses are sharing member variables of the base class.
I've made an example on http://jsbin.com/doxemoza/2/edit, the code is also posted here:
HTML:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body ng-app="demo" ng-controller="MainCtrl">
<p>ChildService: {{value1}}</p>
<p>AnotherChildService : {{value2}}</p>
</body>
</html>
JavaScript:
angular.module('demo', []);
var demo = angular.module('demo').controller('MainCtrl', function ($scope, ChildService, AnotherChildService) {
$scope.value1 = ChildService.getPrivateVar();
$scope.value2 = AnotherChildService.getPrivateVar();
});
var Base = function () {
var Service = {};
Service.privateVar = 0;
Service.setPrivateVar = function (value) {
Service.privateVar = value;
}
Service.getPrivateVar = function () {
return Service.privateVar;
}
return Service;
};
demo.factory('BaseService', Base)
demo.factory('ChildService', function (BaseService) {
var ChildService = Object.create(BaseService);
ChildService.setPrivateVar(1);
return ChildService;
});
demo.factory('AnotherChildService', function (BaseService) {
var AnotherChildService = Object.create(BaseService);
AnotherChildService.setPrivateVar(2);
return AnotherChildService;
});
My expected output is:
ChildService: 1
AnotherChildService : 2
But instead I get:
ChildService: 2
AnotherChildService : 2
I think ChildService and AnotherChildService are sharing the same privateVar, so I got the same value for them.
How should I change the code to make them use different instance of privateVar?
Thanks
I had that same problem, and was solved when a declared my BaseService this way:
demo = angular.module('demo', []);
demo.factory('BaseService', function(){
return {
privateVar: 0,
setPrivateVar: function (value) {
this.privateVar = value;
},
getPrivateVar: function () {
return this.privateVar;
}
}
});
My "child" services are like yours. Everything works very fine.
I use something like this:
angular.module('app')
.controller('ParentController', ['$scope', 'dataService', function ($scope, dataService) {
//controller logic here
}])
.controller('ChildController', ['$scope', '$controller', 'SomeDataService', function ($scope, $controller, SomeDataService) {
//extend your parentcontroller
$scope.init = function () {
//do something
}
angular.extend(this, $controller('ParentController', {
$scope: $scope,
dataService: SomeDataService
}));
}])
.factory('BaseDataService', ['logger', function (logger) {
var privateArray = [];
return {
publicFunction: function(){ return privateArray; },
publicVar: "some var"
}
}])
.factory('SomeDataService', ['BaseDataService', function (dataService) {
var service = angular.extend({}, dataService);
service.privateFunction = function () {
//some code
}
service.privateVar= "some value";
return service;
}]);
I combined all of them in this example.
Hope this helps.
Edit: this uses the mixin pattern described in this post

Categories