angularjs XXXService.XXXfunction is not a function - javascript

It is my first try writing up a service in AngularJS. I have a service, in which I have a function. When I call this service from my controller, it says XXXService.XXXfunction is not a function.
My Javascript is like,
var sharecycle0_angularfire = angular.module("sharecycle0_angularfire", []);
sharecycle0_angularfire.service("baseHttp", function($http, $q){
self.getFire = function(){
var deferred = $q.defer();
$http({
method: GET,
url: "https://boiling-inferno-1234.firebaseio.com/Cat"
}).then(function(response){
deferred.resolve(response);
},function(response){
deferred.reject(resposne);
});
return deferred.promise;
};
});
sharecycle0_angularfire.controller("angularfirecontroller", function($scope, baseHttp, $window){
$scope.fireClick = function(){
baseHttp.getFire()
.then(
function(response)
{
$("<div/>").text(response).appendTo($("#successData"));
},
function(response)
{
$window.alert("error: "+response);
}
);
};
});
My HTML is like,
<body ng-controller="angularfirecontroller">
<button id="fireClickButton" ng-click="fireClick()">fireClick</button>
<div id="successData"/>
</body>
I expect the service code to run upon my button being hit.

You have some issues here, you are not attaching the function getFire to the service instance, instead you are attaching it to the global variable self. self global variable is generally an alias to window object in many browsers so you are attaching the function to the window and not to the controller instance. Since http already returns a promise you do not need to create a deferred object there, instead just return the result of $http. Apart from that GET needs to be a string value, you are trying to use an undefined variable GET and not "GET". Many a times placing a debug log in relevant areas of the code will help you diagnose the issue.
All you would need is:
sharecycle0_angularfire.service("baseHttp", function($http, $q){
this.getFire = function(){
return $http({
method: 'GET',
url: "https://boiling-inferno-1234.firebaseio.com/Cat"
});
}
});
if you intended to cache this then you should create a local variable and assign this to that.
i.e
sharecycle0_angularfire.service("baseHttp", function($http, $q){
var _this = this; //Or var self = this
_this.getFire = function(){
return $http({
method: 'GET',
url: "https://boiling-inferno-1234.firebaseio.com/Cat"
});
}
});

Related

How to update $scope variables through a function

I'm currently working on grabbing some data from an endpoint and then updating a variable called $scope.response. I'm not quite sure how to update this variable and render it on screen.
So what happens in the code below:
I get the query string from an iframe's src attribute, and then post it to my endpoint, where I get a particular response called data. I'd like to update $scope.response with this object, and then render it on the view using {{response}}.
Could someone show me how I could do this?
app.controller('MainCtrl', function($scope) {
$scope.response;
API = {};
API.endpoint = 'https://some/endpoint/';
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
function doAjax(callback) {
var q = getParameterByName('QUERY');
jQuery.ajax({
url: API.endpoint + "script.php",
method: "POST",
data: { q: q },
dataType: "json",
success: function (data) {
callback(data);
$scope.response = data;
}
});
}
});
Angular doesn't magically know when a property on an object changes, it would have to keep re-checking all objects all the time to catch such changes. Angular just makes it look like it notices such changes whenever you use any Angular services or events, since those trigger a digest cycle. At the end of a digest cycle, Angular checks objects it knows about for changes and propagates those changes (e.g. updates views etc.).
When you use jQuery, that's "outside" of what Angular knows about. Primarily you should not use jQuery, but Angular's $http service to make any network requests, since Angular will then properly cycle its digestive system.*
* Pun totally intended
If you have to use some non-Angular system (and again, you really don't have to here, at all), then you need to trigger another digest cycle. The best way to do that is with the $timeout service:
app.controller('MainCtrl', function($scope, $timeout) {
...
success: function (data) {
callback(data);
$timeout(() => $scope.response = data);
}
...
});
Why do you use jQuery in Angular?. If you choose Angular, you should be you $http in angular. Remove function doAjax and replace it to $http. You can read doc in here https://docs.angularjs.org/api/ng/service/$http

AngularJS ng-init does not work

When i go to list.html right after loading the page it doesnt show me the list. I need to go to form.html first and then back to list.html and then it gives me the list. When i had $http functions in controller it worked as i wanted but now that they are in service they dont work like before.
app.js
app.service("dataservice", function($http){
var list = [{}];
this.get = function(){
$http.get("harjoitus22/backend.php").success(function(response){
list = response;
});
};
this.save = function(newcontact){
$http({
method: 'post',
url: 'harjoitus22/backend.php',
data: newcontact,
header: 'Access-Control-Allow-Origin: *'
}).success(function(response){
list = response;
});
};
this.give = function(){
return list;
};
});
app.controller("mainController", function($scope, $location, dataservice){
$scope.save = function(){
dataservice.save($scope.newcontact);
$scope.newcontact = {};
$location.path("/list");
$scope.list = dataservice.give();
};
$scope.get = function(){
dataservice.get();
$scope.list = dataservice.give();
};
});
list.html
<tbody ng-init="get()">
<tr ng-repeat="object in list">
<td>{{object.nimi}}</td>
<td>{{object.email}}</td>
<td>{{object.ruoka}}</td>
<td>{{object.sauna}}</td>
</tr>
</tbody>
$http call works asynchronously, when you call it, It will not give you response on next line execution. To keep eye on that ajax you should take use of promise return by $http method.
For achieving the same you need to return promise object from the service get method ($http methods does return's promise object, which helps you to execute your code by putting up in its .then function's).
Service
this.get = function(){
return $http.get("harjoitus22/backend.php").then(function(res){
list = res.data;
});
};
Controller
dataservice.get().then(function(){
$scope.list = dataservice.give();
});

Change $http parameter in nested ng-repeat

I have two json files being requested for two differents angularjs services.
/people.json
{user-name, user-id}
/task.json
{name-task, task-id, responsible-id}
Where responsible-id = user-id.
I can pass an ID parameter to the task json to request all the task for the user with that ID: /task.json?ID=12
I'm trying to create a nested ng-repeat, the first one to get all the users in /people.json, the second to get all the task for each user in the loop but i ended up with something like this: http://i.imgur.com/xgG0K7i.png
The first ng-repeat shows correctly the different users, but the second show the same tasks of the first user to the others users in the list.
¿How can i change the parameter to update correctly for the nested ng-repeat?
My Services:
.service('TeamworkPeopleSrvc', function($http, $q){
var deferred = $q.defer();
var TeamworkPeopleSrvc = this;
TeamworkPeopleSrvc.getPeople = function(){
$http.defaults.headers.common['Authorization'] = 'Basic ' + window.btoa('mycustomapikey' + ':' + 'X');
$http({
method: 'GET',
url: 'http://projects.com/people.json',
params: { 'pageSize':'5'},
})
.then(function(response) {
deferred.resolve(response);
});
return deferred.promise;
};
return TeamworkPeopleSrvc;
})
.service('TeamworkTasksSrvc', function($http, $q){
var deferred = $q.defer();
var TeamworkTasksSrvc = this;
TeamworkTasksSrvc.getTasks = function(id){
$http.defaults.headers.common['Authorization'] = 'Basic ' + window.btoa('mycustomapikey' + ':' + 'X');
$http({
method: 'GET',
url: 'http://projects.com/tasks.json' ,
params: { 'id':id, 'getSubTasks':'no', 'pageSize':'10'},
})
.then(function(response) {
deferred.resolve(response);
});
return deferred.promise;
};
return TeamworkTasksSrvc;
})
My Controller
.controller('PeopleCtrl', function ($scope, TeamworkPeopleSrvc, TeamworkTasksSrvc) {
$scope.init = function(){
$scope.peopleObjects();
};
$scope.taskObjects = function(id){
TeamworkTasksSrvc.getTasks(id)
.then(function(response){
$scope.userTasklist = response.data['todo-items'];
});
};
$scope.peopleObjects = function(){
TeamworkPeopleSrvc.getPeople()
.then(function(response){
$scope.userlist = response.data.people;
});
};
$scope.init();
});
and try to init the tasks with the updated user id in the template
<div ng-controller="PeopleCtrl">
<div ng-repeat="person in userlist">
<h3>{{person['id']}} | {{person['first-name']}} {{person['last-name']}}</h3>
<div ng-init="taskObjects(person['id'])">
<div ng-repeat="task in userTasklist">
{{task['responsible-party-id']}} - {{task['content']}}
</div>
</div>
</div>
</div>
Best Regards.
The problem is in your controller code. You are using the same $scope for both loops, which means that each time you call taskObjects(), you are overwritting $scope.userTasklist with a new task list. You should either establish a new scope for each instance within the loop, or you should make $scope.userTasklist an object with properties matching the person.id.
In your controller change:
$scope.taskObjects = function(id){
TeamworkTasksSrvc.getTasks(id)
.then(function(response){
if(!$scope.userTasklist) {
$scope.userTasklist = {};
}
$scope.userTasklist[id] = response.data['todo-items'];
});
};
And then in your view use:
<div ng-controller="PeopleCtrl">
<div ng-repeat="person in userlist">
<h3>{{person['id']}} | {{person['first-name']}} {{person['last-name']}}</h3>
<div ng-init="taskObjects(person['id'])">
<div ng-repeat="task in userTasklist['id']">
{{task['responsible-party-id']}} - {{task['content']}}
</div>
</div>
</div>
</div>
Defective $q.defer Anti-Pattern1
.service('TeamworkPeopleSrvc', function($http, $q){
var deferred = $q.defer();
var TeamworkPeopleSrvc = this;
TeamworkPeopleSrvc.getPeople = function(){
$http.defaults.headers.common['Authorization'] = 'Basic ' + window.btoa('mycustomapikey' + ':' + 'X');
$http({
method: 'GET',
url: 'http://projects.com/people.json',
params: { 'pageSize':'5'},
})
.then(function(response) {
deferred.resolve(response);
});
return deferred.promise;
};
return TeamworkPeopleSrvc;
})
Both services in the code use a defective $q.defer Anti-Pattern. With the deferred object created outside the .getPeople function, the promise will only be resolved once with the first resolve. Subsequent resolves are ignored and the promise always returns the first value. In addition if the XHR has an error, the error information is lost. With errors, the promise never resolves or resolves only with the first fulfilled XHR.
Implemented Without $q.defer
.service('TeamworkPeopleSrvc', function($http, $q){
var TeamworkPeopleSrvc = this;
TeamworkPeopleSrvc.getPeople = function(){
var authHeader = { Authorization: 'Basic ' +
window.btoa('mycustomapikey' + ':' + 'X')
};
var httpPromise = ($http({
method: 'GET',
url: 'http://projects.com/people.json',
headers: authHeader,
params: { 'pageSize':'5'},
})
);
return httpPromise;
};
return TeamworkPeopleSrvc;
});
To implement the service correctly, simply return the promise returned by the $http service. A new promise will be created each time the $http function is invoked and error information will be properly retained.

How do I create a angular js service that makes a http request only once

I have the following angularjs service
var assetLookUpTransformService = function($http, $q) {
this.data = null;
var self = this;
this.doStuff = function() {
var defer = $q.defer();
if (self.data) {
defer.resolve(self.data[0].DisplayName);
} else {
$http({
url: '../AssetLookup/GetAssetLookUp',
method: "GET"
}).success(function(response) {
self.data = response;
defer.resolve(self.data[0].DisplayName);
});
}
return defer.promise;
}
}
It works ok, in that once the $http request has returned all subsequent calls to "doStuff" return data from data rather than make a new request.
Then problem I have is that I am making a bunch of calls right after each other on page load. And what is happening is that the first call to doStuff will make an $http request, but also any calls to dostuff that happen before the first http request returns will also make a $http request.
Is there a way to make the calls to doStuff "wait" for the outstanding $http request to return before making there own.
Or is there a way to ensure the $http request only ever happens once?
Cache the promise, not the data. The data is encapsulated in the promise and will always be returned to whatever requests it.
var assetLookUpTransformService = function($http, $q) {
var self = this;
this.doStuff = function() {
if (!self.deferred) {
self.deferred = $q.defer();
$http({
url: '../AssetLookup/GetAssetLookUp',
method: "GET"
}).success(function(response) {
self.deferred.resolve(response[0].DisplayName);
});
}
return self.deferred.promise;
}
}
Why don't you just make each of those calls after the first call to doStuff?
Example:
this.doStuff = function(afterRequests) {
var defer = $q.defer();
if (self.data) {
defer.resolve(self.data[0].DisplayName);
} else {
$http({
url: '../AssetLookup/GetAssetLookUp',
method: "GET"
}).success(function(response) {
self.data = response;
defer.resolve(self.data[0].DisplayName);
//make each request in here.
afterRequests.forEach(function(request) {
$http(request).success(function(response) {
//use self.data here
});
});
});
}
return defer.promise;
}
If for some reason you have to make doStuff at the same time (e.g. it's very long running), then you could use $rootScope.$broadcast to target your controllers which may need to render the data.

How do I set a variable within an Angular JS service to the response data from an $http call within an AngularJS service?

I have the following service which I eventually want to cache. However, I can't seem to figure out how set a variable within the service to the response data from the REST call using $http in Angular.
routerApp.service('activityService', function($http) {
delete $http.defaults.headers.common['X-Requested-With'];
this.temp = null;
this.setTemp(x) {
this.temp = x;
}
this.getActivities = function(x) {
var promise = $http({
method: 'GET',
url: 'my url is here...'
}).then(function(response) {
//setTemp(response.data); //definitely wont work
return response.data;
});
//how to set temp to response.data???
setTemp(promise.data); //doesn't work -
};
});
I don't know JS (or angular for that matter) very well. What is the best way to do this?
There is no need to cache the angular service, it's guaranteed to be a singleton.
If you mean to cache the response data, you will create a cache object in your service.
Now about the main question. In this code there is misuse of promises not angular services. Promises are asynchronous, meaning that a callback provided to .then() will be executed some time later, when request is finished. Besides .then returns another promise, which you should return from activityService.getActivities method.
this.getActivities = function(x) {
var promise = $http({
method: 'GET',
url: 'my url is here...'
}).then(function(response) {
setTemp(response.data); //meaning that setTemp somehow modifies it's argument
return response.data;
});
return promise;
};
Then in one of your controllers you will use this service's method attaching .then to it's return value.
.controller('someController', function (activityService) {
activityService.getActivities()
.then(function (data) {
doSomethingWithResponse(data);
}
});

Categories

Resources