To make the design you've outlined, I would suggest doing the following:
Use a filter to "chunk" your articles into groups of 9 (similar to this memoized chunking solution)
Use a custom directive to create your "template"
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;
}