Count, First and Last in Each

Hi!

A way to know what iteration we’re in at in an Each would be nice. For instance a Count, First and Last parameter where Count is an int representing the iteration. And First and Last would be bools representing whether we’re in the first or last iteration.

You should be able to use Observable.map(function (x, i) { return x + " has index " + i }). that we shipped in todays release.

I don’t really understand how to use that.

Let’s say I have the code below, how would I use the code you describe above to for instance check if we’re on the first, last or nth element?

<JavaScript>
    var elements = [
        {"text": "hei"},
        {"text": "hade"}
    ];

    module.exports = {
        elements: elements
    }
</JavaScript>
<StackPanel>
    <Each Items="{elements}">
        <Text Value="{text}" />
    </Each>
</StackPanel>

Sorry, I read that to fast. Thought you where refering to Observable.forEach. I think we have some tickets regarding what you are asking about. I will take a look. But you can always just modify the elements array with the properties you need in UX, like adding an index.

Ah, okay :slight_smile:

Yeah, it’s a “solution” but I really don’t like doing things staticly by manipulating objects/arrays.

Something like this, with Anders’ solution:

<App Theme="Basic">
    <JavaScript>
      var Observable = require("FuseJS/Observable");
      var elem_text = Observable(
        {"text": "hei"},
        {"text": "hade"}
      );

      var elements = elem_text.map(function (x, i) { 
          x.count = elem_text.count();
          x.i = i;
          return x; 
      });
      module.exports = {
        elements: elements
      }
    </JavaScript>
    <StackPanel>
      <Each Items="{elements}">
          <StackPanel>
          <Match Value="{i}">
              <Case Number="0">
                  <Text Value="FIRST!" />
              </Case>
              <Case Number="{count}">
                  <Text Value="LAST!" />
              </Case>
          </Match>
        <Text Value="{text}" />
        <Text Value="{i}" />
        </StackPanel>
      </Each>
    </StackPanel>
</App>

But it didn’t work as you are unable to reference count with the <Case>

It’s also a bit unfortunate that JS doesn’t have a simple way to clone objects; in that example you’re adding count and i fields to the incoming objects and returning those instead of returning new ones. Which is probably ok, but it’s important to be aware of. I can imagine some really nasty problem cases arising this way.

Ah, anders basically said that, and I guess bolav wrote the example with that in mind, so what I said is basically superfluous :slight_smile: Probably good to have the reminder tho, hehe.

Another suggestion might be to build a “wrapper” object in map with the extra data you need and “unwrap” it in Each using Select. Then you don’t have to feel icky because you’re not “polluting” your original data :slight_smile: . In that case, bolav’s example can be modified like this:

JS:

var elements = elem_text.map(function (x, i) {
  return {
    element: x,
    max_index: elem_text.count() - 1,
    index: i
  };
});

UX:

<Each Items="{elements}">
  <StackPanel>
    <Match Value="{index}">
      <Case Number="0">
        <Text Value="FIRST!" />
      </Case>
      <Case Number="{max_index}">
        <Text Value="LAST!" />
      </Case>
    </Match>
    <Select Value="{element}">
      <Text Value="{text}" />
      <Text Value="{index}" />
    </Select>
    ...

I haven’t tested this code, but it should work just fine. Note that I think the reason why bolav had an issue with binding to count was actually because the index would never reach that value, being 0-indexed. I’ve [hopefully] fixed this and renamed the count field to max_index in this example to illustrate the difference :slight_smile:

The issue seems to be that you cannot reference data sources with <Case>. The below code is pretty much identical to Jake’s example (see above) but it generates the following error:

Object reference not set to an instance of an object. System.NullReferenceException occured.

The code used to get the above error is pasted below:

MainView.js

var Observable = require("FuseJS/Observable");

var elem_text = Observable(
    {"text": "hei"},
    {"text": "hade"}
);


var elements = elem_text.map(function (x, i) {
    return {
        element: x,
        max_index: elem_text.count() - 1,
        index: i
    };
});

module.exports = {
    elements: elements
};

MainView.ux

<App Theme="Basic" Background="#eeeeeeff">
    <DockPanel>
        <JavaScript File="MainView.js" />

        <StackPanel>
            <Each Items="{elements}">
                <StackPanel>
                    <Panel>
                        <Match Value="{index}">
                            <Case Number="0">
                                <Text Value="FIRST!" />
                            </Case>
                            <Case Number="{max_index}">
                                <Text Value="LAST!" />
                            </Case>
                        </Match>
                    </Panel>
                    <Text Value="{element.text}" />
                    <Text Value="{index}" />
                </StackPanel>
            </Each>
        </StackPanel>

    </DockPanel>
</App>

Where it says (see MainView.js)

max_index: elem_text.count() - 1,

It should be

max_index: elem_text.length - 1,

My current work around is to use WhileFloat but not sure if this is the correct way to go about it. Especially if the value will not be changed.

MainView.ux

<App Theme="Basic" Background="#eeeeeeff">
    <DockPanel>
        <JavaScript File="MainView.js" />

        <StackPanel>
            <Each Items="{elements}">
                <StackPanel>
                    <Panel>
                        <WhileFloat Value="{index}" LessThan="1">
                            <Text Value="FIRST!" />
                        </WhileFloat>
                        <WhileFloat Value="{index}" GreaterThanEqual="{max_index}">
                            <Text Value="LAST!" />
                        </WhileFloat>
                    </Panel>
                    <Text Value="{element.text}" />
                    <Text Value="{index}" />
                </StackPanel>
            </Each>
        </StackPanel>

    </DockPanel>
</App>

skezo: Your solution with WhileFloat is fine.

Your example runs fine here, doesn’t crash. What version are you running, and what platform are you building for? Have you tested this in 0.10 ?

Running on Windows 10.

Using Fuse version 0.9.11 (build 5893) the below code creates an error.

Using Fuse version 0.10.0 (build 5986) the below code works as expected

<App Theme="Basic" Background="#eeeeeeff">
    <DockPanel>
        <JavaScript File="MainView.js" />

        <StackPanel>
            <Each Items="{elements}">
                <StackPanel>
                    <Panel>
                        <Match Value="{index}">
                            <Case Number="0">
                                <Text Value="FIRST!" />
                            </Case>
                            <Case Number="{max_index}">
                                <Text Value="LAST!" />
                            </Case>
                        </Match>
                    </Panel>
                    <Text Value="{element.text}" />
                    <Text Value="{index}" />
                </StackPanel>
            </Each>
        </StackPanel>

    </DockPanel>
</App>

Ok, good to hear that it is fixed in 0.10.