7
votes

I'm triyng to use the view helper inside my {{#each}} template blocks without using global paths (my controllers create and destroy their own views).

Examples. Given a view with a myList array property, and an itemButton child view:

This will work

<script type="text/x-handlebars" name="my-list-view">
{{#each myList}} <!-- App.myListView.myList -->

    {{view App.myListView.itemButton}} {{title}}

{{/each}}
</script>


This will not:

<script type="text/x-handlebars" name="my-list-view">
{{itemButton}} <!-- works fine outside the each -->
{{#each myList}}

    {{view itemButton}} {{title}} <!-- itemButton view not found -->

{{/each}}
</script>

I do not appear to be able to access the parent view from the each view helper (or in fact access anything other than the properties of the objects being iterated).

The hacky workarounds I've come up with are:

  • Add the view I want to use to the items I'm iterating over.

or

  1. Creating a collectionView in App.myListView
  2. Create an itemViewClass view in that collection view class
  3. Move the itemButton view inside the itemViewClass
  4. Replace {{#each}} with {{#collection}}

or

  • Create a custom handlebars helper for iteration.

Both of these options seem horrible. Surely there's a better alternative than creating 2 new classes (and nesting 4 views deep) just to iterate over a list, though. Is there a replacement handlebars helper I can use instead?


Workaround implementations

Option #1 : Modifing the content array

http://jsfiddle.net/FQEZq/3/ Disadvantages: Having to add the view to each model instance just for iteration.

Option #2 : Custom collection view

http://jsfiddle.net/ST24Y/1/ Disadvantages: Now you have two additional views that you do not need / want, and less control of markup. References from the child view to the parent instance now requires parentView.parentView.parentView.

2
Can you provide a jsFiddle example?ebryn

2 Answers

3
votes

#each is too limited for your requirements. You can make it work if you're willing to use a global path to the view you want to nest within the #each. Otherwise, your collection view approach is best. Adding the view to the model instance is likely to muddy your app design something fierce, so I would avoid that.

One idea to keep your templates clean is to take advantage of Ember.View properties like:

  • collectionView - Return the nearest ancestor that is an Ember.CollectionView
  • itemView - Return the nearest ancestor that is a direct child of an Ember.CollectionView
  • contentView - Return the nearest ancestor that has the property content.
0
votes

The big thing here - options.

Hooks for how you wish to use a template are available. These are:

<-- render templates/name.js -->
{{partial 'name'}}

<-- render views/name.js -->
{{view 'name'}}

<-- render views/name1.js with controllers/name2.js -->
{{render 'name1' 'name2'}}

<!-- see also: -->
{{output}}
{{output 'named'}}
{{yield}}

An example variant of your initial template showing 4 different options:

<script type='text/x-handlebars' data-template-name='my-list-view'>
  <h2>Option 1</h2>

  {{#each myList}}
    {{! the 2nd parameter will look for itemButtonController }}
    {{render 'itembutton' itemButton}}
  {{/each}}

  <h2>Option 2</h2>
  {{#each myList}}
    {{! using an Ember Component }}
    {{#item-button }}
      some static text OR {{dynamic}}
    {{/item-button}}
    <!-- in component/item-button.hbs add {{yield}} for where you want the static content to output -->
  {{/each}}

  <h2>Option 3</h2>
  {{#each myList}}
    {{! if the button is to be a link }}
    {{#link-to 'route' parameter tagName='button' classNames='btn'}}
      {{title}}
    {{/link-to}}
  {{/each}}

  <h2>Option 4</h2>
  <p>Ludicrous example showing almost every reference option!</p>

  {{! focus the context on subview data }}
  {{#with someArrayOrCollectionOfView}}
    {{! prepend type to add context - here returning back up to this view's properties }}
    {{#each view.myList}}
      {{! this is equivalent to someArrayOrCollectionOfView[x].name }}
      {{name}}

      {{! a button that hooks in route, model, and 2 controllers, and applies a target for the output when clicked }}
      {{#link-to 'page' controllers.page.nextPage target='outlet' tagName='button' disabledWhen=controller.disableNext }}
        {{model.buttonName}}
      {{/link-to}}
    {{/each}}
  {{/with}}

  {{! generic default target (automatic) }}
  {{outlet}}

  {{! example of a named target }}
  {{outlet 'footerlinks'}}
</script>

Mmmm... reference for further reading: Ember.Handlebars.helpers