在Angular中处理Accordion的打开/折叠事件

如果我有这个代码:

<accordion-group heading="{{group.title}}" ng-repeat="group in groups"> {{group.content}} </accordion-group> 

使用AngularJS,angular-ui和Twitter Bootstrap,可以在打开时让手风琴调用一些动作吗? 我知道我不能简单地添加ng-click ,因为在“编译”为HTML来打开/折叠组之后已经使用了。

手风琴组有一个is-open属性,它指向一个可绑定的expression式。 当给定的手风琴组打开时,您可以观看这个表情并执行一些逻辑。 使用这种技术,你会改变你的标记:

 <accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.open"> {{group.content}} </accordion-group> 

这样你可以在控制器中准备一个理想的expression式:

 $scope.$watch('groups[0].open', function(isOpen){ if (isOpen) { console.log('First group was opened'); } }); 

虽然上面的作品在实践中使用可能有点麻烦,所以如果你觉得这可以改进,请打开https://github.com/angular-ui/bootstrap

手风琴组织也允许使用手风琴标题指令,而不是将其作为一个属性提供。 你可以使用它,然后用ng-click将你的头部包裹在另一个标签中。

 <accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.open"> <accordion-heading> <span ng-click="opened(group, $index)">{{group.content}}</span> </accordion-heading> </accordion-group> 

例如: http : //plnkr.co/edit/B3LC1X?p=preview

这是一个基于pkozlowski.opensource解决scheme的解决scheme。
您可以使用dynamic定义的Property ,而不是在集合的每个项目上添加$ watch 。 在这里,您可以将组的IsOpened属性绑定到is-open属性。

 <accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.IsOpened"> {{group.content}} </accordion-group> 

因此,您可以在控制器中的集合的每个项目上dynamic添加IsOpened属性:

 $scope.groups.forEach(function(item) { var isOpened = false; Object.defineProperty(item, "IsOpened", { get: function() { return isOpened; }, set: function(newValue) { isOpened = newValue; if (isOpened) { console.log(item); // do something... } } }); }); 

使用属性而不是手表对性能更好。

我用一个关联数组来创build打开的状态和模型对象之间的关系。

HTML是:

  <div ng-controller="CaseController as controller"> <accordion close-others="controller.model.closeOthers"> <accordion-group ng-repeat="topic in controller.model.topics track by topic.id" is-open="controller.model.opened[topic.id]"> <accordion-heading> <h4 class="panel-title clearfix" ng-click="controller.expand(topic)"> <span class="pull-left">{{topic.title}}</span> <span class="pull-right">Updated: {{topic.updatedDate}}</span> </h4> </accordion-heading> <div class="panel-body"> <div class="btn-group margin-top-10"> <button type="button" class="btn btn-default" ng-click="controller.createComment(topic)">Add Comment<i class="fa fa-plus"></i></button> </div> <div class="btn-group margin-top-10"> <button type="button" class="btn btn-default" ng-click="controller.editTopic(topic)">Edit Topic<i class="fa fa-pencil-square-o"></i></button> </div> <h4>Topic Description</h4> <p><strong>{{topic.description}}</strong></p> <ul class="list-group"> <li class="list-group-item" ng-repeat="comment in topic.comments track by comment.id"> <h5>Comment by: {{comment.author}}<span class="pull-right">Updated: <span class="commentDate">{{comment.updatedDate}}</span> | <span class="commentTime">{{comment.updatedTime}}</span></span></h5> <p>{{comment.comment}}</p> <div class="btn-group"> <button type="button" class="btn btn-default btn-xs" ng-click="controller.editComment(topic, comment)">Edit <i class="fa fa-pencil-square-o"></i></button> <button type="button" class="btn btn-default btn-xs" ng-click="controller.deleteComment(comment)">Delete <i class="fa fa-trash-o"></i></button> </div> </li> </ul> </div> </accordion-group> </accordion> 

控制器片段是:

  self.model = { closeOthers : false, opened : new Array(), topics : undefined }; 

“主题”是在AJAX调用中填充的。 将“打开”状态与从服务器更新的模型对象分离意味着在刷新时保持状态。

我也用ng-controller="CaseController as controller"声明ng-controller="CaseController as controller"

手风琴controller.js

 MyApp.Controllers .controller('AccordionCtrl', ['$scope', function ($scope) { $scope.groups = [ { title: "Dynamic Group Header - 1", content: "Dynamic Group Body - 1", open: false }, { title: "Dynamic Group Header - 2", content: "Dynamic Group Body - 2", open: false }, { title: "Dynamic Group Header - 3", content: "Dynamic Group Body - 3", open: false } ]; /** * Open panel method * @param idx {Number} - Array index */ $scope.openPanel = function (idx) { if (!$scope.groups[idx].open) { console.log("Opened group with idx: " + idx); $scope.groups[idx].open = true; } }; /** * Close panel method * @param idx {Number} - Array index */ $scope.closePanel = function (idx) { if ($scope.groups[idx].open) { console.log("Closed group with idx: " + idx); $scope.groups[idx].open = false; } }; }]); 

的index.html

 <div ng-controller="AccordionCtrl"> <accordion> <accordion-group ng-repeat="group in groups" is-open="group.open"> <button ng-click="closePanel($index)">Close me</button> {{group.content}} </accordion-group> <button ng-click="openPanel(0)">Set 1</button> <button ng-click="openPanel(1)">Set 2</button> <button ng-click="openPanel(2)">Set 3</button> </accordion> </div> 

这是一个由kjv的答案启发的解决scheme,它可以轻松跟踪哪个手风琴元素是开放的。 我发现很难让ng-click在手风琴的标题上工作,尽pipe围绕在<span>标签中的元素,并添加了ng-click,可以很好地工作。

我遇到的另一个问题是,虽然accordion元素被编程添加到页面,但内容不是。 当我尝试使用Angular指令(即。 {{path}} )链接到$scopevariables时,我会用undefined命中内容,因此使用下面的方法来填充手风琴内容。

控制器:

  //initialise the open state to false $scope.routeDescriptors[index].openState == false function opened(index) { //we need to track what state the accordion is in if ($scope.routeDescriptors[index].openState == true){ //close an accordion $scope.routeDescriptors[index].openState == false } else { //open an accordion //if the user clicks on another accordion element //then the open element will be closed, so this will handle it if (typeof $scope.previousAccordionIndex !== 'undefined') { $scope.routeDescriptors[$scope.previousAccordionIndex].openState = false; } $scope.previousAccordionIndex = index; $scope.routeDescriptors[index].openState = true; } function populateDiv(id) { for (var x = 0; x < $scope.routeDescriptors.length; x++) { $("#_x" + x).html($scope.routeDescriptors[x]); } } 

HTML:

  <div ng-hide="hideDescriptions" class="ng-hide" id="accordionrouteinfo" ng-click="populateDiv()"> <accordion> <accordion-group ng-repeat="path in routeDescriptors track by $index"> <accordion-heading> <span ng-click="opened($index)">route {{$index}}</span> </accordion-heading> <!-- Notice these divs are given an ID which corresponds to it's index--> <div id="_x{{$index}}"></div> </accordion-group> </accordion> </div> 

你可以做一个Angular指令:

HTML

 <div uib-accordion-group is-open="property.display_detail" ng-repeat="property in properties"> <div uib-accordion-heading ng-click="property.display_detail = ! property.display_detail"> some heading text </div> <!-- here is the accordion body --> <div ng-init="i=$index"> <!-- I keep track of the index of ng-repeat --> <!-- and I call a custom directive --> <mydirective mydirective_model="properties" mydirective_index="{% verbatim ng %}{{ i }}{% endverbatim ng %}"> here is the body </mydirective> </div> </div> 

JS

 app.directive("mydirective", function() { return { restrict: "EAC", link: function(scope, element, attrs) { /* note that ng converts everything to camelCase */ var model = attrs["mydirectiveModel"]; var index = attrs["mydirectiveIndex"]; var watched_name = model + "[" + index + "].display_detail" scope.$watch(watched_name, function(is_displayed) { if (is_displayed) { alert("you opened something"); } else { alert("you closed something"); } }); } } }); 

有关于我的设置有一些特质(我使用Django,因此“{%verbatim%}”标签),但方法应该工作。