5

I’m having a hard time figuring a way to render the following markup using Ember.Handlebars:

<div class="row-fluid">
  <div class="span4">Item #1 (row #1 / column #1)</div>
  <div class="span4">Item #2 (row #1 / column #2)</div>
  <div class="span4">Item #3 (row #1 / column #3)</div>
</div>
<div class="row-fluid">
  <div class="span4">Item #4 (row #2 / column #1)</div>
  <div class="span4">Item #5 (row #2 / column #2)</div>
  <div class="span4">Item #6 (row #2 / column #3)</div>
</div>
<div class="row-fluid">
  <div class="span4">Item #7 (row #3 / column #1)</div>
</div>

Using pure JavaScript, I’d have done something like this:

var array = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6', 'Item 7'],
    output = '<div class="row-fluid">';

for (var i = 0, j = array.length; i < j; i++) {
  output += '<div class="span4">' + i + '</div>';

  if ((i + 1) % 3 == 0) {
    output += '</div><div class="row-fluid">';
  }
}

output += '</div>';

Ideally, I’d put this in a custom Handlebars helper (thus removing the logic from the template) but Ember documentation only explains how to write simple helpers and I really don’t know how to write a more complex block helper without losing the property bindings.

Does anyone know the best way to use Twitter Bootstrap’s grid system with a collection of Ember models?

Thank you in advance! Best regards,

David

davidg
  • 651
  • 1
  • 7
  • 18

3 Answers3

4

G'day Dave

Instead of useing rows and div to make a block grid try using an "unordered list"

The HTML

<ul class="row-fluid block-grid-3">
  <li>Item #1 (row #1 / column #1)</li>
  <li>Item #2 (row #1 / column #2)</li>
  <li>Item #3 (row #1 / column #3)</li>
  <li>Item #4 (row #2 / column #1)</li>
  <li>Item #5 (row #2 / column #2)</li>
  <li>Item #6 (row #2 / column #3)</li>
  <li>Item #7 (row #3 / column #1)</li>
</ul>

Then the CSS would look like this.

ul.block-grid-3 {
   display: block;
   overflow: hidden;
   padding: 0;
   -webkit-box-sizing: border-box;
   -moz-box-sizing: border-box;
   box-sizing: border-box;
}
ul.block-grid-3 > li {
   width: 33.33333%;
   float: left;
   padding: 0 12px 12px;
   display: block;
   height: auto;
   -webkit-box-sizing: border-box;
   -moz-box-sizing: border-box;
   box-sizing: border-box;
}
ul.block-grid-3 > li:nth-of-type(3n+1) {
   clear: left;
}

Then if you wanted to change to four blocks you can can change the css to:

ul.block-grid-4 > li {
   width: 25%;
   float: left;
   padding: 0 12px 12px;
   display: block;
   height: auto;
   -webkit-box-sizing: border-box;
   -moz-box-sizing: border-box;
   box-sizing: border-box;
}  
ul.block-grid-4 > li:nth-of-type(4n+1) {
   clear: left;
}

jsfiddle example a more robust solution.

You can also check out this ember app Open-pos the products are layout using this method.

Zurb's css framework "foundation" as great solution called block-grid. This there system it is super easy to change the 3 up grid other number with just a small change in the css. you could drop in the block grid code into your bootstrap scss. If you have any questions let me know.

Cheers

kiwiupover
  • 1,782
  • 12
  • 26
  • Hey, Thank you so much for your response and especially for taking the time to set up a Fiddle! The markup you suggest is both simpler and more semantic than Bootstrap’s and usually I prefer to use something like this. But in this case, I’m working on a project for which Bootstrap is used and I wanted to stick to it. Moreover I think that it’s often a bad sign when the HTML must be tailored to the (front- or back-end) framework rather than the opposite, don’t you think? Anyway, thank you very much for your response and for these great examples! Have a good day! :) – davidg Apr 23 '13 at 08:33
2

For those interested, here is a pretty clean way to handle this scenario.

Here is the template:

{{#each post in posts}}
  {{#if post.first}}
    <div class="row-fluid">
  {{/if}}
    <div class="span4">
      <div class="post">
        {{post.title}}
      </div>
    </div>
  {{#if post.lastOfRow}}
    </div>
    <div class="row-fluid">
  {{/if}}
  {{#if post.last}}
    </div>
  {{/if}}
{{/each}}

And the corresponding controller:

App.PostsController = Ember.ArrayController.extend({
  posts: function () {
    var length = this.get('length');

    return this.map(function (post, i) {
      // Checks if it’s the last post
      if ((i + 1) == length) {
        post.last = true;
      } else {
        post.last = false;

        // Checks if it’s the first post
        if (i == 0) {
          post.first = true;
        } else {
          post.first = false;
        }

        // Checks if it’s the last post of a row
        if ((i + 1) % 3 == 0) {
          post.lastOfRow = true;
        } else {
          post.lastOfRow = false;
        }
      }

      return post;
    });
  }.property('content.@each')
});

This may also be useful to generate tables (with <td> nested in <tr>)… Even though I ended up using kiwiupover’s solution! ;-)

Regards,

D.

davidg
  • 651
  • 1
  • 7
  • 18
0

The accepted answer works well, but if you have posts that are dynamic the rows and spans will be re-drawn any time the posts model / property changes. This might be OK, if the content is small, but in my case I have a chart for each block and it's readily apparent when they all are redrawn at the same time.

In my case, I decided to override the bootstrap css; however, you would want to account for various screen sizes via additional media queries.

Here's the rules to target the largest grid and ensure that there are 3 span4s (note this was BS 2.3.2) correctly aligned on each "row", but inside a single row-fluid:

  .row-fluid [class*="span4"]:nth-of-type(3n+1) {
    margin-left: 0;
  }
  .row-fluid [class*="span4"]:nth-of-type(n+4) {
    margin-top: 10px;
  }
steakchaser
  • 5,198
  • 1
  • 26
  • 34