Handle open/collapse events of Accordion in Angular

后端 未结 7 1391
猫巷女王i
猫巷女王i 2020-11-29 02:04

If I have this code:


      {{group.content}}



        
相关标签:
7条回答
  • 2020-11-29 02:21

    Here's a solution inspired by kjv's answer, which easily tracks which accordion element is open. I found difficult getting ng-click to work on the accordion heading, though surrounding the element in a <span> tag and adding the ng-click to that worked fine.

    Another problem I encountered was, although the accordion elements were added to the page programmatically, the content was not. When I tried loading the content using Angular directives(ie. {{path}}) linked to a $scope variable I would be hit with undefined, hence the use of the bellow method which populates the accordion content using the ID div embedded within.

    Controller:

        //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>
    
    0 讨论(0)
  • 2020-11-29 02:28

    There is the is-open attribute on the accordion-group which points to a bindable expression. You could watch this expression and execute some logic when a given accordion group is open. Using this technique you would change your markup to:

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

    so that you can, in the controller, prepare a desired watch expression:

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

    While the above works it might be a bit cumbersome to use in practice so if you feel like this could be improved open an issue in https://github.com/angular-ui/bootstrap

    0 讨论(0)
  • 2020-11-29 02:29

    Here's a solution based on pkozlowski.opensource solution.
    Instead of adding a $watch on each item of the collection, you can use a dynamically defined Property. Here, you can bind the IsOpened property of the group to the is-open attribute.

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

    So, you can dynamically add the IsOpened property on each item of the collection in the controller :

    $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...
          }
        }
      });
    });
    

    Using properties instead of watches is better for performances.

    0 讨论(0)
  • 2020-11-29 02:40

    Accordion groups also allow for an accordion-heading directive instead of providing it as an attribute. You can use that and then wrap your header in another tag with an 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>
    

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

    0 讨论(0)
  • 2020-11-29 02:42

    I used an associative array to create a relationship between the opened state and the model object.

    The HTML is:

      <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>
    

    The controller snippet is:

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

    The 'topics' are populated on an AJAX call. Separating the 'opened' state from the model objects that are updated from the server means the state is preserved across refreshes.

    I also declare the controller with ng-controller="CaseController as controller"

    0 讨论(0)
  • 2020-11-29 02:42

    accordion-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>
    
    0 讨论(0)
提交回复
热议问题