5

New to Nunjucks and have a similar problem to here but I couldn't get my code to work. I'm trying to loop through the first 5 items in a Nunjucks loop of 14 items. So far I've found that the range function should be able to accomplish this but can't get the syntax right. It seems like I'm pointing to the index incorrectly.

My code to get all 14 items is:

    {% for images in index %}
      <div class="spacer col-md-2 col-sm-6">
      </div>
      <div class="yellp-img col-md-2 col-sm-6">
        <img src="/uploads/images/{{ images.image.filename }}" />
      </div>
    {% endfor %}

This prints all 14 images in index. I could also print 14 images using the following:

    {% for images in range(0, index.length) -%}
      <div class="spacer col-md-2 col-sm-6">
      </div>
      <div class="yellp-img col-md-2 col-sm-6">
        <img src="/uploads/images/{{ images.image.filename }}" />
      </div>
    {%- endfor %}

The problem is then all the images are broken (printed without the filename in src url) as follows:

<img src="/uploads/images/" />

This is probably obvious but I can't figure out how to limit how many images print with the data from the filename.


UPDATE (responding to Aikon's comment below): Image data is stored as JSON (loaded through Express/Node using Keystonejs CMS). A console log of the data loaded from Express is as follows:

    images={ _id: 59acf4ef822f172bc92ceaf9,
  __v: 0,
  image: 
   { filename: 'b8LMOFEstFE0K8eW.png',
     size: 7070,
     mimetype: 'image/png' } },{ _id: 59acf58d822f172bc92ceafa,
  __v: 0,
  image: 
   { filename: 'SZSJDneW0l3DumOz.png',
     size: 10070,
     mimetype: 'image/png' } },{ _id: 59acf6a4822f172bc92ceafb,
  __v: 0,
  image: 
   { filename: 'CLlGDaqZv6gBDt1B.png',
     size: 9235,
     mimetype: 'image/png' } },{ _id: 59acf751822f172bc92ceafc,
  __v: 0,
  image: 
   { filename: 'x-K9if9xSIaDFD-0.png',
     size: 8670,
     mimetype: 'image/png' } },{ _id: 59acf7ac822f172bc92ceafd,
  __v: 0,
  image: 
   { filename: '4dTpPFWD3nqCKqcr.png',
     size: 11181,
     mimetype: 'image/png' }

That results from the following code in my keystone view to load the image data via Express/Node from MongoDB:

    // Load the current post
view.on('init', function(next) {

    var images = keystone.list('ImageUpload').model.find()
        .sort('-createdAt')
        .limit(5)
        .populate('images');

    images.exec(function(err, result) {
        if (result !== null) {
            var images = result;
            console.log('images=' + images);
        } else {
            console.log('404!!!');
            return res.status(404).render('errors/404');
        }
        next(err);
    });

});

// Load All Images
view.query('index', keystone.list('ImageUpload').model.find());

// Render the view
view.render('index');

So index is referring to the current view, not the database model. Hopefully that clarifies.

Yawner
  • 93
  • 2
  • 10
  • How do you store image data inside `index`? – Aikon Mogwai Sep 05 '17 at 21:09
  • Updated the post to show how image data is stored – Yawner Sep 06 '17 at 04:32
  • @KyleMit `slice` actually returns a list of lists. I thought this might work `{%- for items in arr | slice(3) | first %}`, but `slice` sliced the list the other way so it doesn't give the required result :( – prtksxna May 26 '22 at 09:17
  • Thanks @prtksxna, going to delete my comment because it's wrong, but linked this post to another where you can use slice on the actual array (instead of as a filter) like this `arr.slice(0,3)` – KyleMit May 26 '22 at 15:22

2 Answers2

5

You can use the loop.index value (documentation) to limit the numbers in a portable way:

{% for images in index %}
  {% if (loop.index <= 5) %}
    <div class="spacer col-md-2 col-sm-6">
    </div>
    <div class="yellp-img col-md-2 col-sm-6">
      <img src="/uploads/images/{{ images.image.filename }}" />
    </div>
  {% endif %}
{% endfor %}
Stephane Rodet
  • 543
  • 5
  • 5
3
// Nunjucks
{% set length = images.length if images.length < 13 else 13 %} 
{% for i in range(0, length) %}
<img src="/uploads/images/{{images[i].image.filename}}" />
{% endfor %}

// Node.js + Nunjucks
var nunjucks  = require('nunjucks');
var env = nunjucks.configure();

var images = [{ 
    _id: '59acf4ef822f172bc92ceaf9',
    __v: 0,
    image: { 
        filename: 'b8LMOFEstFE0K8eW.png',
        size: 7070,
        mimetype: 'image/png' }
    }, { 
    _id: '59acf58d822f172bc92ceafa',
    __v: 0,
    image: { 
        filename: 'SZSJDneW0l3DumOz.png',
        size: 10070,
        mimetype: 'image/png' } 
    },{ 
    _id: '59acf6a4822f172bc92ceafb',
    __v: 0,
    image: { 
        filename: 'CLlGDaqZv6gBDt1B.png',
        size: 9235,
        mimetype: 'image/png' } 
    }];

// You can prepare data before pass it to Nunjucks
// var filenames = images.map((e) => e.image.filename);

res = nunjucks.renderString(`
    {% set length = images.length if images.length < 13 else 13 %} 
    {% for i in range(0, length) %}
    <img src="/uploads/images/{{images[i].image.filename}}" />
    {% endfor %}`, 
    {images: images} // Imho, assign data to `images`-var is more readable than `index`.
); 
console.log(res);
Aikon Mogwai
  • 4,954
  • 2
  • 18
  • 31
  • Thanks although I think it would be more clear for others if we remove the node.js related stuff, since my original question was just about the Nunjucks templating code part. I also agree that setting the variable to images is more readable but didn't make sense in my views (index is referencing the view, not just the images). Maybe we should remove the node.js parts to make it more helpful for others? – Yawner Sep 09 '17 at 00:16