Ben Osborne Ben Osborne - 21 days ago 6
AngularJS Question

Angular - Update Every Property Of An Object After Resource Save

I'm passing an object to a resource, which makes some database updates that could change multiple properties of the object (beyond the changes made in the UI).

If I update the properties individually, the changes are reflected in the HTML. But if I try to update the entire object, the changes are not reflected.

So, this works:

myResource.doSomething(item)
.$promise
.then(function (response) {
item.Color = response.Result.Color;
item.Shape = response.Result.Shape;
});


But this doesn't:

myResource.doSomething(item)
.$promise
.then(function (response) {
item = response.Result;
});


What's the best way to update every property of the object?

In case it helps, here's my resource (posts to an MVC controller which returns JSON of the updated object):

app.factory('myResource', function ($resource) {
return {
doSomething: function (item) {
return $resource('/Something/Do/:Id', { Id: item.Id }).save();
}
};
});`


EDIT: To clarify, the item is part of a collection bound to an ng-repeat directive, e.g.:

<tr ng-repeat="item in items" >
<td>
<a ng-click="doSomething(item)" >Do Something</a>
</td>
</tr>

Answer

The reason this doesn't work:

myResource.doSomething(item)
    .$promise
    .then(function (response) {
        item = response.Result;
    });

Is because what you're essentially doing is overwriting the local variable named item.

I don't know exactly how your items list is filled as you haven't shown the Controller/Component code, but let me just show you how it should work with comments, hopefully you can make sense of it that way :)

angular
  .module("app", [])
  .controller("myController", function($timeout, myResource) {
    var _this = this;
    _this.loadingItems = true;
 
    // Fake loading the items
    $timeout(function() {
      _this.items = [
        { "Id": 1, "Name": "Some Name" },
        { "Id": 2, "Name": "Other Name" },
        { "Id": 3, "Name": "Yet another Name" },
        { "Id": 4, "Name": "Final Name" },
      ];
      _this.loadingItems = false;
    }, 500);
    
    _this.doSomething = function(item) {
        item.sending = true;
       myResource.doSomething(item)
        .then(function(newItem) {
          // Here's where the magic happens
          // First find where the existing items is at in the array
          var existingIndex = _this.items.indexOf(item);
          // Replace the item with the one from the server
          _this.items.splice(existingIndex, 1, newItem);
        });
    }
    
  })
  .factory('myResource', function ($timeout) {
    return {
        doSomething: function (item) {
            // Fake sending something to the server which returns a new Item object
            return $timeout(function() {
               return { Id: item.Id, "Name": "New name returned by server!" };
            }, 250);
        }
    };
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>

<div ng-app="app">

  <!-- Notice the 'as vm' on the controller to put the controller on the scope as 'vm' -->
  <div ng-controller="myController as vm">
    <p ng-if="vm.loadingItems">Busy loading items</p>
    
    <table ng-if="!vm.loadingItems">
      <tr ng-repeat="item in vm.items track by item.Id">
        <td>
          {{item.Name}}
        </td>
        <td>
          <a href="javascript:void(0);" ng-if="!item.sending" ng-click="vm.doSomething(item)">Do Something</a>
          <span ng-if="item.sending">Working...<span>
        </td>
      </tr>
    </table>

  </div>

</div>

I really hope this helps! Otherwise I spent way too much time typing this :)