Passing Parameters Between UX Files

I have two UX files: Index and Search. Index has two text fields: From and To. They will be used as parameters for Search screen results.

Search performs a GET request to a REST service that retrieves the results in an Observable variable. Everything is working if I don’t split the files because all the variables is set at the same scope. My problem starts when I separate the files.

What is the correct way to provide these parameters to Search screen?

An example of the Observable GET request:

<JavaScript>
    var valueChanged = function (args) {
        fetch('http://transport.opendata.ch/v1/locations?from=&#39; + args.data.from + '&to=' + args.data.to)
            .then(function(response) { return response.json(); })
            .then(function(responseObject) { data.value = responseObject; });
            // Uncomment this line below to see the REST output.
            // console.log(data.stations);
    }
<JavaScript>

And the markup:

<ScrollView>
    <StackPanel>
        <Each Items="{data.stations}">
            <TrainItem />
        </Each>
    </StackPanel>
</ScrollView>

If the seperate file is still in the same scope those variables that are exported should still be accessible by the seperate file

You’re going to have to show some more code to debug this properly

If you can isolate your exact example and post both UX files (and any corresponding JS files) that would help alot.

As long as both are exporting the Observables, you should be able to access them across files using Index.From, Index.To, etc.

But seeing what you’re doing would be most helpful.

Okay, I missed the exports at Index.ux:

    function selectMeFrom(args) {
        selectedFrom.value = args.data.name;
    }

    function selectMeTo(args) {
        selectedTo.value = args.data.name;
    }

    module.exports = {
        data: data,
        selectedFrom: selectedFrom,
        selectedTo: selectedTo,
        selectMeFrom: selectMeFrom,
        selectMeTo: selectMeTo
    };

Markup:

<StackPanel>
    <Panel Margin="5">
        <Text Dock="Left" Alignment="VerticalCenter">From:</Text>
    </Panel>
    <Panel Height="40" Margin="5">
        <Rectangle>
            <Stroke Brush="#BFD8DD" Width="2"/>
        </Rectangle>
        <DropdownSelectedItem Text="{selectedFrom}" />
        <Clicked>
            <Toggle Target="expandDropdownFrom" />
        </Clicked>

        <WhileTrue ux:Name="expandDropdownFrom">
            <StackPanel Offset="0,40">
                <Each Items="{fromOptions}">
                    <DropdownOption Text="{name}" Clicked="{selectMeFrom}" />
                </Each>
            </StackPanel>
        </WhileTrue>
    </Panel>

    <Panel Margin="5">
        <Text Dock="Left" Alignment="VerticalCenter">To:</Text>
    </Panel>
    <Panel Height="40" Margin="5">
        <Rectangle>
            <Stroke Brush="#BFD8DD" Width="2"/>
        </Rectangle>
        <DropdownSelectedItem Text="{selectedTo}" />
        <Clicked>
            <Toggle Target="expandDropdownTo" />
        </Clicked>

        <WhileTrue ux:Name="expandDropdownTo">
            <StackPanel Offset="0,40">
                <Each Items="{toOptions}">
                    <DropdownOption Text="{name}" Clicked="{selectMeTo}" />
                </Each>
            </StackPanel>
        </WhileTrue>
    </Panel>
</StackPanel>

mrcl3an2008@gmail.com wrote:

If you can isolate your exact example and post both UX files (and any corresponding JS files) that would help alot.

As long as both are exporting the Observables, you should be able to access them across files using Index.From, Index.To, etc.

But seeing what you’re doing would be most helpful.

Following your suggestion, I tried this:

<JavaScript>
    var Observable = require("FuseJS/Observable");

    fetch('http://transport.opendata.ch/v1/locations?from=&#39; + index.selectedFrom + '&to=' + index.selectedTo)
            .then(function(response) { return response.json(); })
            .then(function(responseObject) { data.value = responseObject; });

Index.ux has this markup:

<StackPanel ux:Class="Index" ux:Name="index">

Gives me this error:

JS stack trace: ReferenceError: index is not defined
    at Search.ux:9:73
    at Search.ux:18:4

You cannot reference UX components from JS, so that won’t work.

  1. I would consider making your JS a separate file called Index.js.
  2. Your Index.ux can also be a separate ux file
  3. In your Main.js, make sure to var Index = require('Index');
  4. In your Main.ux make sure to <JavaScript File="Index.js" ux:Global="Index" />
  5. Export as normal from Index.js
  6. In your Main.js, export like so selectedFrom: Index.selectedFrom, selectedTo: Index.selectedTo etc.

This should allow it to work using separate files. Ultimately everything “acts” like it’s in one file, but you get some organizationto boot.

Okay, I did the following:

1. Created an Index.js file

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

var data = Observable();
var selectedFrom = Observable();
var selectedTo = Observable();

var fromOptions = Observable(
    { name: "Basel SBB" },
    { name: "Bern"},
    { name: "Zurich HB" }
);

var toOptions = Observable(
    { name: "Basel SBB" },
    { name: "Bern"},
    { name: "Zurich HB" }
);

console.log(fromOptions);

var searchCallback = function (args) {
    fetch('http://transport.opendata.ch/v1/locations?from=&#39; + selectedFrom + '&to=' + selectedTo)
        .then(function(response) { return response.json(); })
        .then(function(responseObject) { data.value = responseObject; });
        // Uncomment this line below to see the REST output.
        // console.log(data.stations);
}

function selectMeFrom(args) {
    selectedFrom.value = args.data.name;
}

function selectMeTo(args) {
    selectedTo.value = args.data.name;
}

module.exports = {
    data: data,
    fromOptions: fromOptions,
    toOptions: toOptions,
    selectedFrom: selectedFrom,
    selectedTo: selectedTo,
    selectMeFrom: selectMeFrom,
    selectMeTo: selectMeTo
    // valueChanged: valueChanged
};

2. Added this statement in MainView.ux

<JavaScript File="Index.js" ux:Global="IndexJs" />

3. Modified Index.ux

<StackPanel>
    <Panel Margin="5">
        <Text Dock="Left" Alignment="VerticalCenter">From:</Text>
    </Panel>
    <Panel Height="40" Margin="5">
        <Rectangle>
            <Stroke Brush="#BFD8DD" Width="2"/>
        </Rectangle>
        <DropdownSelectedItem Text="{IndexJs.selectedFrom}" />
        <Clicked>
            <Toggle Target="expandDropdownFrom" />
        </Clicked>

        <WhileTrue ux:Name="expandDropdownFrom">
            <StackPanel Offset="0,40">
                <Each Items="{IndexJs.fromOptions}">
                    <DropdownOption Text="{name}" Clicked="{IndexJs.selectMeFrom}" />
                </Each>
            </StackPanel>
        </WhileTrue>
    </Panel>

    <Panel Margin="5">
        <Text Dock="Left" Alignment="VerticalCenter">To:</Text>
    </Panel>
    <Panel Height="40" Margin="5">
        <Rectangle>
            <Stroke Brush="#BFD8DD" Width="2"/>
        </Rectangle>
        <DropdownSelectedItem Text="{IndexJs.selectedTo}" />
        <Clicked>
            <Toggle Target="expandDropdownTo" />
        </Clicked>

        <WhileTrue ux:Name="expandDropdownTo">
            <StackPanel Offset="0,40">
                <Each Items="{IndexJs.toOptions}">
                    <DropdownOption Text="{name}" Clicked="{IndexJs.selectMeTo}" />
                </Each>
            </StackPanel>
        </WhileTrue>
    </Panel>
</StackPanel>

But now I have an infinite loop when I try to show Index.ux. Basically, the message shown is:

Object reference not set to an instance of an object

Apparently, the DropDowns are not receiving the callbacks selectMeFrom and selectMeTo. I replaced again the JS code inside the UX and the DropDown came back working.

Index.ux should reference selectFrom, not IndexJS.selectFrom. That is the way you’re exporting it.

The only reason you’d reference it that way, and only in your Main.js is if your functions needed to reference it there. I thought that was the problem.

mrcl3an2008@gmail.com wrote:

Index.ux should reference selectFrom, not IndexJS.selectFrom. That is the way you’re exporting it.

The only reason you’d reference it that way, and only in your Main.js is if your functions needed to reference it there. I thought that was the problem.

Believe ou not, this was not the problem. I think it’s a bug. I want your opinion.

a. This way doesn’t work

<StackPanel ux:Class="Index">
    <JavaScript File="Index.js" ux:Global="IndexJs" />

b. This way also doesn’t work

<StackPanel ux:Class="Index">
    <StackPanel>
        <JavaScript File="Index.js" ux:Global="IndexJs" />

c. This way works

<StackPanel ux:Class="Index">
    <StackPanel>
        <JavaScript File="Index.js" />

Weird… Although all my JS includes are in my Main.ux at the top. Not in my individual ux files.

Option C should be correct. What do you mean is a bug?

Anders Bondehagen wrote:

Option C should be correct. What do you mean is a bug?

As the documentation says:

Specifying the ux:Class attribute on an UX markup node defines a new class. A class is a reusable component that can be instantiated elsewhere in your project.

Well, I really don’t know if this should be considered as a bug or not. I think it’s more by my lack of experience with Fuse and also the lack of examples.

In fact I’ll need to require() this JS global resource every time I need these parameters, but please note I’d spend two days to make all this mechanism to work (thanks, @Mike, for the hints, finally I’ve got it). The outcome is:

First Step: Declare the JS as a Global Module at MainView.ux

<App Theme="Basic" Background="#eeeeeeff">
    <JavaScript File="Index.js" ux:Global="IndexJs" />

    ...
</App>

Second Step: require() the Module at Index.ux

<StackPanel ux:Class="Index">
    <JavaScript>
        var IndexJs = require('IndexJs');

          module.exports = {
          data: IndexJs.data,
          fromOptions: IndexJs.fromOptions,
          toOptions: IndexJs.toOptions,
          selectedFrom: IndexJs.selectedFrom,
          selectedTo: IndexJs.selectedTo,
          selectMeFrom: IndexJs.selectMeFrom,
          selectMeTo: IndexJs.selectMeTo
          };
      </JavaScript>
      ...

</StackPanel>

Third Step: Also require() the Module at Search.ux

<StackPanel ux:Class="Search" ux:Name="search">
    <JavaScript>
        var Observable = require("FuseJS/Observable");
        var IndexJs = require('IndexJs');

        console.log(IndexJs.selectedFrom.value); // Works

        ...
    </JavaScript>
    ...
</StackPanel>

My point is: this is not intuitive, and I really didn’t like to expose this JS globally. Another way I tried (and it didn’t work dynamically, but just for the example) is declaring properties that could be called by the parent UX (in my case, Index.ux):

<StackPanel ux:Class="Search" ux:Name="search" From="Bern">
    <string ux:Property="From" />

For me, it can be intuitive to call this UX like this:

<Search From="{selectedFrom}" />

I know that exists ux:InnerClass and the feature can be used for JS as well, but what about having this feature of “markup parameters” in each UX?

Hi Leonel,

Thanks a lot for explaining your approach.

As of version 0.11, your approach is probably the best way to acheive what you need.

We are however very aware of these difficulties and have been working for months already on a good solution for structuring larger apps.

In a few weeks from now, we’ll release a major upgrade that contains a new set of best practices for how to structure apps, pass paramters between pages, have modules available in a scope etc. Among many new features, Fuse is getting a Router (similar to that found in Angular 1/2).

Thanks for your patience.

Hi Anders,

Was this ever published? Where can I find the best practices for how to structure apps, have modules available in a scope, etc?

mgleason: Yes, this was published along with the 0.20 release.

The docs is a little all over the place, but this is an important one: https://www.fusetools.com/docs/navigation/navigation