Collapsible accordion list with animation

Hello! I have a ScrollView with items and these items contain an x number of child items. I’m trying to make these child items collapsible with animation. The problem is that if I don’t define a preliminary height for the child items, the animation doesn’t work. So far I figured to make this work by calculating the height of the child items in JS (by knowing the number and height of the children), but I was wondering is there a more robust way to make this in UX?

<App>
  <JavaScript>

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

    var items = Observable();

    var children = [1,2,3,4,5];

    for (i=0;i<10;i++) {

      items.add({
        items : children
      });

    }

    module.exports = {
      items : items
    };

  </JavaScript>
  <ScrollView Padding="20">
    <StackPanel ux:Name="Parent">
      <Each Items="{items}">
        <StackPanel>
          <Text Value="PARENT" Padding="5">
            <Clicked>
              <Toggle Target="Reveal" />
            </Clicked>
          </Text>
          <StackPanel Padding="5" ux:Name="Children" Height="160" ClipToBounds="True">
            <WhileTrue ux:Name="Reveal">
              <Change Children.Height="0" Duration="0.25" Easing="QuadraticInOut"/>
              <Change Children.Visibility="Collapsed" Delay="0.25" />
            </WhileTrue>
            <Each Items="{items}">
              <Text Value="CHILD" Padding="5" />
            </Each>
          </StackPanel>
        </StackPanel>
      </Each>
    </StackPanel>
  </ScrollView>
</App>

Hi Matti,

there is a height() UX expression function available that you could use in UX. Alternatively, you could couple a LayoutAnimation with a changing LayoutRole - look those up in the docs.

Hope this helps!

Thank you! I tried replacing Height=“160” with Height=“height(this)”, but there was still no animation. Is this because the child items are not populated yet?

What you say you did makes no sense.

You should use the height() expression to measure the height of an element that only holds the items you want to show/hide; a nested StackPanel maybe? And you should only use it on a Change modifier, so that the calculation happens when you actually need to deviate from the rest state.

Ok, thank you!

Here’s something you could do with LayoutRole and LayoutAnimation. Not perfect, but should be a good start:

<App>
  <JavaScript>

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

    var items = Observable();

    var children = [1,2,3,4,5];

    for (var i=0;i<10;i++) {

      items.add({
        items : children
      });

    }

    module.exports = {
      items : items
    };

  </JavaScript>
  <ScrollView Padding="20">
    <StackPanel ux:Name="Parent">
      <Each Items="{items}">
        <StackPanel ClipToBounds="true">
          <LayoutAnimation>
            <Resize Vector="1" RelativeTo="SizeChange" Duration="0.25" />
            <Move Vector="1" RelativeTo="PositionChange" Duration="0.25" />
          </LayoutAnimation>
          <Text Value="PARENT" Padding="5">
            <Clicked>
              <Toggle Target="Reveal" />
            </Clicked>
            <WhileTrue ux:Name="Reveal">
                <Change Children.LayoutRole="Standard" />
                <Change Children.Visibility="Visible" />
            </WhileTrue>
          </Text>
          <StackPanel Padding="5" ux:Name="Children" LayoutRole="Inert" Visibility="Hidden">
            <Each Items="{items}">
              <Text Value="CHILD" Padding="5" />
            </Each>
          </StackPanel>
        </StackPanel>
      </Each>
    </StackPanel>
  </ScrollView>
</App>

Wow! Thanks a million Uldis!