将新对象“附加”到 ng 重复项

"Attach" a new object to a ng-repeated item

本文关键字:ng 附加 新对象 对象      更新时间:2023-09-26

我有一个items循环,需要通过API动态替换。问题在于,外部范围在替换原始对象时似乎失去了对原始对象的引用。如何使外部作用域注意到数组中某个项的值更改?

(function(ng) {
  'use strict';
  ng.module('app', [])
    .controller('ParentController', ['$scope',
      function($scope) {
        $scope.items = [{
          name: "Foo",
          number: 0
        }, {
          name: "Bar",
          number: 1
        }, {
          name: "Baz",
          number: 1
        }];
      }
    ])
    .controller('ItemController', ['$scope',
      function($scope) {
        // fake code, would get from API normally
        var getFromApi = function() {
          return {
            name: $scope.item.name,
            number: $scope.item.number + 1
          };
        }
        $scope.incr = function() {
          $scope.item = getFromApi();
        }
      }
    ]);
})(window.angular);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html>
<body ng-app="app">
  <div ng-controller="ParentController">
    <p>When you click one of the below buttons</p>
    <ul>
      <li ng-repeat="item in items" ng-controller="ItemController">
        {{item.name}} ({{item.number}})
        <button ng-click="incr()">+</button>
      </li>
    </ul>
    <p>This should update</p>
    <p>{{items}}</p>
    <p>...but it doesn't</p>
  </div>
</body>
</html>

您需要知道要更改其属性的项的,然后在集合中更改此项。

要声明密钥,您可以使用ng-repeat="(key, item) in items" .然后,该密钥将作为$scope.keyItemController中提供。

接下来,由于ItemController继承了$scope ParentController,您可以直接访问ItemController中的$scope.items

项目控制器.js:

$scope.incr = function() {
  $scope.items[$scope.key] = getFromApi();
}

仅当 ItemController 是父控制器的子级或继承其作用域时,这才有可能。

您可以看到脚本像这样运行:

(function(ng) {
  'use strict';
  ng.module('app', [])
    .controller('ParentController', ['$scope',
      function($scope) {
        $scope.items = [{
          name: "Foo",
          number: 0
        }, {
          name: "Bar",
          number: 1
        }, {
          name: "Baz",
          number: 1
        }];
      }
    ])
    .controller('ItemController', ['$scope',
      function($scope) {
        // fake code, would get from API normally
        var getFromApi = function() {
          return {
            name: $scope.item.name,
            number: $scope.item.number + 1
          };
        }
        $scope.incr = function() {
          $scope.items[$scope.key] = getFromApi();
        }
      }
    ]);
})(window.angular);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html>
<body ng-app="app">
  <div ng-controller="ParentController">
    <p>When you click one of the below buttons</p>
    <ul>
      <li ng-repeat="(key, item) in items" ng-controller="ItemController">
        {{item.name}} ({{item.number}})
        <button ng-click="incr()">+</button>
      </li>
    </ul>
    <p>This updates</p>
    <p>{{items}}</p>
    <p>...and it does!</p>
  </div>
</body>
</html>

您可以使用父数组中项的索引来替换它

(function(ng) {
  'use strict';
  ng.module('app', [])
    .controller('ParentController', ['$scope',
      function($scope) {
        $scope.items = [{
          name: "Foo",
          number: 0
        }, {
          name: "Bar",
          number: 1
        }, {
          name: "Baz",
          number: 1
        }];
      }
    ])
    .controller('ItemController', ['$scope',
      function($scope) {
        // fake code, would get from API normally
        var getFromApi = function() {
          return {
            name: $scope.item.name,
            number: $scope.item.number + 1
          };
        }
        $scope.incr = function() {
          $scope.$parent.items[$scope.$index] = getFromApi();
        }
      }
    ]);
})(window.angular);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html>
<body ng-app="app">
  <div ng-controller="ParentController">
    <p>When you click one of the below buttons</p>
    <ul>
      <li ng-repeat="item in items track by $index" ng-controller="ItemController">
        {{item.name}} ({{item.number}}) {{$index}}
        <button ng-click="incr()">+</button>
      </li>
    </ul>
    <p>This should update</p>
    <p>{{items}}</p>
    <p>...but it doesn't</p>
  </div>
</body>
</html>

另一个(hacky)选项是简单地遍历新对象,分配旧对象的变量:

var newItem = getFromApi();
for (var key in newItem) {
  $scope.item[key] = newItem[key];
}

(function(ng) {
  'use strict';
  ng.module('app', [])
    .controller('ParentController', ['$scope',
      function($scope) {
        $scope.items = [{
          name: "Foo",
          number: 0
        }, {
          name: "Bar",
          number: 1
        }, {
          name: "Baz",
          number: 1
        }];
      }
    ])
    .controller('ItemController', ['$scope',
      function($scope) {
        // fake code, would get from API normally
        var getFromApi = function() {
          return {
            name: $scope.item.name,
            number: $scope.item.number + 1
          };
        }
        $scope.incr = function() {
          var newItem = getFromApi();
          for (var key in newItem) {
            $scope.item[key] = newItem[key];
          }
        }
      }
    ]);
})(window.angular);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html>
<body ng-app="app">
  <div ng-controller="ParentController">
    <p>When you click one of the below buttons</p>
    <ul>
      <li ng-repeat="item in items" ng-controller="ItemController">
        {{item.name}} ({{item.number}})
        <button ng-click="incr()">+</button>
      </li>
    </ul>
    <p>This should update</p>
    <p>{{items}}</p>
    <p>...now it does!</p>
  </div>
</body>
</html>

您可以将 API 调用逻辑移动到父控制器,请参阅 JSFiddle 演示以获取示例,或者至少替换父作用域$scope.items数组中修改的元素。

编辑:在这里废弃我之前的陈述,它不起作用的 reson 是因为当您在子作用域的 incr() 方法中分配$scope.item时,您没有修改$scope.items数组。您只是在子作用域中更改变量赋值。

因此,在incr()调用之前,内存中的变量赋值可能如下所示:

$scope.items =
   item1 -> [ js obj 1 ]
   item2 -> [ js obj 2 ]
   item3 -> [ js obj 3 ]
$scope.item -> [ js obj 1 ]

incr()呼叫第一项之后:

$scope.items =
   item1 -> [ js obj 1 ]       // still obj 1 here!
   item2 -> [ js obj 2 ]
   item3 -> [ js obj 3 ]
$scope.item -> [ js obj 4 ]    // but entirely new obj here!

示例代码:

  angular.module('app', [])
      .controller('ParentController', ['$scope',
  function ($scope) {
      $scope.items = [{
          name: "Foo",
          number: 0
      }, {
          name: "Bar",
          number: 1
      }, {
          name: "Baz",
          number: 1
      }];
      // fake code, would get from API normally
      var getFromApi = function (item) {
          return {
              name: item.name,
              number: item.number + 1
          };
      }
      $scope.updateItem = function (index) {
          $scope.items[index] = getFromApi($scope.items[index]);
      };
  }])
      .controller('ItemController', ['$scope',
  function ($scope) {
      $scope.incr = function (index) {
          $scope.updateItem(index);
      }
  }]);
显然,如果您想

以另一种方式识别修改后的项目,您可以在不使用$index的情况下逃脱。上面的代码只是针对您的问题的最简单的修复方法。您可能会将getFromApi()保留在子作用域中,并仅在那里使用$scope.items[$index] = getFromApi()(由于作用域继承)。我只是发现将该方法保留在父范围内更干净。