How to use foreach with a special first element?
Asked Answered
C

4

18

If I have an observable array

foos = [{ name: "a" }, { name: "b" }, { name: "c" }]

on my viewmodel, I would like to render the following:

<ul>
  <li class="add-new-foo">Special stuff here</li>
  <li>a</li>
  <li>b</li>
  <li>c</li>
</ul>

I got pretty close with

<ul data-bind="template: { name: 'foo-template', foreach: foos }">
  <li class="add-new-foo">Special stuff here</li>
</ul>

<script id="foo-template" type="text/html">
  <li data-bind="text: name"></li>
</script>

But this ended up putting the .add-new-foo after the a, b, c.

Any ideas? In my case it's crucial to use Knockout foreach instead of jQuery template's {{each}}, because of the benefits mentioned in the Knockout docs.

Crossarm answered 20/3, 2011 at 21:52 Comment(0)
B
39

Looks like it's going to be possible with the new containerless control flow and foreach binding in KO 1.3 2.0:

<ul>
    <li>Static item</li>
    <!-- ko foreach: products -->
        <li data-bind="text: name"></li>
    <!-- /ko -->
</ul>

See this post for details: http://blog.stevensanderson.com/2011/08/31/knockout-1-3-0-beta-available/

Barrett answered 4/11, 2011 at 10:27 Comment(0)
M
10

As there is not currently a way to tell the template binding where to render the template, I don't see a cleaner way to do it right now other than something like:

<ul data-bind="template: { name: 'foo-template', foreach: foos, templateOptions: { first: foos()[0]} }">
</ul>

<script id="foo-template" type="text/html">
    {{if $item.first === $data}}
    <li class="add-new-foo">Special stuff here</li>
    {{/if}}
    <li data-bind="text: name"></li>
</script>

So, we are passing the first item in your array as templateOptions and checking for if the item that we are dealing with in the template is indeed the first.

Sample here: http://jsfiddle.net/rniemeyer/XuXcr/

Also templateOptions was added after 1.12 KO, so you would need current code. More info about templateOptions here.

Hope this helps.

Meraz answered 20/3, 2011 at 23:40 Comment(5)
Looks, well, not terrible. Especially if I used template composition to isolate the "real template" from the proxy one.Crossarm
In theory land, I'd say the best solution would be to add a <ko:bind-location /> tag (or similar), such that that tag in particular gets replaced with the results of data binding instead of filled. Then the <ul /> could contain both a <li class="add-new-foo" /> and a <ko:bind-location /> with the data-bind attribute value that currently lives on the <ul />. Maybe I'll try to fork KO and add that some time...Crossarm
One way of doing is to use the sort function of the arrays. you can define your own sort function where you can always put the special stuff on top.Subservience
I was thinking about suggesting adding the first item to the array, but it seems like his first item is a different animal and not really something that he would want to get mixed in with his observableArray (anything that operates on items in the array would have to know that this one was different and you would have to filter it out when going back to the server). Might work for him though.Meraz
Yup, the first one is really not part of the view model, but part of the view.Crossarm
F
4
<!-- ko if: $index() == 0 -->
your code
<!-- /ko -->
Falkirk answered 28/5, 2013 at 3:2 Comment(1)
This is the answer cause the question was "How to use a foreach without elements"Steverson
D
0

This:

<ul>
    <li>Static item</li>
    <!-- ko foreach: products -->
        <li data-bind="text: name"></li>
    <!-- /ko -->
</ul>

will not work in IE8. I'm leaning toward the template answer for that situation. Any other ideas?

EDIT: here is what worked in IE8 - knockout 2.2.1: using the options bindings as per the bottom of the following comment:

https://stackoverflow.com/a/16361750

Delfeena answered 9/5, 2014 at 3:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.