Однократная привязка: модель обновления и просмотр рендера

Мне было интересно, если возможно, используя angular однократную привязку, чтобы полностью повторно отобразить представление/шаблон после обновления модели, а также перекомпилировать шаблон. Например, при нажатии кнопки, возможно, на так реагирует работа: я обновляю модель и явно принудительно обновляю представление. В основном это то, чего я пытаюсь достичь:

// controller
angular.module('app', []).controller('AppCtrl', function($scope) {
    $scope.items = [
        {id: 1},
        {id: 2},
        {id: 3}
    ];

    $scope.addAndRefresh = function() {
        $scope.items.push({id: 4});
        // manually call render logic here???
    };
});


<!-- HTML template -->
<div ng-repeat="item in ::items">
    {{item.id}}
</div>
<button ng-click="addAndRefresh()">Add</button>

Нажав кнопку "Добавить", я хочу обновить представление, чтобы увидеть вновь добавленный элемент.

Ответ 1

Я пытался выяснить, как это сделать элегантно. Мне жаль, что в инфраструктуру не было что-то встроенное для обновления одноразовых привязок. Все, что я придумал, это использовать ngIf для удаления элемента, который я хотел обновить, и добавить его обратно.

Вот демо. Нажмите кнопку "Добавить элемент", вы увидите, что список не обновляется до одноразовой привязки для повтора. Проверьте значения обновления и нажмите еще раз, и элементы будут обновлены:

var app = angular.module('demo', []);

app.controller('RefreshCtrl', function($scope, $timeout) {
  var counter = 4;

  $scope.visible = true;

  $scope.items = ['Item1', 'Item2', 'Item3'];

  $scope.addItem = function() {

    if ($scope.refresh) {
      $scope.visible = false;
    }

    $scope.items.push('Item' + counter);

    counter++;

    $timeout(function() {
      $scope.visible = true;
    });
  };

});
<script src="https://code.angularjs.org/1.3.17/angular.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

<div ng-app="demo" ng-controller="RefreshCtrl" class="container">
  <button class="btn btn-default" ng-click="addItem()">Add Item</button>
  <input type="checkbox" ng-model="refresh" />Refresh Values
  <div ng-if="visible">
    <h3 ng-repeat="item in ::items">{{item}}</h3>
  </div>

  <p>Items Array: {{items}}</p>
</div>

Ответ 2

В зависимости от того, что вам нужно, я бы рекомендовал одно из двух решений:

  • Получить angular-bind-notifier.
    • Скомпилирует ваш шаблон не, обновляет только связанные значения.
  • Получить kcd-recompile.
    • Перекомпилирует шаблон вместе со связанными значениями.

Я автор первого, и большая разница между ним и другими решениями - это выбор подключения к сервису $parse.

Таким образом, вы можете использовать введенный синтаксис {{:refreshkey:expression}}/:refreshkey:expression в большинстве (если не во всех) областях Angular, где выражение принимается.


В вашем случае реализация может выглядеть примерно так:

JS

angular.module('app', []).controller('AppCtrl', function($scope) {
  $scope.items = [
      {id: 1},
      {id: 2},
      {id: 3}
  ];

  $scope.addAndRefresh = function() {
      $scope.items.push({id: 4});
      /**
       * '$$rebind' is the internal namespace used by angular-bind-notifier.
       * 'refresh' is the refresh key used in your view.
       */
      $scope.$broadcast('$$rebind:refresh');
  };
});

Разметка

<!-- HTML template -->
<div ng-repeat="item in :refresh:items">
    {{::item.id}}
</div>
<button ng-click="addAndRefresh()">Add</button>

Или, если вы хотите что-то полудинамическое

JS

angular.module('app', []).controller('AppCtrl', function($scope) {
  $scope.items = [
      {id: 1},
      {id: 2},
      {id: 3}
  ];

  $scope.add = function() {
      $scope.items.push({id: 4});
  };
});

Разметка

<!-- HTML template -->
<div bind-notifier="{ refresh: items.length }">
  <div ng-repeat="item in :refresh:items">
      {{::item.id}}
  </div>
</div>
<button ng-click="add()">Add</button>

Посмотрите README, и это jsBin для некоторых примеров использования.