Eric Mitjans
Eric Mitjans

Reputation: 2169

Structure issues using ng-repeat with Bootstrap and AngularJS

I'm building an app with Bootstrap and AngularJS. At some point I have an ng-repeat on a col-md-3, listing products. My problem is that I want to be able to insert a collapse into the grid, but as the columns are automatically generated, I don't really know how to do it.

Here's a diagram to understand it better: First, the grid of .col-md-3 is populated from the ng-repeat.

enter image description here

And what I'm trying to achieve, is to add a .col-md-12 that appears right under the row of the .col-md-3 that gets clicked on.

enter image description here

My initial thought was to add an empty .col-md-12 dynamically after each group of 4 .col-md-3, but I wouldn't know how to do so, and it kinda seems to be that it would be a rather dull approach. Any ideas?

Here's the relevant html:

<div class="infinite" infinite-scroll="loadDetails()">
      <div class="col-xs-3 col-md-3" ng-repeat="release in main.releases | filter:main.album">
        <release release="release" artist="main.artist" class="dropdown"></release> 
      </div>
</div>

EDIT: Here's a working Plunker including tasseKATTs solution.

Upvotes: 1

Views: 2610

Answers (2)

j.wittwer
j.wittwer

Reputation: 9497

This question is easier to answer in an angular way if you follow the bootstrap convention using 12 columns per a row:

Grid columns are created by specifying the number of twelve available columns you wish to span. For example, three equal columns would use three .col-xs-4.

In your case, this means each row can have up to 4 .col-xs-3 columns, or just 1 .col-xs-12. You can prep your data to be displayed this way by splitting it into an array of smaller arrays.

$scope.getRows = function(array) {
var rows = [];

//https://stackoverflow.com/questions/8495687/split-array-into-chunks
var i,j,temparray,chunk = 4;
for (i=0,j=array.length; i<j; i+=chunk) {
    temparray = array.slice(i,i+chunk);

    rows.push(temparray);
}

return rows;
};

$scope.rows = $scope.getRows($scope.main.releases);

Then you can nest ngRepeat to achieve the desired layout, using ng-if to only create a col-xs-12 when a corresponding .col-xs-3 is clicked.

<div ng-repeat="row in rows">
  <div class="row">
    <div class="col-xs-3" ng-repeat="release in row" ng-click="main.releaseClicked=release">
      <div class="release">{{release}}</div> 
    </div>
  </div>
  <div class="row" ng-repeat="release in row" ng-if="main.releaseClicked==release">
    <div class="col-xs-12">
      <div class="detail">Release detail: {{release}}</div>
    </div>
  </div>
</div>

This leaves you with a more declarative view that describes how the app works, and doesn't require jQuery to do DOM manipulation.

Here is a working demo: http://plnkr.co/ujlpq5iaX413fThbocSj

Upvotes: 2

tasseKATT
tasseKATT

Reputation: 38490

Place a custom directive on your inner element together with a position counter that starts with 1 and a marker describing if it's the last element:

<div ng-repeat="item in items" class="col-xs-3">
  <div class="item" the-directive position="{{ $index + 1 }}" last="{{ $last }}">
  </div>
</div>

Create the directive with an isolated scope, bind scope properties to the values of the position and last attributes and attach a click event handler to the element:

app.directive('theDirective', function() {
  return {
    restrict: 'A',
    scope: { position: '@', last: '@' },
    link: function(scope, element, attrs) {
      element.bind('click', function() {
        ...
      });
    }
  };
});

In the click handler first create the collapse element or select it if it already exists:

var collapseQuery = document.querySelector('#collapse');
var collapse = collapseQuery === null ?
angular.element('<div id="collapse" class="col-xs-12"><div class="twelve"></div></div>') :
angular.element(collapseQuery);

Based on the position of the clicked element calculate the rounded number up to the nearest multiple of four:

var calculatedPosition = Math.ceil(scope.position / 4) * 4;

Get the element at the calculated position or the last one if the position is out of range:

var calculatedQuery = document.querySelector('[position="' + calculatedPosition + '"]');
if (calculatedQuery === null) calculatedQuery = document.querySelector('[last="true"]');;

var calculatedElement = angular.element(calculatedQuery);

Insert the collapse element after the element at the calculated position:

calculatedElement.parent().after(collapse);

Could use some optimizations, but hopefully puts you on the right track.

Demo with some extra visuals: http://plnkr.co/edit/fsC51vS7Ily3X3CVmxSZ?p=preview

Upvotes: 3

Related Questions