ng-repeat doesn't work when HTML is altered under $compile

感情迁移 提交于 2019-12-11 06:18:12

问题


Directive code:

.directive('replace', function($compile) {
      return function (scope, element) {
        element.html(element.html().replace("Hej", "Hey!"));
        $compile(element.contents())(scope);
      }
  });
})

HTML

 <div ng-controller="GreeterController">
     <div replace>Hej <div ng-repeat="e in arr">{{ e }}</div>
     </div>
 </div>

Controller

app.controller('GreeterController', ['$scope', function($scope) {
    $scope.arr = [1, 2, 3, 4];
}]);

Live example

As the title says, ng-repeat doesn't work when I'm using the directive from above on HTML which contains it. But once I remove that line which uses .replace() command to replace part of HTML then ng-repeat starts working for some reason.
Does anyone know where's the actual problem? I have tried everything and I still seem to not get it why it doesn't work as it should.


回答1:


The manipulation can also be done in the compile phase:

app.directive("replace", function(){
    return {
        compile: function(element){
            element.html(element.html().replace('Hej', 'Hey!'));
            /*return {
                pre: function(scope, element){
                  element.html(element.html().replace('Hej', 'Hey!'));
                }
            }*/
        }
    };
});

The original problem was caused because the linking of the ng-repeat directive was done before the element with that directive is replaced with the replace operation. The watchers associated with the ng-repeat directive then operate on elements that are no longer attached to the visible DOM.

By moving the replace operation to either the compile phase or the preLink phase, the replacing of the element that has the ng-repeat directive is done before the ng-repeat directive is linked. The watchers associated with ng-repeat directive then work with the replaced DOM.




回答2:


You should let Angular and its change detection cycle do the HTML manipulation for you, instead of directly changing it yourself.

I've edited your example to use scope bindings to achieve what you wanted:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example - example-compile-production</title>
  

  <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
  

  
</head>
<body ng-app="compileExample">
  <script>
  angular.module('compileExample', [], function($compileProvider) {
    // configure new 'compile' directive by passing a directive
    // factory function. The factory function injects the '$compile'
    $compileProvider.directive('replace', function($compile) {
        return {
            link: function (scope, element) {
              scope.greeting = 'Hey!';
              $compile(element.contents())(scope);
            }
        }
    });
  })
  .controller('GreeterController', ['$scope', function($scope) {
    $scope.test = [1, 2, 3, 4];
    $scope.greeting = 'Hej';
  }]);
</script>
<div ng-controller="GreeterController">
  <div replace>{{greeting}} <div ng-repeat="e in test">{{ e }}</div></div>
</div>
</body>
</html>

<!-- 
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->

Note: I removed "scope: false" as that is the default behaviour.

EDIT: Since you must replace HTML in place here's a solution with jQuery:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example - example-compile-production</title>
  

  <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
  <script
  src="https://code.jquery.com/jquery-3.1.1.min.js"
  integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
  crossorigin="anonymous"></script>

  
</head>
<body ng-app="compileExample">
  <script>
  angular.module('compileExample', [], function($compileProvider) {
    // configure new 'compile' directive by passing a directive
    // factory function. The factory function injects the '$compile'
    $compileProvider.directive('replace', function($compile) {
        return function (scope, element) {
          $(element).find( ".translation" ).replaceWith("Hey!");
        }
    });
  })
  .controller('GreeterController', ['$scope', function($scope) {
    $scope.arr = [1, 2, 3, 4];
  }]);
</script>
<div ng-controller="GreeterController">
  <div replace><span class="translation">Hej</span> <div ng-repeat="e in arr">{{ e }}</div></div>
</div>
</body>
</html>

<!-- 
Copyright 2017 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
-->



回答3:


Solved it like this:

.directive("replace", function(){
    return {
        compile: function(){
            return {
                pre: function(scope, element){
                    element.html(element.html().replace('Hej', 'Hey!'));
                }
            }
        }
    };
});

Live example



来源:https://stackoverflow.com/questions/42327008/ng-repeat-doesnt-work-when-html-is-altered-under-compile

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!