Use template inside EACH

I need to create tabs dynamically based on data fetch from a dB so the number of tabs may change according to the data. The Tabs example (reported below) would be just fine:

    <Grid Dock="Top" ColumnCount="3" Height="50" Background="#bdc3c7">
        <Panel ux:Name="page1Tab">
            <Tab Text="Page 1">
                <Clicked>
                    <Set navigation.Active="page1" />
                </Clicked>
            </Tab>
        </Panel>
        <Panel ux:Name="page2Tab">
            <Tab Text="Page 2">
                <Clicked>
                    <Set navigation.Active="page2" />
                </Clicked>
            </Tab>
        </Panel>
        <Panel ux:Name="page3Tab">
            <Tab Text="Page 3">
                <Clicked>
                    <Set navigation.Active="page3" />
                </Clicked>
            </Tab>
        </Panel>
    </Grid>

    <PageControl ux:Name="navigation">
        <Page ux:Name="page1" Background="#eee">
            <WhileActive Threshold="0.5">
                <Set indicator.LayoutMaster="page1Tab" />
            </WhileActive>
            <WelcomeText>Welcome to Page 1</WelcomeText>
        </Page>
        <Page ux:Name="page2" Background="#abb7b7">
            <WhileActive Threshold="0.5">
                <Set indicator.LayoutMaster="page2Tab" />
            </WhileActive>
            <WelcomeText>Welcome to Page 2</WelcomeText>
        </Page>
        <Page ux:Name="page3" Background="#f2f1ef">
            <WhileActive Threshold="0.5">
                <Set indicator.LayoutMaster="page3Tab" />
            </WhileActive>
            <WelcomeText>Welcome to Page 3</WelcomeText>
        </Page>
    </PageControl>

but I don’t understand how I can modify it so that tabs are added dynamically to the UX markup. For example let’s say I have the following data fetched and exported:

    module.exports = {
       datas: ["one", "two", "three"],
       num_datas: num_datas
   };

   num_datas = datas.lenght; 

datas should be the tabs name and num_datas is going to be used to render the grid so, taking the above example, the code may be changed like this:

<Grid Dock="Top" ColumnCount="{num_datas}" Height="50">
    <Each Items="{datas}">
        <Panel ux:Name="page1Tab">
            <Tab Text="{}">  
                <Clicked>
                    <Set navigation.Active="page1" />
                </Clicked>
            </Tab>
       </Panel>
   </Each>
</Grid>

This works fine as the tabs name correspond to the fetch data datas, but the problem I don’t know how to solve is how to change dynamically the strings page1Tab, page2Tab, page3Tab and the page1, page2, page3 so that the tabs may react correctly on the clicked event.

I guess I have to use templates but reading the docs and trying many times did not helped me.

Hi Enrico,

please see if this recent thread solves it for you.

Note that on the website there are several examples that have data-bound pages with tabbed navigation, so check them out.

Thank you Uldis.
Your post helped me a lot but although I’m spending a lot of time on the code I’m still stuck on a part of my issue.

Your post works as I wish but now I’m trying to modify it so that it reacts to some data which I fetch from an API. The data has the following structure and I would like to give to the tabs the names cinema and shows while the other data would be the content of the page inside a ScrollView. The strings cinema and shows are dynamic and I can’t “hard code” them. They must be fetch from the API.

The problem is that I don’t understand how to set the tab name as said (set correctly the Observable and data-bind it).

Thank you.

var pages = Observable(
    {
        "cinema":
        [
            {"name": "Faucibus tempus", "title": "Pharetra pretium nisl"},
            {"name": "Lacinia sed semper", "title": "Enim nisl risus scelerisque"}
        ],
        "shows":
        [
            {"name": "Pharetra pretium", "title": "Eget cubilia porttitor"},
            {"name": "Posuere a vehicula", "title": "Non ultrices tincidunt"}
        ]
    }

Enrico, please provide a complete, minimal reproduction, otherwise it’s impossible to help.

Make it just like the one I supplied in the other thread - a single-UX-file app, copy-paste and run.

Hi Uldis,

Below I took your post and I changed the structure of the Observable pages. I’m trying to set the Tabs name so that match the strings cinema and shows. As said these strings should not be hardcoded as pages here represent data fetched from an API so the name of the tabs and the number of the tabs may change. The other data (title and subtitle) represent the content of the page.

Thank you.

<App>
    <JavaScript>
	var Observable = require("FuseJS/Observable");
	var pages = Observable(
		{
			"cinema":
			[
				{"name": "Faucibus tempus", "title": "Pharetra pretium nisl"},
				{"name": "Lacinia sed semper", "title": "Enim nisl risus scelerisque"}
			],
			"shows":
			[
				{"name": "Pharetra pretium", "title": "Eget cubilia porttitor"},
				{"name": "Posuere a vehicula", "title": "Non ultrices tincidunt"}
			]
		}
	);

	module.exports = {
		pages: pages,
		pageCount: pages.length
	}
    </JavaScript>

    <Page ux:Class="MyPage">
        <string ux:Property="Title" />
        <StackPanel>
            <Text Value="{title}" Color="#FFFFFF" />
            <Text Value="{subtitle}" Color="#FFFFFF" />
        </StackPanel>
    </Page>

    <Panel ux:Class="Tab">
        <string ux:Property="Text" />
        <Text Value="{ReadProperty Text}" Color="#FFF" Font="Bold" Alignment="Center" TextWrapping="Wrap" />
    </Panel>

    <ClientPanel Color="#64b5f6">

    <Panel Dock="Top" Height="56">
        <ScrollView AllowedScrollDirections="Horizontal">
            <PageIndicator Navigation="pages">
                <StackLayout Orientation="Horizontal" />
                <DockPanel ux:Template="Dot" Width="80" HitTestMode="LocalBounds">
                    <ActivatingAnimation>
                        <Change indicator.Opacity="1" />
                    </ActivatingAnimation>
                    <Rectangle ux:Name="indicator" Dock="Bottom" Height="4" Color="#6c7a89" Opacity="0" />
                    <Tab Text="{Page Title}" />
                    <Clicked>
                        <NavigateTo Target="{Page Visual}" />
                    </Clicked>
                </DockPanel>
            </PageIndicator>
        </ScrollView>
    </Panel>

    <PageControl ux:Name="pages">
        <Each Items="{pages}">
            <MyPage Title="{title}" />
        </Each>
    </PageControl>

</ClientPanel>

Refer to the PageTabs example to see how to include custom values in the page resources.

In your case you will end up with this on the MyPage class:

<ResourceFloat4 Key="movieTitle" Value="{title}"/>

Then in the indicator template use {Page movieTitle} to get at that value.

I guess a part of the question was still if it’s possible to access the key names in UX. A direct answer to that is a “no”. Enrico, you should look into refactoring that pages Observable to be a list instead of a single-value Observable, otherwise the Each simply won’t work.

Thank you for the support, edA-qa mort-ora-y.

Yes Uldis, you are right. I was trying to acces the key name in UX. As I’m fetching data from a dB with an API I made by myself I can change it easily. Do you have any suggestion how to refactor pages (which would be the result of fetch)?

It should be as easy as reading about Observable.replaceAll and other Observable list functions. If your API would return a JSON array, you could directly call pages.replaceAll(result) with the API response.

This example shows one way you can approach it.

Hope this helps!