0

(Note: This is AngularJs but should be a "standard" Javascript-Issue)

I use the following method to read the content of a json-file inside an angular controller:

$http.post(apiUrl + 'data.php?select', {directory: "customers", file: "john"})
.success(function (data) {
   $scope.customers= data;
})

(The php - function simply returns a (valid) JSon-Array).

This works as expected; The content is stored inside $scope.customers

Because this kind of actions is needed in different places I decided to write a service for this:

myApp.factory('util', function ($http) {
    return {
        getFile:function(directory, file, target) {
            $http.post(apiUrl + 'data.php?select', {directory: directory, file: file})
                .success(function (data) {
                    target = data;
                });
        }
    };

And then call this method the follwing way:

util.getFile("customers","john",$scope.customers);

But this does not work ($scope.customers remains empty).

After digging into SO I understand that this cannot work this way, because arrays are not passed by-reference but by-value.

But is there another way to achieve what I want to do?

Ole Albers
  • 8,715
  • 10
  • 73
  • 166
  • here you change reference, so inside your function _getFile_ target changed, but outside not. Just return this `$http` and use it `util.getFile("customers","john").then(function(data){ $scope.customers = data;})` and in function instead `target = data;` use `return data` – Grundy Nov 13 '15 at 08:55
  • What happens if you do `$scope.customers = util.getFile("customers","john");` and as well change your inner assigning to `return data;` – Asons Nov 13 '15 at 09:02
  • $http.post is asynchronous. So if I return inside the "success" function it will NOT return the content to the caller of the "getFile"-function. Being asynchronous (and not being able to just return the response) is kind-of the main issue here. – Ole Albers Nov 13 '15 at 09:06
  • 1
    @OleAlbers, `$http` functions return promise, so if you return it from `getFile` you can use it – Grundy Nov 13 '15 at 09:09
  • It will return whatever is in the last of your factory's success call, and you could get it by using the `data` argument in your success function. Consider putting a return after your HTTP call: `.success(function (data) { return data; });` – Ralph Sto. Domingo Nov 13 '15 at 09:10
  • @Grundy Gosh. Should have known that earlier :) Thanks. Will try that. If you want to, make this an answer and I will accept it. – Ole Albers Nov 13 '15 at 09:14
  • worked. As an addition to @Grundy ' suggestion I also had to return $http.post() and to access the content I had to use "$scope.customers=data.data" – Ole Albers Nov 13 '15 at 09:20
  • @xyz No. My original intention was NOT to RETURN a value. The best solution seems to be to use a return, but that was not the intention of this question. – Ole Albers Nov 13 '15 at 12:06
  • @OleAlbers, retracted my close vote. But, I thought [How to return the response from an async call](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) would be the solution that you are looking for. –  Nov 13 '15 at 12:16

1 Answers1

2

I think the problem is that target is now a local variable because javascript is always pass by value, so in this case target refers to the same object like $scope.customers. But overwriting target will not overwrite $scope.customers, then target will just point to another object.

You should set the variable using .then(result) like this:

myApp.factory('util', function ($http) {
    return {
        getFile:function(directory, file, target) {
            return $http.post(apiUrl + 'data.php?select', {directory: directory, file: file})
        }
    };

and call it with

util.getFile("customers","john").then(function successCallback(response) {
  // this callback will be called asynchronously
  // when the response is available
  $scope.customers = response.data;
}, function errorCallback(response) {
  // called asynchronously if an error occurs
  // or server returns response with an error status.
  console.log("Error getting customers")
});

This also makes it clear that util.getFile is an asynchronous function and you can place code here that gets executed when the result is available.

Note: Another alternative to stay with you approach would be to overwrite a property of the object (but I prefer the approach above):

myApp.factory('util', function ($http) {
    return {
        getFile:function(directory, file, target) {
            $http.post(apiUrl + 'data.php?select', {directory: directory, file: file})
                .success(function (result) {
                    target.data = result.data;
                });
        }
    };
Carl Ambroselli
  • 616
  • 6
  • 18
  • but `target` **IS** pointer to original object – Grundy Nov 13 '15 at 09:24
  • The second approach does not really work, because i would have to use "data" in every $scope I want to use this method with (or change each array to an object). Nonetheless. The first code works :) – Ole Albers Nov 13 '15 at 09:25
  • I think it is always pass by value, so in this case target refers to the same object like $scope.customers. But overwriting target will not overwrite $scope.customers, then target will just link to another object. Or am I wrong about this? – Carl Ambroselli Nov 13 '15 at 09:27
  • Just a remark (not criticism): "put code into then that you want tor run when the result is available". There is no code, because it's angular, which "magicly" displays the data when its changed :) – Ole Albers Nov 13 '15 at 09:28
  • but you not say: _problem is that target is now a local variable and not a pointer to the original object that you specified as a parameter_ – Grundy Nov 13 '15 at 09:28
  • 1
    @Ole Albers yes that would only work if you pass and object that has attributes like a container. If that's not the case you need to go with the first approach. (You could pass $scope and tell that it should overwrite $scope['customers'] but thats a very ugly way.) – Carl Ambroselli Nov 13 '15 at 09:29
  • 1
    @Grundy ok thanks for pointing this out, I updated the text in the post! – Carl Ambroselli Nov 13 '15 at 09:33