3

I am using Jade and I would like to make a row every n items so something like

.row
  .product
  .product
  .product
.row

This seems to do something like what I want...

for p in products
  - var klass = (i % 3 == 0?"row product simpleCart_shelfItem col-md-4":"product simpleCart_shelfItem col-md-4")
  - i++
  div(class=klass)
    .. more product stuff

This isn't what I want though because the .row item is added to the same div. Is there a way I can do this without writing the product stuff n times?

Jackie
  • 21,969
  • 32
  • 147
  • 289

3 Answers3

3

Usually the approach of performing that logic inside Jade template does not work very well.

Much more robust approach would be to split an array of products into two dimensional array i.e. array of arrays of products inside request handler method.

So let's assume you have the following array of products (products) in request handler method. You can convert it to two dimensional array (products2D) and pass it as a parameter to your Jade template.

Example:

function arrayTo2DArray (list, howMany) {
    var result = []; input = list.slice(0); 
    while(a[0]) { 
        result.push(a.splice(0, howMany)); 
    }
    return result;
}

var handler = function(req, res) {
   var products = [ ... ] // fetched from DB
   var products2D = arrayTo2DArray(products, 3)

   res.render('template', { products: products2D });
}

Inside Jade template you can render over arrays (that will generate .row elements) and then inside .row element you can iterate over products generating .product elements.

Example:

for prodGroup in products2D 
  .row
    ...
    for p in prodGroup
      .product
        ... more product stuff

You can refer to this post for more examples of converting an array into 2 dimensional array.

I hope that will help.

Tom
  • 26,212
  • 21
  • 100
  • 111
  • Your code has an error. The copy of the list is assigned to a variable named `input`, but the while loop operates on a variable named `a`. You seem to have the same error in [your blog entry](http://blog.tompawlak.org/array-conversion-2-dimensional-javascript) You're also not declaring the variable you use for the copy of the list. I don't think that's actually an error, but wasn't this a bad idea even when you posted this in 2014? [This](https://stackoverflow.com/a/16007360/2948042) goes much deeper into the interpreter's handling of undeclared variables. I tend to just avoid them. – Vince Apr 16 '18 at 10:54
2

The code has an error. This works for me:

function arrayTo2DArray (list, howMany) {
    var result = [], a = list.slice(0);
    while(a[0]) { 
        result.push(a.splice(0, howMany)); 
    }
    return result;
}
Leon Schwanitz
  • 223
  • 1
  • 8
2

If the typo is corrected, Tom's answer is essentially correct, but I would take a different approach.

The reason you want to break the list up into groups is that you want it to look a certain way. So I think this should definitely stay in the view.

The necessary JavaScript code can run just as easily inside the Pug file as an unbuffered code block.

Finally, JavaScript has improved a bit and we can take advantage of some features that may not have been available when this question was written. Here's an example.

-
  let i = 0
  rows = products.reduce((accumulator, currentValue, currentIndex) => {
    if (currentIndex > 0 && currentIndex % 3 === 0) i++
    if (!Array.isArray(accumulator[i])) accumulator[i] = []
    accumulator[i].push(currentValue)
    return accumulator
  }, [])

each row in rows
  .row
    each product in row
      .product.simpleCart_shelfItem.col-md-4
        p= product.name

I know this is an old question, but I had the same question today. The best submitted answer led me down the right path, but it had a typo, it was outdated, and it broke my idea of separating the data from the view. This is what worked for me.

Vince
  • 3,962
  • 3
  • 33
  • 58