简体   繁体   English

如何在不隔离范围的情况下创建递归Angular.js模板?

[英]How to Create recursive Angular.js Templates without isolating scope?

I have a recursive data structure I am trying to represent in Angular.js. 我正在尝试在Angular.js中表示一个递归数据结构。 a simplified demo is available here: 此处提供了一个简化的演示:

http://plnkr.co/edit/vsUHLYMfI4okbiVlCK7O?p=preview http://plnkr.co/edit/vsUHLYMfI4okbiVlCK7O?p=preview

In the Preview, I have the following HTML for a recursive object: 在预览中,我具有以下用于递归对象的HTML:

<ul>
  <li ng-repeat="person in people">
    <span ng-click="updateClicks(person)">{{person.name}}</span>
    <ul>
      <li ng-repeat="kid in person.kids">
        <span ng-click="updateClicks(kid)">{{kid.name}}</span>
      </li>
    </ul>
  </li>
</ul>

In my application, the view is much more complex. 在我的应用程序中,视图要复杂得多。 I would like to have a way to generate the template html for each person in a recursive fashion. 我想要一种以递归方式为每个人生成模板html的方法。 I tried doing this with a directive, however I ran into issues with infinite loops when I did not isolate the scope. 我尝试使用指令执行此操作,但是当我没有隔离范围时,便遇到了无限循环的问题。 And when I did isolate the scope, I was no longer able to call functions that are tied to the controller (in this example, the updateClicks function, however in my application there are several). 当我做了隔离范围,我不再能够调用到绑定的控制器功能(在这个例子中, updateClicks功能,但是在我的应用程序有几个)。

How can I generate html for these objects recursively, and still be able to call functions belonging to a controller? 如何为这些对象递归生成html,并且仍然能够调用属于控制器的函数?

I think the best way to do this is with an $emit. 我认为最好的方法是使用$ emit。

Let's say your recursive directive looks like this: 假设您的递归指令如下所示:

directive('person', function($compile){
  return{
  restrict: 'A',
  link: function(scope, element, attributes){
    //recursive bit, if we've got kids, compile & append them on
    if(scope.person.kids && angular.isArray(scope.person.kids)) {
     $compile('<ul><li ng-repeat="kid in person.kids" person="kid"></li></ul>')(scope, function(cloned, scope){
       element.find('li').append(cloned); 
     });
    }
  },
  scope:{
    person:'='
  },
  template: '<li><span ng-click="$emit(\'clicked\', person)">{{person.name}}</span></li>'
  }
});

notice the ng-click="$emit(clicked, person)" code, don't be distracted the \\, that's just there to escape. 注意ng-click="$emit(clicked, person)"代码,不要分散\\的使用,这只是为了逃避。 $scope.$emit will send an event all the way up your scope chain, so that in your controller, your clicked function stays mostly unchanged, but now instead of being triggered by ng-click, you're listening for the event. $ scope。$ emit会在整个作用域链中一直发送一个事件,因此在您的控制器中,您单击的函数基本上保持不变,但是现在您正在监听该事件,而不是被ng-click触发。

$scope.$on('clicked', function(event, person){
  person.clicks++;
  alert(person.name + ' has ' + person.clicks + ' clicks!');
});

cool thing is that the event object even has the isolated scopes from your recursed directives. 很酷的事情是,事件对象甚至具有与递归指令隔离的作用域。

Here's the fully working plnkr: http://plnkr.co/edit/3z8OXOeB5FhWp9XAW58G?p=preview even went down to tertiary level to make sure recursion was working. 这是可以正常使用的plnkr: http ://plnkr.co/edit/3z8OXOeB5FhWp9XAW58G?p=preview甚至降到了第三级,以确保递归有效。

Recursive tree with angular directive without scope isolation, forces you to simulate isolation by using different scope properties per depth level. 带有角度指令的递归树没有作用域隔离,迫使您通过在每个深度级别使用不同的作用域属性来模拟隔离。

I didn't find any so I wrote my own. 我找不到任何东西,所以我写了自己的。

Let's say your HTML is : 假设您的HTML是:

<body ng-app="App" ng-controller="AppCtrl">
  <div test="tree.children" test-label="tree.label">{{b}}</div>
</body>

Then you have a main module and a controller adding a tree to the scope : 然后,您有一个主模块和一个控制器,将一个树添加到范围:

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

App.controller('AppCtrl', function($scope, $timeout) {
  // prodive a simple tree
  $scope.tree = {
    label: 'A',
    children: [
      {
        label: 'a',
        children: [
          { label: '1' },
          { label: '2' }
          ]
      },
      {
        label: 'b',
        children: [
          { label: '1' },
          { label: '2' }
          ]
      }
      ]
  };

  // test that pushing a child in the tree is ok
  $timeout(function() {
    $scope.tree.children[1].children.push({label: 'c'});
  },2000);
  $timeout(function() {
  // test that changing a label is ok
    $scope.tree.children[1].label = 'newLabel';
  },4000);

});

Finally consider the following implementation of the directive test : 最后考虑指令test的以下实现:

App.directive('test', function($compile) {
  // use an int to suffix scope properties 
  // so that inheritance does not cause infinite loops anymore
  var inc = 0;
  return {
    restrict: 'A',
    compile: function(element, attr) {
      // prepare property names
      var prop = 'test'+(++inc),
          childrenProp = 'children_'+prop,
          labelProp = 'label'+prop,
          childProp = 'child_'+prop;

      return function(scope, element, attr) {
        // create a child scope
        var childScope = scope.$new();
        function observeParams() {
          // eval attributes in current scope
          // and generate html depending on the type
          var iTest = scope.$eval(attr.test),
              iLabel = scope.$eval(attr.testLabel),
              html = typeof iTest === 'object' ?
              '<div>{{'+labelProp+'}}<ul><li ng-repeat="'+childProp+' in '+childrenProp+'"><div test="'+childProp+'.children" test-label="'+childProp+'.label">{{'+childProp+'}}</div></li></ul></div>'
            : '<div>{{'+labelProp+'}}</div>';

          // set scope values and references
          childScope[childrenProp]= iTest;
          childScope[labelProp]= iLabel;

          // fill html
          element.html(html);

          // compile the new content againts child scope
          $compile(element.contents())(childScope);
        }

        // set watchers
        scope.$watch(attr.test, observeParams);
        scope.$watch(attr.testLabel, observeParams);
      };
    }
  };
});

All the explanations are in the comments. 所有的解释都在注释中。

You may have a look at the JSBin . 您可以看看JSBin

My implementation can of course be improved. 我的实现方式当然可以改善。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM