AngularJS:ng-repeat 和 scoped ng-click

AngularJS: ng-repeat and scoped ng-click

我有一系列由 ng-repeat 填充的列表项。可见性由简单的 ng-click 和 ng-show 关系控制。在大多数情况下,这工作得很好,但我希望能够使用全局按钮控制 show/hide 行为,该按钮将显示或隐藏列表中的 all 可用项目.

公平披露:我对 AngularJS 还是很陌生。我知道这是一个范围界定问题,但我不确定如何解决它。这几乎可以肯定是不知道要问正确的问题的情况。

我这里有一个 jsfiddle 来展示我的困难:http://jsfiddle.net/36BYs/838/

样本HTML:

<div ng-controller="MainCtrl">

  <span ng-show="!IsVisible" ng-click="isVisible = !isVisible;" >
    (show/hide all)
    <i class="fa fa-minus-square-o fa-small"></i>
  </span>
<ul>
        <li ng-repeat="mentor in mentors">
        <a ng-click="isVisible = !isVisible;">show/hide</a>
        <span ng-show="isVisible">{{mentor}}</span>
        </li>
    </ul>
</div>

示例 JS:

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

function MainCtrl( $scope ) {
  $scope.isVisible = true;
  $scope.mentors = [ 'Jonathan', 'Nathan', 'Chris', 'Brian', 'Timothy' ];
}

只要您没有单独切换列表项之一,它就可以正常工作。但是如果您在特定行上单击 show/hide,则全局 ng-click 将失去对该项目的控制。

提前感谢您提供的任何建议。

看来,在初始渲染中,angular 正在尝试为每位导师评估 isVisible。因为 mentor.isVisible 将是 undefined,angular 框架使用父作用域的 isVisible.

但是,当你切换一个特定的导师时,你有效地为那个特定的 mentor 字符串分配了一个 isVisible 布尔值 属性。 (因为您可以愉快地将附加属性附加到 javascript 中的 string)。

在此之后,您可以切换 show/hide all,这将影响每个尚未分配给 isVisible 属性 的导师。

它展示了 AngularJS 的初始版本中的范围界定是多么混乱。

这是一个可行的答案:http://jsfiddle.net/fkw5923t/

我做了一些修改:

  • 我没有为 $scope 赋值,而是使用 controllerName as mnemonic 语法。在控制器代码中,我将值分配给 this 而不是 $scope。我更喜欢这种方法,因为现在范围界定清晰明了
  • 我已将 mentor 从字符串更改为对象,包含两个值:nameisVisible。同样,这现在使范围规则清晰明了。

HTML:

<div ng-controller="MainCtrl as mainCtrl">

  <span class = "clickable" ng-click="mainCtrl.isVisible = !mainCtrl.isVisible;" >
    (show/hide all)
    <i class="fa fa-minus-square-o fa-small"></i>
  </span>
<ul ng-show="mainCtrl.isVisible">
        <li ng-repeat="mentor in mainCtrl.mentors">
        <a class = "clickable" ng-click="mentor.isVisible = !mentor.isVisible;">show/hide</a>
        <span ng-show="mentor.isVisible">{{ mentor.name }}</span>
        </li>
    </ul>
</div>

javascript:

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

function MainCtrl() {
  this.isVisible = true;
  var mentorNames = ['Jonathan', 'Nathan', 'Chris', 'Brian', 'Timothy'];
  this.mentors = mentorNames.map(
      name => { 
         return { 
             name:name, 
             isVisible:true 
         }; 
      }
  );
}

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

app.controller("myCtrl", function ($scope) {
  $scope.mentors = [
    'Jonathan',
    'Nathan',
    'Chris',
    'Brian',
    'Timothy'
  ];
  
  $scope.showAll = function() {
    $scope.visibleMentors = $scope.mentors.slice();
  };
  
  $scope.hideAll = function() {
    $scope.visibleMentors = [];
  };
  
  $scope.isVisible = function(mentor) {
    return $scope.visibleMentors.includes(mentor);
  };
  
  $scope.show = function(mentor) {
    $scope.visibleMentors.push(mentor);
  };
  
  $scope.hide = function(mentor) {
    $scope.visibleMentors.splice($scope.visibleMentors.indexOf(mentor), 1);
  };
  
  $scope.showAll();
});
a {
  cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>

<div ng-app="myApp">
  <div ng-controller="myCtrl">
    <button ng-click="showAll()">show all</button>
    <button ng-click="hideAll()">hide all</button>                                                                                                                                                                           
    <ul>
      <li ng-repeat="mentor in mentors">
        <a ng-show="isVisible(mentor)" ng-click="hide(mentor)">hide</a>
        <a ng-show="!isVisible(mentor)" ng-click="show(mentor)">show</a>
        <span ng-show="isVisible(mentor)">
          {{mentor}}
        </span>
      </li>
    </ul>
  </div>
</div>

虽然其他评论很有用,但用户 miqid 的评论为我的特定需求提供了最佳答案:

As you're aware, ng-repeat introduces a separate scope so that each isVisible underneath no longer tracks the parent isVisible. One solution is to explicitly track isVisible per item in addition to tracking a parent visibility state that overrides item-level one if necessary. Demo—jsfiddle.net/uLykhg0z – miqid 14 hours ago

jsfiddle 是 Andrew Shepherd 解决方案 (jsfiddle.net/uLykhg0z) 的变体:

HTML:

<div ng-controller="MainCtrl">

  <span ng-show="!IsVisible" class = "clickable" ng-click="toggleVisibility()" >
    (show/hide all)
    <i class="fa fa-minus-square-o fa-small"></i>
  </span>
  <ul>
        <li ng-repeat="mentor in mentors">
        <a class = "clickable" ng-click="mentor.isVisible = !mentor.isVisible;">show/hide</a>
        <span ng-show="mentor.isVisible">{{mentor.name}}</span>
        </li>
    </ul>
</div>

JS:

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

function MainCtrl( $scope ) {
    var isVisible = true;
  $scope.mentors = [
    { name: 'Jonathan', isVisible: true },
    { name: 'Nathan', isVisible: true },
    { name: 'Chris', isVisible: true },
    { name: 'Brian', isVisible: true },
    { name: 'Timothy', isVisible: true },
  ];
  $scope.toggleVisibility = function () {
    isVisible = !isVisible;
    $scope.mentors = $scope.mentors.map(function (mentor) {
        mentor.isVisible = isVisible;
      return mentor;
    });
  };
}

这使我能够容纳一些我必须在树结构中处理的更复杂的嵌套。

再次感谢大家的帮助!