I m trying to use a controller callback function inside my service, when it successes an $http post request. Here's my code, the exact description is a bit below.
my controller :
function UserAccountCtrl (UserService, $rootScope, listUsers) {
$rootScope.title = 'Comptes utilisateurs';
this.users = listUsers.data;
this.isShown = false;
this.isModification = false;
this.deleteEntry = function(entry){
this.users.splice(this.users.indexOf(entry), 1);
UserService.delete(entry);
};
this.show = function(){
this.isShown = true;
};
this.hide = function(){
this.isShown = false;
};
this.save = function(){
var success = function(data){
this.users.push(data);
};
var err = function(data){
alert(data);
};
UserService.post(this.user, success, err);
this.hide();
};
}
My service function :
UserService.post = function (data,succ,err) {
$http({
url: __ADRS_SRV__ + "user",
method: "POST",
data:data,
isArray: true
}).success(function(data){
succ(data);
}).error(function(error){
err(error);
});
}
The functionnement is simple : when I post a new user, the WebService inserts it in mongo, and returns the fully new object generated. I can get the new object from console.log or with an alert, and it works fine.
But I cant push the new item in my array. I have the error :
Uncaught TypeError: undefined is not a function
At the exact line where I need to push the new item.
So, does anybody have an idea ?
Thanks for advance
this in this.users.push(data); is not the same this as outside of the function and therefore does not have a users array to push the new data to. (See MDN this for more info on this in javascript)
I would actually not use this at all and attach everything to the $scope object as required. This would get around your issue as $scope will be the same no matter what the context.
function UserAccountCtrl ($scope, UserService, $rootScope, listUsers) {
$scope.users = listUsers.data;
$scope.save = function(){
var success = function(data){
$scope.users.push(data);
};
var err = function(data){
alert(data);
};
UserService.post($scope.user, success, err);
$scope.hide();
};
// do the same with other functions
}
See 'this' vs $scope in AngularJS controllers for more details.
Can you try if this works?
this.save = function(){
var self = this;
var success = function(data){
self.users.push(data);
};
var err = function(data){
alert(data);
};
UserService.post(this.user, success, err);
this.hide();
};
Related
I have a problem with my Angular Service. I have two controllers and one service. Basically the first controller gets data via AJAX call and store that data on the service. The second controller then access that data via service. I have successfully passed the data from the 1st controller to the service however, when I access the data from the 2nd controller it returns nothing.
I have one view with 2 controllers by the way.
Thanks
Service
app.service('SharedDataService', function () {
// Holds subtask that will be passed to other controllers
// if SharedDataService is invoke.
var _subTask = {};
return {
subTask : _subTask
};
});
Controller 1
app.controller('MainCategoryController',function($scope,$http,SharedDataService){
$scope.loadSubtask = function(m_uid){
$http({
method: 'POST',
url: $locationProvider + 'query_stasks',
data: {
m_uid: m_uid
}
}).then(function successCallback(response) {
SharedDataService.subTask = response.data;
},function errorCallback(response){
});
}
}
Controller 2
app.controller('SubTaskController',function($scope,$http,$location,$rootScope,SharedDataService){
$scope.$watch('SharedDataService.subTask', function(newValue,oldValue){
console.log("ni sud');");
if (newValue !== oldValue) {
$scope.subTasks = newValue;
}
return SharedDataService.subTask;
});
}
Because SharedDataService is not on $scope, the first argument of the $watch method needs to be a watchExpression function instead of an Angular expression string.
app.controller('SubTaskController',function($scope,$http,$location,$rootScope,SharedDataService){
$scope.$watch(
function watchExpression() {return SharedDataService.subTask},
function listener (newValue,oldValue){
console.log("ni sud");
if (newValue !== oldValue) {
$scope.subTasks = newValue;
}
}
});
});
For more information, see AngularJS $rootScope.scope API Reference - $watch.
May be you should save the value use object in service.In my project, i always do like this:
app.service('SharedDataService', function () {
var service = {};
service._subTask = '';
service.toogle_sub_task = function(v){
if(v){
service._subTask = v;
}
return service._subTask;
}
return service;
});
Then, in your controller. You should call service.toogle_sub_task to set and get value. Just give it a try.Best wishes.
you have to write a method to store the data into service and return the data from service
app.factory('SharedDataService', function () {
// Holds subtask that will be passed to other controllers
// if SharedDataService is invoke.
var _subTask = [];
function setSubTask = function(data){
_subTask = data;
};
return {
subTask : _subTask,
setSubTask:setSubTask
};
});
and in controller call
SharedDataService.setSubTask(response.data);
to set the data...
try it
app.service('SharedDataService', function () {
this._subTask ={};
});
// keep code same 1st controller
app.controller('MainCategoryController',function($scope,$http,SharedDataService){
$scope.loadSubtask = function(m_uid){
$http({
method: 'POST',
url: $locationProvider + 'query_stasks',
data: {
m_uid: m_uid
}
}).then(function successCallback(response) {
SharedDataService._subTask = response.data;
},function errorCallback(response){
});
}
}
// 2nd controller
app.controller('SubTaskController',function($scope,$http,$location,$rootScope,SharedDataService){
console.log(SharedDataService._subTask );
});
The best practice is to make any $http/resource calls in services and not in controller or directives directly. This makes the code more module and less interlinked.
You should ideally have
app.factory('SharedDataService', function ($http) {
var subtasks = [];
var saveSubtasks = function(sub){
subtasks = sub;
console.log(subtasks)
}
var tasks = {
loadSubtasks : function(m_uid){
$http({
method: 'POST',
url: $locationProvider + 'query_stasks',
data: {
m_uid: m_uid
}
}).then(function(data){
saveSubtasks(data);
});
},
getSubtasks : function(){
return subtasks;
}
}
return tasks;
});
and use it like
app.controller('SharedDataService',function($scope,SharedDataService){
$scope.load = function(val){
SharedDataService.loadSubtasks(val);
}
});
app.controller('SubTaskController',function($scope,SharedDataService){
$scope.get = function(){
$scope.subtasks = SharedDataService.getSubtasks();
console.log($scope.subtasks);
}
});
I'm looking to get what I thought would be a simple script to run an AJAX call and keep various values stored to an object, but I cannot get the globals to remain consistent the way I would expect.
I've gone around in circles trying what I think is everything. As soon as I put the AJAX call in I can't get it to play nicely with the global variables. The process value is always false that way and the content never loads in.
ExtContent = function(){
var self = this;
this.init = function() {
self.output = null;
self.process = false;
};
this.request = function(url){
$.ajax({
type : 'GET',
timeout : 10000,
dataType : 'html',
url : url,
passself : self,
success : function(response){
this.passself.setoutput(response);
},
error : function(req,response){
if(response==='error'){
self.error=req.statusText;
}
}
});
};
this.setoutput = function(data){
this.output = data;
this.process = true;
};
this.returnprocess = function(){
return self.process;
};
this.returnoutput = function(){
return self.output;
};
self.init();
};
<div id="holder"></div>
loadcontent = new ExtContent();
loadcontent.request('/test.html');
if(loadcontent.returnprocess()){
$('#holder').before(loadcontent.returnoutput());
}else{
$('#holder').before('FAILED');
}
I can't get the process to be true and the content to be stored in output.
Thanks.
Despite wrapping everything as a class/object, the jQuery $.ajax call is still an asynchronous operation. basically "You have ordered a pizza, then try to eat it before it arrives".
i.e. this orders it:
loadcontent.request('/test.html');
and this tries to eat it immediately:
if(loadcontent.returnprocess()){
The call to setoutput (i.e. the "Pizza delivery") happens long after these operations complete.
You need to add event handler properties to your class, or use deferreds+promises to wait for the data to arrive.
To use promises, just return the $.ajax result from request:
this.request = function(url){
return $.ajax({
type : 'GET',
timeout : 10000,
dataType : 'html',
url : url,
passself : self,
success : function(response){
this.passself.setoutput(response);
},
error : function(req,response){
if(response==='error'){
self.error=req.statusText;
}
}
});
};
and use it like this:
loadcontent.request('/test.html').done(function(){
if(loadcontent.returnprocess()){
$('#holder').before(loadcontent.returnoutput());
}else{
$('#holder').before('FAILED');
}
});
Or if you setup the return values correctly inside request:
loadcontent.request('/test.html').done(function(){
$('#holder').before(loadcontent.returnoutput();
}).fail(function(){
$('#holder').before('FAILED');
});
Maybe this can help you
this.setoutput = function(data){
// 'this' here, is refering 'setoutput' function, not ExtContent,
// so ExtContent.process != ExtContent.setoutput.process
// this.output = data;
// this.process = true;
self.output = data;
self.process = true;
};
I m actually facing a problem with javascript in general. In fact, I need to update a list after calling a callback in two different files.
This is my description of the callback :
this.modify = function(){
var self = this;
var success = function(){
self.user = self.userEdit;
};
var err = function(data){
alert(data);
};
UserService.put(this.userEdit, success, err);
}
}
And this is the function which calls the callback :
UserService.put = function (data, succ, err) {
var user = {login:data.login,nom:data.nom,prenom:data.prenom,password:data.password};
$http({
url: __ADRS_SRV__ + "user/"+data._id,
method: "PUT",
data:user,
isArray: true
}).success(function(data){
succ();
}).error(function(error){
err(error);
});
}
In fact,
var success = function(){
self.user = self.userEdit;
};
doesn't seem to work properly, when I log self.user in the callback call, I got an undefined...
Do you have an idea to bypass this ?
Thanks for advance
You have to remember the this as self before declaring the success function:
var self = this;
var success = function(){
self.user = self.userEdit;
};
Or an alternative would be just using this, but bind the function with this variable:
var success = function() {
this.user = this.userEdit;
}.bind(this);
Hope this helps.
I'm trying to create a controller that gets data from Google app engine and allows me to display it on a page. The problem seems to be that the data (resp) can be accessed locally, but I can't seem to access it outside of the function. I am able to do so if I simply use javascript (...document.getElementById('getListingsResult').innerHTML = result;...), but if I invoke $scope for Angular, I can't access it any longer. Does anyone have any idea of how I can fix it while retaining the same structure to load and call gapi? Heres' my code:
(edit: added $scope.loadData, but problem persists)
phonecatControllers.controller('datastoreTestCtrl', ['$scope',
function($scope) {
$scope.data;
$scope.loadData = function() {
var ROOT = 'https://my_team.appspot.com/_ah/api';
gapi.client.load('listingserviceapi', 'v1', function(){
console.log("reached step 1");
var request = gapi.client.listingserviceapi.getListings();
request.execute(function (resp){
if (!resp.code) {
// console.debug(resp);
console.log('loaded! :)');//returns loaded
resp.items = resp.items || [];
$scope.data = resp.items;
console.log($scope.data); //returns an array of data
}
};
} , ROOT );};
$scope.loadData;
console.log($scope.data); //returns [object, object] which is incorrect
}]);
It should work using promise. Also, there is a missing parenthesis for request.execute function in your code.
Check the below code (untested):
phonecatControllers.controller('datastoreTestCtrl', ['$scope', '$q',
function ($scope, $q) {
$scope.data = null;
$scope.loadData = function () {
var deferred = $q.defer(),
ROOT = 'https://my_team.appspot.com/_ah/api';
gapi.client.load('listingserviceapi', 'v1', function () {
console.log("reached step 1");
var request = gapi.client.listingserviceapi.getListings();
request.execute(function (resp) {
if (!resp.code) {
// console.debug(resp);
console.log('loaded! :)'); //returns loaded
resp.items = resp.items || [];
//$scope.data = resp.items;
//console.log($scope.data); //returns an array of data
deferred.resolve(resp.items);
}
}); //---missing parenthesis here
}, ROOT);
return deferred.promise;
};
$scope.loadData().then(function (data) {
$scope.data = data;
console.log($scope.data); //returns [object, object] which is incorrect
});
}]);
That is because you are doing asynchronous call. When you trying to access $scope.data from outside of your callback your request is not finished yet it is still in process. You have to make sure that your request is done.
I think I need (but I'm not sure) to use a promise in my Angularjs app. This following code is within my controller and it calls a .service (not a .factory if that's relevant?) called processString.
//complete this first
var item = MagicBoxService.processString(str);
//and then do this
$scope.task.items.push({ content_type: item.content_type, provider: item.provider, front: item.front, data: item.data });
$scope.save = true;
The service needs to communicate with a 3rd party API (as well as my own) to get the data. This happens very quickly but the item variable is empty when the next part of the code is executed.
I've tried a $timeout on the API call but this doesn't seem to work so I thought maybe a promise is what I need to use so I've tried the following:
var item = MagicBoxService.processString(str).then(function() {
$scope.task.items.push({ content_type: item.content_type, provider: item.provider, front: item.front, data: item.data });
$scope.save = true;
})
but this gives me undefined is not a function. Any advice/code would be much appreciated.
EDIT
Here is an edited version of my .service.
this.processString = function(str) {
...
oEmbedService.query({url: str}, function(response) {
item.content_type = "image";
item.provider = "Flickr";
item.data = response.content.url;
item.front = $sce.trustAsResourceUrl(item.data);
})
...
return item
};
if your processString() function looks like this:
function(str) {
var promiseManager = $q.defer();
...
oEmbedService.query({url: str}, function(response) {
var item = {};
item.content_type = "image";
item.provider = "Flickr";
item.data = response.content.url;
item.front = $sce.trustAsResourceUrl(item.data);
promiseManager.resolve(item);
})
...
return promiseManager.promise;
};
Then you can call it like this:
MagicBoxService.processString(str).then(function(item) {
$scope.task.items.push({ content_type: item.content_type, provider: item.provider, front: item.front, data: item.data });
$scope.save = true;
})