1

I'm looking for a good way to bind grouped objects and render it to the grid. Here's a grid example:

| League / Country  | Canada     | United States     | Brazil   |
| 1                 | John, Sam  |                   | Tim      |
| 2                 | Liam       |                   | Robert   |
| 3                 |            | James, Peter, Tom | Den      |

And Player's model

DS.Model.extend({
    name: DS.attr(),
    country: DS.attr(),
    leagueId: DS.attr("number")
}); 

And data received from backend is:

[
  { name: "John", country: "Canada", leagueId: 1 },
  { name: "Sam", country: "Canada", leagueId: 1 },
  { name: "Tim", country: "Brazil", leagueId: 1 },
  { name: "Liam", country: "Canada", leagueId: 2 },
 ... 
]

I thought about having something following:

{{#each country in countries}}
    <tr>
      {{#each league in leagues}}
        <td>
          {{#each player in players}}
            {{#is player.country "==" country}}
              {{#is player.leagueId "==" league}}
                ... output player ..., e.g. {{ render 'player/card' player }}
              {{/is}}
            {{/is}}
          {{/each}}
        </td>
      {{/each}}
    </tr>
{{/each}}

But having filtering within template does not look good. Is there a good way to move it to controller?

What is the Ember-way to output list of players into such grid, so that it nicely bound and if I change country or league - player is rendered into proper cell?

fantactuka
  • 3,298
  • 19
  • 29

2 Answers2

1

You can write a bound helper for this as follows:

Ember.Handlebars.registerBoundHelper('filterByCountryAndLeague', 
  function(allPlayers, curCountry, curLeague) {
    var matched = allPlayers.filterBy("country", curCountry).
                             filterBy('leagueId', curLeague);

    return matched.mapBy('name').join(", ");
}, '@each.country', '@each.leagueId');

Inside the helper function, you are passing all players as well as current country and league being processed and are filtering the allPlayers array by the values passed in. (The @each... at the end makes sure that when the country/league update for the player - the info gets rerendered.)

Your template then can look something like this:

<table border='2'>
  {{! First row }}
  <tr>
    <td>League / Country</td>
      {{#each country in countries}}
        <td>{{country}}</td>
      {{/each}}
  </tr>

  {{! Leagues }}
  {{#each league in leagues}}
    <tr>
      <td>{{league}}</td>
      {{#each country in countries }}
        <td>
          {{ filterByCountryAndLeague model country league  }}
        </td>
      {{/each}}
    </tr>
  {{/each}}

</table>

Working demo here

Kalman
  • 8,001
  • 1
  • 27
  • 45
  • Yeah, that's a better way. Quick question: is there any way to have it as block? So that it's possible to do: ```{{#filterByCountryAndLeague... }} {{render 'player.card' player}} {{/filterByCountryAndLeague}}``` – fantactuka Jan 23 '15 at 09:10
1

Following up on your question in the comment:

is there any way to have it [bound helper] as block? So that it's possible to do: {{#filterByCountryAndLeague... }} {{render 'player.card' player}}

The answer is yes and no. So, as a bound helper? No. See the docs here

Bound helpers do not support use with Handlebars blocks or the addition of child views of any kind.

But... if you need to use a template to display each player's info, you can use a component instead of a bound helper.

App.FilteredPlayersComponent = Ember.Component.extend({
  allPlayers: null, 
  forCountry: null, 
  forLeague: null,
  filteredPlayers: function(){
    var allPlayers = this.get('allPlayers');
    var forCountry = this.get('forCountry');
    var forLeague = this.get('forLeague');
    var matched = allPlayers.filterBy("country", forCountry).
                             filterBy('leagueId', forLeague);
    return matched;
 }.property('allPlayers.@each.country', 'forCountry', 'forLeague')
});

Then, inside your component template, you can render a specialized template per each player:

<script type="text/x-handlebars" id="components/filtered-players">
  {{#each player in filteredPlayers}}
    {{ render "player.card" player }}
  {{/each}}
</script>  

<script type="text/x-handlebars" id="player/card">
  <b>{{player.name}}</b>
</script>

Working demo here

Kalman
  • 8,001
  • 1
  • 27
  • 45