Toggling accordion panel icons with ng-class and ng-click

Problem

I have an accordion with "chevron" icons that are toggled to point up or down when clicked, this is done with ng-click and ng-class. The accordion only permits one panel to be open at the same time - so when I click on a panel that is closed, the panel that is open closes. But how do I toggle the chevron icon on the open panel that is getting closed with ng-click?

Originally i could do it with DOM manipulation etc, but since this is a partial view in angular I cannot do it.

Code:

<div class="panel-group" id="accordion">
  <div class="panel panel-default">
    <div class="panel-heading">
      <h4 class="panel-title">
        <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" ng-click="firstpaneisopen=!firstpaneisopen">
           <i class="glyphicon" ng-class="{'glyphicon-chevron-up': firstpaneisopen, 'glyphicon-chevron-down': !firstpaneisopen}"></i> Collapsible Group Item #1
         </a>
      </h4>
     </div>
    <div id="collapseOne" class="panel-collapse collapse in">
     <div class="panel-body">
       Body
     </div>
    </div>
  </div>
  <div class="panel panel-default">
   <div class="panel-heading">
     <h4 class="panel-title">
       <a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" ng-click="secondpaneisopen=!secondpaneisopen>
         <i class="glyphicon" ng-class="{'glyphicon-chevron-up': secondpaneisopen, 'glyphicon-chevron-down': !secondpaneisopen}"></i> Collapsible Group Item #2
       </a>
     </h4>
    </div>
    <div id="collapseTwo" class="panel-collapse collapse">
     <div class="panel-body">
       Body
     </div>
    </div>
   </div>
 </div>
Problem courtesy of: bjornasm

Solution

I would recommend that you check out 'UI Bootstrap' from the AngularUI team. It's a collection of "Bootstrap components written in pure AngularJS".

http://angular-ui.github.io/bootstrap/

Their website features an example which shows their Accordion directive using ng-class to toggle the chevron icons.

http://angular-ui.github.io/bootstrap/#/accordion

Their directive also features a close-others attribute which, if true, will ensure only a single panel is open at any one time.

<accordion close-others="true">
Solution courtesy of: Andrew Sula

Discussion

This is what I've tried and it seems to work

In my controller I have

$scope.menuStatus = [   { isOpen : true     },
                        { isOpen : false    }, 
                        { isOpen : false    } ]

In my html I have

<accordion id='cntLeftMenu' close-others='false'>
    <accordion-group is-open='menuStatus[0].isOpen'>
        <accordion-heading class='menu-title'>
            Cars <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': menuStatus[0].isOpen , 'glyphicon-chevron-right': !menuStatus[0].isOpen }"></i> 
        </accordion-heading>
        <ul class='left-menu'>
            <li><a href=''>test1</a></li>
            <li><a href=''>test1</a></li>
            <li><a href=''>test1</a></li>
        </ul>

    </accordion-group>

    <accordion-group is-open='menuStatus[1].isOpen'>
        <accordion-heading class='menu-title'>
            Customers <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': menuStatus[1].isOpen , 'glyphicon-chevron-right': !menuStatus[1].isOpen }"></i> 
        </accordion-heading>
        <ul class='left-menu'>
            <li><a href=''>test1</a></li>
            <li><a href=''>test1</a></li>
            <li><a href=''>test1</a></li>
        </ul>

    </accordion-group>

    <accordion-group is-open='menuStatus[2].isOpen'>
        <accordion-heading class='menu-title'>
            Staff <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': menuStatus[2].isOpen , 'glyphicon-chevron-right': !menuStatus[2].isOpen }"></i> 
        </accordion-heading>
        <ul class='left-menu'>
            <li><a href=''>test1</a></li>
            <li><a href=''>test1</a></li>
            <li><a href=''>test1</a></li>
        </ul>

    </accordion-group>


<accordion>
Discussion courtesy of: Clinton

Did some researching, I was able to use this method to add the class open to the panel-header when it's toggled open. This is example allows multiple panels to be open and uses Angular Strap Collapse.

Template:

<div class="panel-group" 
     role="tablist" 
     data-ng-model="multiplePanels.activePanels" 
     data-allow-multiple="true" 
     data-bs-collapse>
   <div class="panel panel-default" ng-repeat="location in locations">
       <div class="panel-heading" 
            role="tab" 
            data-ng-class="{'open' : multiplePanels.activePanels.indexOf($index) > -1}"
            bs-collapse-toggle>
            <h3>Some Header Stuff</h3>
       </div>
       <div class="panel-collapse" role="tabpanel" bs-collapse-target>
            <div class="panel-body">
                <p>Stuff in the panel body...</p>
            </div>
       </div>
   </div>
</div>

JS in the controller:

$scope.multiplePanels.activePanels = [];
Discussion courtesy of: BrandonReid

After some research i've got one way to solve this.

JS:

$scope.firstpaneisopen = false;
$scope.secondpaneisopen = false;

Modified ng-click on the first pane:

<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" ng-click="firstpaneisopen=!firstpaneisopen; secondpaneisopen=false">

vice versa on the second pane.

I don't think this is any elegant solution, especially if we got more than two panes, but it works.

Discussion courtesy of: bjornasm

The easiest solution I have ever think. And not to use any controller,

<div class="fa fa-fw list-arrow" ng-class="{'fa-angle-down': isClicked, 'fa-angle-right' : !isClicked}" ng-click="isClicked = !isClicked"> </div> You can write similar like me.

Discussion courtesy of: Ashraful Islam

How about working with CSS? Bootstrap adds a class collapsed on the html element that has the data-toggle="collapse". When the slide opens, it removes this collapsed class. We can then work with css to, for example, rotate the span element (a child of the element that has the data-toggle attribute).

<style>

 button.collapsed span {
  transform: rotate(-90deg);
 }
</style>
<button type="button" class="btn btn-info collapsed" data-toggle="collapse" data-target="#demo">
 Simple collapsible
 <span class="glyphicon glyphicon-chevron-down"></span>
</button>
<div id="demo" class="collapse">
 Lorem ipsum dolor...
</div>

IMPORTANT NOTE : Ensure collapsed class is added to the element that has the data-toggle="collapse" attribute for this to work smoothly. Otherwise, on initial loading of the element, the span element doesn't rotate as expected the first time.

Discussion courtesy of: Akash

This recipe can be found in it's original form on Stack Over Flow.