2

I have this array of objects :

$scope.posts = [
    { 
        "name" : "name1",
        "age" : 12
    },
    { 
        "name" : "name1",
        "age" : 13
    },
    { 
        "name" : "name1",
        "age" : 14
    }
]

I am ng-repeating this array in my markup as follows:

<div ng-repeat="post in posts">
    <span>{{post.name}}</span>
    <button ng-click="editPost(post)">Click!</button> <!-- passing post as argument here-->
</div>

Controller:

app.controller('PostController', function($scope, postFactory){
    //array is present in controller, just not showing it here
    $scope.editPost = function(post) {
        postFactory.editPost(post).then(function(data) { //factory having an ajax call which gets new data to be assigned to 'post'
            post = angular.copy(data.data)
            console.log(post); //here new data is populated successfully
        })
    }
})

As you can see, when 'editPost' is called, new post data is fetched from an API via a factory and assigned to 'post'(which is the argument to the 'editPost' function). When I console log 'post' after it is assigned the new data, it shows the new data in the console. HOWEVER, in my markup, still the old data is displayed. Any idea why the markup isn't updated? Here's my factory:

app.factory('postFactory', function($http) {
    return {
        editPost : editPost
    }

    function editPost(post) {
        return $http.post('<api>', post).success(function(response) {
            return response.data;
        })
    }
})

EDIT:

I just noticed something. When I changed:

post = angular.copy(data.data)

to

post.name = data.data.name;

the post gets updated. So, I am guessing mass assignment of an object is not getting updated but when I update an individual property, it gets updated in the template.

3
  • Btw the api you have is odd. You resolves data from the (deprecated) success function, and the post itself seems to be nested yet another level in data, meaning the api returns a single post nested in data.data. Commented Oct 20, 2015 at 10:12
  • Whats the alternative for 'success'? 'then'? Commented Oct 20, 2015 at 10:14
  • Yes, then is the way to go, but beware that the first argument represents the whole http response (not only the body), and that a success that resolves response.data is equivalent to a then that resolves...response.data.data Commented Oct 20, 2015 at 10:17

2 Answers 2

2

Short answer: References are "passed by value" to functions.

Long answer:

When you're passing an object to a function, the function will receive a copy of a reference to that object. When you overwrite that reference, the results will only be visible in the scope of that function.

In other words, when you're doing:

post = angular.copy(data.data)

You're only changing the thing that post (inside of editPost) is pointing to. This will have no effect on the outside world/scope (when I say scope, I mean the normal JS function scope, not Angular's scope).

One possible way to make your code work is to pass the $index value to editPost and then modify $scope.posts like this:

$scope.editPost = function(index) {
        postFactory.editPost($scope.posts[index]).then(function(data) { 
            $scope.posts[index]= angular.copy(data.data)
        })
    }

EDIT:

Since my explanation wasn't clear enough I'll try to elaborate.

Imagine that you have an object in memory. Let's call this object objInMem. This object is referenced by vars. In essence, a var is like an arrow, pointing to some place in memory, the place where our object is.

someVar -> objInMem

Of course the value of that reference (the "address" of the object) is also in memory and can be copied by assiging it to other vars.

var otherVar = someVar; // someVar -> objInMem <- otherVar

When you invoke a function that takes an object, JS will copy the value of the reference that you passed and "give" that copy to the function.

function f(obj) {...}
f(someVar); // someVar -> objInMem <- obj (inside function f)

When you try to assign something else to obj (inside of f), it just changes the value of that reference,But the original reference will remain unchanged:

obj = someOtherThing; // obj -> someOtherThingInMem

// someVar -> objInMem (still the same)

I hope this clears things up. It's a bit oversimplified and does not cover simple values (like numbers), but it should explain this specific situation.

Sign up to request clarification or add additional context in comments.

5 Comments

It is actually, passing by reference. If I do post.name = 'something new' just inside my 'editPost' function, the value gets reflected in the template.
Yes, because you're replacing a reference inside of the actual post object by using the dot operator. You can't overwrite the outer reference to post however.
so you are saying when I use dot operator, it updates the actual reference. But if I assign the entire argument to something, it is just updating the copy of that reference in the function? This is confusing. ;)
Yes, I admit that I didn't explain it very clearly, but it is confusing. I'll update the answer.
Thanks, I get your point. But it would be great to update the answer for others.
-1

Most likely angular is not aware of the asynchronous update.

Try to wrap it in a timeout like so:

$timeout(function(){
  post = angular.copy(data.data)
});

1 Comment

not working. I just edited the question with an observation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.