1

Okay, I'm working with a bit of a challenging grid here and need some help. I need to find a way to populate the data by ng-repeat. I'll attach an image of the grid layout of what I'm trying to achieve and the order of the data should populate. I have done the layout in HTML in the following method. But can't find a way to do it by ng-repeat.

ROW 1 - normal bootstrap col-md-4 and col-md-8

ROW 2 - I've done this row using CSS3 Flexbox. You can have al look at the code below.

ROW 3 - normal bootstrap col-md-4 and col-md-8

enter image description here

This is a plunker of what I've been trying to do. [https://plnkr.co/edit/NtXEnQcKpM0JxnLsxc4m?p=preview]2

I'll also try to convey the logic behind this. So at the bottom of the grid there will be a load more button to repeat this entire layout again and populate the data. Hence I've planned to put and ng-repeat on the container. Following this I've divided the layout in 3 rows. - the first one consist of columns 1 and 2 - the second row consist of columns 3 to 7 - the third row consist of columns 8 and 9

The array returning as well is divided the same way.

I hope I get some clarity if my approach to this is wrong and maybe a work-around to achieve this layout.

16
  • 1
    have you looked at ng-repeat-start and ng-repeat-end? Commented Mar 4, 2017 at 7:41
  • I'm just getting started with AngularJS. I'll have a look at this. Thanks for your input. Commented Mar 4, 2017 at 7:47
  • Is this duplicate of stackoverflow.com/questions/19839743/nested-ng-repeat? Commented Mar 4, 2017 at 7:49
  • 1
    Here's a working fiddle rather than your broken plunkr to show the repeats working: jsfiddle.net/93sozhkf Commented Mar 4, 2017 at 8:11
  • are you set on your data json in the format you've outlined? or would it be preferrable to not have to "pre-group" your data into rows? and instead be able to just provide a list to generate the design for each set of 9 elements? Commented Mar 4, 2017 at 8:12

2 Answers 2

2

To make the design you've outlined, I would suggest doing the following:

  1. Use a filter to "chunk" your articles into groups of 9 (similar to this memoized chunking solution)

  2. Use a custom directive to create your "template"

  3. Leverage a responsive fixed ratio grid system (like cough my library Perfect Bootstrap cough)

Example JSFiddle


Step1

Let's put your JSON back into a standard array, and then "chunk" it into batches of 9 at run time. We create an angular filter that we then apply using | chunk. To avoid an infinite digest loop, we'll need to memoize the result of the function using a variable, in the below code we use the string 'newsGroups'. If you intend to load more items into your array, say for infinite scroll, you'll need to update this variable in order to refresh the memoized result. Think of it like a cachebreak.

For brevity, i have stripped back your article array to be just an id and title item for each element. I would assume this is coming from an AJAX call in your development environment anyways.

HTML

<div class="container-fluid" ng-controller="MyCtrl">
  <div class="template-container" ng-repeat="newsGroup in NewsListing | chunk:'newsGroups'">
    <!-- content here -->
  </div>
</div>

JS Chunk filter and basic array of articles

var myApp = angular.module('myApp', []);
myApp.filter('chunk', function() {
  function cacheIt(func) {
    cache = {};
    return function(arg) {
      // if the function has been called with the argument
      // short circuit and use cached value, otherwise call the
      // cached function with the argument and save it to the cache as well then return
      return cache[arg] ? cache[arg] : cache[arg] = func(arg);
    };
  }

  // unchanged from your example apart from we are no longer directly returning this
  function chunk(items, chunk_size) {
    var chunks = [];
    if (angular.isArray(items)) {
      if (isNaN(chunk_size))
        chunk_size = 9;
      for (var i = 0; i < items.length; i += chunk_size) {
        chunks.push(items.slice(i, i + chunk_size));
      }
    } else {
      console.log("items is not an array: " + angular.toJson(items));
    }
    return chunks;
  }
  // now we return the cached or memoized version of our chunk function
  // if you want to use lodash this is really easy since there is already a chunk and memoize function all above code would be removed
  // this return would simply be: return _.memoize(_.chunk);

  return cacheIt(chunk);
});
myApp.controller('MyCtrl', function($scope) {
  $scope.NewsListing = [{
    "id": "71b85130-ffe4-11e6-81a4-c1bd97df0d2d",
    "title": "Exercitation ullamco laboris"
  }, {
    "id": "58847180-ffe4-11e6-81a4-c1bd97df0d2d",
    "title": "Duis aute irure dolor in reprehenderit"
  }, {
    "id": "35d2c290-ffe4-11e6-81a4-c1bd97df0d2d",
    "title": "Ut enim ad minim"
  }, {
    "id": "fdbdeb00-ffe3-11e6-81a4-c1bd97df0d2d",
    "title": "Ut enim ad minim"
  }, {
    "id": "df858f30-ffe3-11e6-81a4-c1bd97df0d2d",
    "title": "Dolore magna aliqua"
  }, {
    "id": "bbc619c0-ffe3-11e6-81a4-c1bd97df0d2d",
    "title": "Qui officia deserunt mollit anim"
  }, {
    "id": "8467e800-ffe3-11e6-81a4-c1bd97df0d2d",
    "title": "Consectetur adipisicing elit, sed do eiusm"
  }, {
    "id": "5aea9180-ffe3-11e6-81a4-c1bd97df0d2d",
    "title": "Lorem elit, sed do eiusm"
  }, {
    "id": "418d09c0-ffe3-11e6-81a4-c1bd97df0d2d",
    "title": "ALL YOU NEED TO KNOW ABOUT WAREHOUSE"
  }, {
    "id": "71b85130-ffe4-11e6-81a4-c1bd97df0d2e",
    "title": "Exercitation ullamco laboris2"
  }, {
    "id": "58847180-ffe4-11e6-81a4-c1bd97df0d2e",
    "title": "Duis aute irure dolor in reprehenderit2"
  }, {
    "id": "35d2c290-ffe4-11e6-81a4-c1bd97df0d2e",
    "title": "Ut enim ad minim2"
  }, {
    "id": "fdbdeb00-ffe3-11e6-81a4-c1bd97df0d2e",
    "title": "Ut enim ad minim2"
  }, {
    "id": "df858f30-ffe3-11e6-81a4-c1bd97df0d2e",
    "title": "Dolore magna aliqua2"
  }, {
    "id": "bbc619c0-ffe3-11e6-81a4-c1bd97df0d2e",
    "title": "Qui officia deserunt mollit anim2"
  }, {
    "id": "8467e800-ffe3-11e6-81a4-c1bd97df0d2e",
    "title": "Consectetur adipisicing elit, sed do eiusm2"
  }, {
    "id": "5aea9180-ffe3-11e6-81a4-c1bd97df0d2e",
    "title": "Lorem elit, sed do eiusm2"
  }, {
    "id": "418d09c0-ffe3-11e6-81a4-c1bd97df0d2e",
    "title": "ALL YOU NEED TO KNOW ABOUT WAREHOUSE2"
  }];
})

Step 2

In the content here comment section of the HTML in Step 1 we would apply a custom directive. In real-life development i would probably use a separate file to hold the template, however, for the constraints of the js fiddle i needed to define it as an text/ng-template script tag.

You can see that the newsGroup from the ng-repeat in Step 1 will be handed to our custom directive using the scope value items. This will contain at most 9 news articles.

HTML Directive call

<div nine-item-news items="newsGroup"></div>

JS directive definition

myApp.directive('nineItemNews', function() {
  return {
    restrict: 'A',
    replace: true,
    templateUrl: '/nine-item-news-template.html',
    scope: {
      items: '='
    }
  }
});

HTML TEMPLATE

<script type="text/ng-template" id="/nine-item-news-template.html">
<div class="template">
    <!-- template content here -->
</div>
</script>

Step 3

A while ago i developed a CSS library designed to provide responsive fixed ratio grid elements. This question has provoked me to finally make it public. Think of it like bootstrap's horizontal col- functionality, but vertically.

We replace the template content here comment above with the following html that will load in the items passed to it by the custom directive from Step 2. I would recommend you expand on this template to add ng-if statements to load/unload blocks, similarly, i would recommend using ng-class to adapt the design for the cases you arent handed a full 9 items (ie. what would it look like if only 6 articles were passed to it). Alternatively, using an ng-switch based on items.length and loading an entirely different template could similarly work.

The additional CSS is to add background colors and remove the default padding/margin for the bootstrap columns.

NB: Please note that in order to make the elements maintain their ratio, their overflow is set to hidden. This works particularly well when items have background images, but can have unintended consequences for text. My example includes a mobile responsivised template design that creates sections with a ration of 2:1. I encourage you to test this for your specific build carefully and a number of screen resolutions.

HTML template content

<div class="row" ng-if="items.length">
    <div class="col-xs-12 col-md-4 col-no-padding">
        <div class="row-xs-6 row-md-12">
            <div class="abs-inner bg-darkgrey">
                <p ng-bind="items[0].title"></p>
            </div>
        </div>
    </div>
    <div class="col-xs-12 col-md-8 col-no-padding">
        <div class="row-xs-6">
            <div class="abs-inner bg-grey">
                <p ng-bind="items[1].title"></p>
            </div>
        </div>
    </div>
</div>
<div class="row">
    <div class="col-xs-12 col-md-4 col-no-padding">
        <div class="row">
            <div class="col-xs-12 col-no-padding">
                <div class="row-xs-6">
                    <div class="abs-inner bg-grey">
                        <p ng-bind="items[2].title"></p>
                    </div>
                </div>
            </div>
            <div class="col-xs-12 col-no-padding">
                <div class="row-xs-6">
                    <div class="abs-inner bg-midgrey">
                        <p ng-bind="items[5].title"></p>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="col-xs-12 col-md-4 col-no-padding">
        <div class="row-xs-6 row-md-12">
            <div class="abs-inner bg-white">
                <p ng-bind="items[3].title"></p>
            </div>
        </div>
    </div>
    <div class="col-xs-12 col-md-4 col-no-padding">
        <div class="row">
            <div class="col-xs-12 col-no-padding">
                <div class="row-xs-6">
                    <div class="abs-inner bg-grey">
                        <p ng-bind="items[4].title"></p>
                    </div>
                </div>
            </div>
            <div class="col-xs-12 col-no-padding">
                <div class="row-xs-6">
                    <div class="abs-inner bg-midgrey">
                        <p ng-bind="items[6].title"></p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<div class="row">
    <div class="col-xs-12 col-md-4 col-no-padding">
        <div class="row-xs-6 row-md-12">
            <div class="abs-inner bg-darkgrey">
                <p ng-bind="items[7].title"></p>
            </div>
        </div>
    </div>
    <div class="col-xs-12 col-md-8 col-no-padding">
        <div class="row-xs-6">
            <div class="abs-inner bg-grey">
                <p ng-bind="items[8].title"></p>
            </div>
        </div>
    </div>
</div>

CSS

.col-no-padding{
  padding-left:0;
  padding-right:0;
}
.col-no-padding > .row{
  margin-left:0;
  margin-right:0;
}
.bg-darkgrey{
  background-color:#535353;
}
.bg-midgrey{
  background-color:#808080;
}
.bg-grey{
  background-color:#959595;
}
.bg-lightgrey{
  background-color:#d0d0d0;
}
.bg-white{
  background-color:#FFFFFF;
}
Sign up to request clarification or add additional context in comments.

Comments

0

I think the right way to do this in Angular should be a custom directive.

When the user loas more, there should be an entier layout again, so the directive should display the entire layout.

You just have to give the data in argument to the directive, display it as you want (like give the data in an array or something to get the index).

Let's call that directive layoutDirective.

Then you put this in yout html :

<div ng-repeat="array in arrays" ng-if="$index < limitDisplay">
  <layout-directive data=array> </layout-directive>
</div>

The only trick here is the ng-if directive

Where limitDisplay is an int incremented when someone press loadMore I'm not sure about the data binding written like this, I'll try to improve my answer later to help you create a good directive

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.