Blindsyde Blindsyde - 3 years ago 92
AngularJS Question

Using panels as accordions with angular

I have a jsfiddle example illustrating an accordion that works when using no angular; in the no angular example you can see that the previous panel closes when I click on a new panel, you can also see that there is spacing between each panel. When I add angular and have the panels build from an object array the accordion no longer works correctly; instead the previous panel does not collapse when clicking a new panel and the spacing between each panel no longer exists. I have compared the code and I can't figure out what I am doing wrong, can you see what is wrong?

HTML:

<div class="container" ng-controller="cntrl">
<p>No angular:</p>
<div class="panel-group" id="accordion">
<div class="panel panel-default" data-toggle="collapse" data-parent="#accordion" data-target="#collapse1">
<div class="panel-heading">
<h4 class="panel-title">
Collapsible Group 1
</h4>
</div>
<div id="collapse1" class="panel-collapse collapse in">
<div class="panel-body">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
</div>
</div>
<div class="panel panel-default" data-toggle="collapse" data-parent="#accordion" data-target="#collapse2">
<div class="panel-heading">
<h4 class="panel-title">
Collapsible Group 2
</h4>
</div>
<div id="collapse2" class="panel-collapse collapse">
<div class="panel-body">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
</div>
</div>
<div class="panel panel-default" data-toggle="collapse" data-parent="#accordion" data-target="#collapse3">
<div class="panel-heading">
<h4 class="panel-title">
Collapsible Group 3
</h4>
</div>
<div id="collapse3" class="panel-collapse collapse">
<div class="panel-body">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
</div>
</div>
</div>

<!-- now using angular -->
<p>Using angular (<em>notice the previous panel no longer collapses and the spacing between the panels does not exist like in my no angular example</em>):</p>
<div class="panel-group" id="ngaccordion">
<div ng-repeat="prop in someObj">
<div class="panel panel-default" data-toggle="collapse" data-parent="#ngaccordion" data-target="#{{prop.id}}">
<div class="panel-heading">
<h4 class="panel-title">
{{prop.contractName}}
</h4>
</div>
<!-- Instead of {{prop.id}} for id below and data-target above I have also tried "test{{$index}}" -->
<div id="{{prop.id}}" class="panel-collapse collapse" ng-class="{ 'in' : $first}">
<div class="panel-body">
<ul class="list-group">
<li class="list-group-item">
<label>Price:</label> {{prop.price | currency:"$" }}</li>
<li class="list-group-item">
<label>Start Date:</label> {{prop.startDate}}</li>
<li class="list-group-item">
<label>End Date:</label> {{prop.endDate}}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>


JS:

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

app.controller('cntrl', ['$scope', function($scope) {
$scope.someObj = [{
id: 'test1',
contractName: 'Test One',
price: 0.00,
startDate: moment().format("YYYY-MM-DD"),
endDate: moment().add(6, 'months').format("YYYY-MM-DD")
}, {
id: 'test2',
contractName: 'Test Two',
price: 20.00,
startDate: moment().add(6, 'months').format("YYYY-MM-DD"),
endDate: moment().add(12, 'months').format("YYYY-MM-DD")
}, {
id: 'test3',
contractName: 'Test Three',
price: 40.00,
startDate: moment().add(12, 'months').format("YYYY-MM-DD"),
endDate: 'NO END'
}];
}]);


Any help would be appreciated!

Answer Source

Since you are adding the ng-repeat in another div which is a parent of the accordion element (Bootstrap is confounded as to where the other ones are! :) ), this problem is occurring, to solve this issue just set the ng-repeat inside the panel div itself, refer my below example.

Before:

<div class="panel-group" id="ngaccordion">
    <div ng-repeat="prop in someObj">
      <div class="panel panel-default" data-toggle="collapse" data-parent="#ngaccordion" data-target="#{{prop.id}}">
        <div class="panel-heading">
          <h4 class="panel-title">

After:

<div class="panel-group" id="ngaccordion">
    <div ng-repeat="prop in someObj" class="panel panel-default" data-toggle="collapse" data-parent="#ngaccordion" data-target="#{{prop.id}}">
      <div class="panel-heading">
        <h4 class="panel-title">

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

app.controller('cntrl', ['$scope', function($scope) {
  $scope.someObj = [{
    id: 'test1',
    contractName: 'Test One',
    price: 0.00,
    startDate: moment().format("YYYY-MM-DD"),
    endDate: moment().add(6, 'months').format("YYYY-MM-DD")
  }, {
    id: 'test2',
    contractName: 'Test Two',
    price: 20.00,
    startDate: moment().add(6, 'months').format("YYYY-MM-DD"),
    endDate: moment().add(12, 'months').format("YYYY-MM-DD")
  }, {
    id: 'test3',
    contractName: 'Test Three',
    price: 40.00,
    startDate: moment().add(12, 'months').format("YYYY-MM-DD"),
    endDate: 'NO END'
  }];
}]);
.container {
  margin-top: 15px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<div class="container" ng-controller="cntrl">
  <p>
    No angular:
  </p>
  <div class="panel-group" id="accordion">
    <div class="panel panel-default" data-toggle="collapse" data-parent="#accordion" data-target="#collapse1">
      <div class="panel-heading">
        <h4 class="panel-title">
          Collapsible Group 1
        </h4>
      </div>
      <div id="collapse1" class="panel-collapse collapse in">
        <div class="panel-body">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
      </div>
    </div>
    <div class="panel panel-default" data-toggle="collapse" data-parent="#accordion" data-target="#collapse2">
      <div class="panel-heading">
        <h4 class="panel-title">
          Collapsible Group 2
        </h4>
      </div>
      <div id="collapse2" class="panel-collapse collapse">
        <div class="panel-body">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
      </div>
    </div>
    <div class="panel panel-default" data-toggle="collapse" data-parent="#accordion" data-target="#collapse3">
      <div class="panel-heading">
        <h4 class="panel-title">
          Collapsible Group 3
        </h4>
      </div>
      <div id="collapse3" class="panel-collapse collapse">
        <div class="panel-body">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
      </div>
    </div>
  </div>

  <!-- now using angular -->
  <p>
    Using angular (<em>notice the previous panel no longer collapses and the spacing between the panels does not exist like in my no angular example</em>):
  </p>
  <div class="panel-group" id="ngaccordion">
    <div ng-repeat="prop in someObj" class="panel panel-default" data-toggle="collapse" data-parent="#ngaccordion" data-target="#{{prop.id}}">
      <div class="panel-heading">
        <h4 class="panel-title">
          {{prop.contractName}}
        </h4>
      </div>
      <!-- Instead of {{prop.id}} for id below and data-target above I have also tried "test{{$index}}" -->
      <div id="{{prop.id}}" class="panel-collapse collapse" ng-class="{ 'in' : $first}">
        <div class="panel-body">
          <ul class="list-group">
            <li class="list-group-item">
              <label>Price:</label> {{prop.price | currency:"$" }}</li>
            <li class="list-group-item">
              <label>Start Date:</label> {{prop.startDate}}</li>
            <li class="list-group-item">
              <label>End Date:</label> {{prop.endDate}}</li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</div>

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download