summerfreeze summerfreeze - 3 months ago 7
Javascript Question

Creating rows in Angular when using ng-repeat

My code repeats posts from the API and shows 9 posts in a row. My goal is to create a grid - 3 rows with 3 posts in each one. The solution I tried doesn't work. I also tried 'clearfix', but with no success.
Does anyone know how to make this work?

Here's the code:



var myApp = angular.module('myApp', ['ngRoute', 'ui.bootstrap']);

myApp.config(function ($routeProvider) {
$routeProvider.when('/', {
templateUrl: 'allposts.htm',
controller: 'PostsController'
}).when('/post', {
templateUrl: 'post.htm',
controller: 'PostController'
}).when('/addpost', {
templateUrl: 'addpost.htm',
controller: 'AddController'
}).otherwise({
redirectTo: '/'
});
});

myApp.controller('PostsController', function ($scope) {
});

myApp.controller('PostController', function ($scope) {
});

myApp.controller('AddController', function ($scope) {
});


myApp.controller('controller', function ($scope, $http) {
$scope.title = "Kansi app";
$http({
method: 'GET',
url: "http://jsonplaceholder.typicode.com/posts"
}).then(function (response) {
$scope.posts = response.data;
$scope.post = response.data[0];
$scope.viewby = 9;
$scope.totalItems = $scope.posts.length;
$scope.currentPage = 1;
$scope.itemsPerPage = $scope.viewby;
$scope.maxSize = 5;
});

$scope.setPage = function (pageNo) {
$scope.currentPage = pageNo;
};

$scope.setItemsPerPage = function (num) {
$scope.itemsPerPage = num;
$scope.currentPage = 1; //reset to first page
};

$scope.getRowClass = function (index) {
if (index % 3 === 0) {
return "row";
}
};
});

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<title>App</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-route.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.1.3/ui-bootstrap-tpls.js"></script>
</head>

<body layout="column" ng-app="myApp" ng-cloak ng-controller="controller">
<h1>{{title}}</h1>
<a href="#post">Post</a> |
<a href="#addpost">Add a post</a>
<script type="text/ng-template" id="allposts.htm">
View
<select ng-model="viewby" ng-change="setItemsPerPage(viewby)">
<option>9</option>
<option>18</option>
<option>36</option>
<option>100</option>
</select> posts
<div layout="row">
<div class="col-sm-4" ng-class="getRowClass($index)"
ng-repeat="post in posts.slice(((currentPage-1)*itemsPerPage), ((currentPage)*itemsPerPage))">

<a href="#post">{{post.title}}</a>
<hr>
<p>{{post.body}}</p>
</div>
<div class="clearfix" ng-if="$index % 3 == 0"></div>

</div>
<ul uib-pagination total-items="totalItems" ng-model="currentPage" class="pagination-sm"
items-per-page="itemsPerPage"></ul>

</script>
<script type="text/ng-template" id="post.htm">
</script>
<script type="text/ng-template" id="addpost.htm">
</script>
<div ng-view></div>
</body>
</html>




Answer

The problem is that you are mixing Angular Material with Bootstrap classes. The following example only uses the Bootstrap.

The next problem is that when you make a grid for example if you are use 6 divisions with .col-xs-4 class in a single .row element then those will adjust automatically only if the height of all 6 divisions are same. Otherwise, the view will be messed up like in your example.

To fix this issue, you have to create multiple .row element so for this either you can write a directive to do it in the view itself or collate the list before passing it to the view.

The following example is doign it in the controller itself (i.e. before passing to the view):

// Helper function to collate a list
Array.prototype.collate = function(collateSize) {
    var collatedList = [];

    if (collateSize <= 0) {
        return [];
    }
    angular.forEach(this, function(item, index) {
        if (index % collateSize === 0) {
            collatedList[Math.floor(index / collateSize)] = [item];
        } else {
            collatedList[Math.floor(index / collateSize)].push(item);
        }
    });

    return collatedList;
};

var myApp = angular.module('myApp', ['ngRoute', 'ui.bootstrap']);

myApp.config(function($routeProvider) {
    $routeProvider.when('/', {
        templateUrl: 'allposts.htm',
        controller: 'PostsController'
    }).when('/post', {
        templateUrl: 'post.htm',
        controller: 'PostController'
    }).when('/addpost', {
        templateUrl: 'addpost.htm',
        controller: 'AddController'
    }).otherwise({
        redirectTo: '/'
    });
});

myApp.controller('PostsController', function($scope) {});

myApp.controller('PostController', function($scope) {});

myApp.controller('AddController', function($scope) {});


myApp.controller('controller', function($scope, $http) {
    $scope.title = "Kansi app";
    $http({
        method: 'GET',
        url: "http://jsonplaceholder.typicode.com/posts"
    }).then(function(response) {
        $scope.posts = response.data;
        $scope.viewby = 9;
        $scope.totalItems = $scope.posts.length;
        $scope.currentPage = 1;
        $scope.itemsPerPage = $scope.viewby;
        $scope.maxSize = 5;
        $scope.collatedPosts = getCollatedPosts($scope.posts);
    });

    function getCollatedPosts(posts) {
        if (!posts) {
            return [];
        }

        var paginatedPosts = posts.slice((($scope.currentPage - 1) * $scope.itemsPerPage), (($scope.currentPage) * $scope.itemsPerPage));
        return paginatedPosts.collate(3);
    }

    $scope.setPage = function(pageNo) {
        $scope.currentPage = pageNo;
    };

    $scope.setItemsPerPage = function(num) {
        $scope.itemsPerPage = num;
        $scope.currentPage = 1; //reset to first page
        $scope.collatedPosts = getCollatedPosts($scope.posts);
    };

    $scope.pageChanged = function(currentPage) {
        $scope.currentPage = currentPage;
        $scope.collatedPosts = getCollatedPosts($scope.posts);
    };
});
.row {
  /* Using red border just for understanding */
  border-bottom: 2px solid red;
  margin-bottom: 10px;
  margin-top: 10px;
}
<html>

<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js'></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-route.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.1.3/ui-bootstrap-tpls.js"></script>
</head>

<body layout="column" ng-app="myApp" ng-cloak ng-controller="controller">
    <h1>{{title}}</h1>
    <a href="#post">Post</a> |
    <a href="#addpost">Add a post</a>
    <script type="text/ng-template" id="allposts.htm">
        View
        <select ng-model="viewby" ng-change="setItemsPerPage(viewby)">
            <option>9</option>
            <option>18</option>
            <option>36</option>
            <option>100</option>
        </select>posts
        <div class="row" ng-repeat="collatedPostList in collatedPosts">
            <div class="col-xs-4" ng-repeat="post in collatedPostList">

                <a href="#post">{{post.title}}</a>
                <hr>
                <p>{{post.body}}</p>
            </div>

        </div>
        <ul uib-pagination total-items="totalItems" ng-model="currentPage" class="pagination-sm"
            items-per-page="itemsPerPage" ng-change="pageChanged(currentPage)"></ul>

    </script>
    <script type="text/ng-template" id="post.htm">
  </script>
    <script type="text/ng-template" id="addpost.htm">
  </script>
    <div ng-view></div>
</body>

</html>