Passing a Resource value to JS and back?

Hello Fuse team,

I’m working on a custom UI component that works exactly like the Android Floating Action Buttons Menu, but I want it to be flexible enough so that I can pass a Javascript object to it so it will populate the menu with the sub-buttons I pass to it.

So far I have the current setup:

MainView.js

function FloatingButton(label, color, icon) {
	this.label = label;
	this.color = color;
	this.icon = icon;
}

var DefaultFloatingButtonsList = Observable(
	new FloatingButton("Delete", "{Resource brightRed}", "{Resource DeleteIcon}"),
	new FloatingButton("Download", "{Resource brightYellow}", "{Resource DownloadIcon}"),
	new FloatingButton("Preview", "{Resource mediumPurple}", "{Resource PreviewIcon}")
);

,

AndroidFloatingButtonsMenu.ux

<Panel ux:Class="TWAndroidFloatingMenu" ButtonsList="{DefaultFloatingButtonsList}" Alignment="BottomRight" Margin="24">
    <object ux:Property="ButtonsList" />
    <StackPanel>
        <Each Items="{Property this.ButtonsList}">
            <Panel>
                <Circle Color="{color}" Width="32" Height="32">
                    <Icon24 Value="{icon}" Color="#FFF" Alignment="Center" />
                </Circle>
                <Text Value="{label}" Color="#000" Alignment="Left" />
            <Panel>
        </Each>
    </StackPanel>
</Panel>

As you can tell from the Javascript I can’t set the color and icon in my FloatingButton object because they get interpreted as strings when I read them back with {color} and {icon}. I also tried changing the value of icon in JS FloatingButton object to the Unicode character of the icon font but that gets interpreted as a string as well.

Is there any way to do pass these Resource strings to JS and back to UX? Is there a better design for my reusable component?

Thanks!

Hey, Fernando. I tried hacking away at a solution with the code that you provided above, and I had no luck with achieving what you were hoping for. I think the problem is that properties from the context object will only be interpolated once. So where you have the {color} binding Fuse literally replaces that with a string {Resource brightRed}, but then doesn’t do anything else with that value. However there is what’s called a DataToResource binding: https://www.fusetools.com/docs/fuse/reactive/databinding_1. On that page there is a snippet that gives an example:

<FileImageSource ux:Key="picture" File="Pictures/Picture1.jpg" />
<JavaScript>
    module.exports = {
        picture: "picture"
    }
</JavaScript>
<Image Source="{DataToResource picture}" />

Hi David,

Thank you very much for your help, however I wasn’t able to get the code to work in my project, for reasons I cannot understand. I am running Fuse 0.24 on a brand new install of Windows 10, so it’s not a case of running uno clean (although I did).

My best guess is that this code [https://www.fusetools.com/community/forums/howto_discussions/resources_in_their_own_file_replacement_for_style?page=1&highlight=0e5894c3-ee2e-49e2-8668-25579c81e571#post-0e5894c3-ee2e-49e2-8668-25579c81e571] that allows me to list my Resources in a single file and import them into MainView is doing something to the resources and breaking it. But I haven’t had any trouble with it before, to be honest.

Here is the full code to my class as it is:

TWAndroidFloatingMenu.ux

<Panel ux:Class="TWAndroidFloatingMenu" ButtonsList="{DefaultFloatingButtonsList}" Alignment="BottomRight" Margin="24">
	<object ux:Property="ButtonsList" />
	
	<JavaScript>
	var Observable = require("FuseJS/Observable");

	var isOpen = Observable(false);
	var itemCountDelay = Observable(0);

	function increaseItemCount()
	{
		itemCountDelay += 0.2;
	}

	function toggleOpen() {
		isOpen.value = !isOpen.value;
	}

	module.exports = {
		isOpen : isOpen,
		toggleOpen : toggleOpen,
		itemCountDelay : itemCountDelay,
		increaseItemCount : increaseItemCount
	};
	</JavaScript>
	
	<Circle Width="48" Height="48" Color="{roxo00}" ux:Name="MainFloatingButton" Alignment="BottomRight">
		<Icon24 Value="{CloseIcon}" Alignment="Center" Color="{branco}" ux:Name="MainIcon"/>
		<Clicked>
			<Callback Handler="{toggleOpen}" />
		</Clicked>
	</Circle>

	<Panel ux:Name="Submenu" Alignment="BottomRight">
		<Translation RelativeTo="Size" RelativeNode="MainFloatingButton" Y="-1" />
		<Translation X="-8" />
		<!-- SubMenuItem Class -->
		<StackPanel ux:Class="SubMenuItem" Orientation="Horizontal" Width="100%" ItemIcon="{icon}" ItemColor="{color}" ItemLabel="{label}" IsOpen="false" ux:Name="_thisSubMenu">
			<float4 ux:Property="ItemColor" />
			<string ux:Property="ItemIcon" />
			<string ux:Property="ItemLabel" />
			<bool ux:Property="IsOpen" />

			<WhileFalse Value="{Property _thisSubMenu.IsOpen}">
				<Move RelativeTo="Size" Y="1" Duration="1" DurationBack="0.3" Easing="CubicOut" EasingBack="CubicIn" Delay="{itemCountDelay}"/>
			</WhileFalse>

			<Rectangle CornerRadius="2" Color="{branco}" Padding="4" Margin="0,0,6,0" ux:Name="Label">
				<WhileFalse Value="{Property _thisSubMenu.IsOpen}">
					<Move X="1" RelativeTo="Size" Duration="0.3" DurationBack="0.3" Easing="CubicOut" EasingBack="CubicInOut" />
					<Change Target="Label.Opacity" Value="0" Duration="0.5" DurationBack="0.5" Easing="CubicOut" EasingBack="CubicInOut"/>
				</WhileFalse>
				<Text Value="{Property _thisSubMenu.ItemLabel}" Alignment="Center" Color="{preto}" FontSize="13" />
			</Rectangle>

			<Circle Color="{Property _thisSubMenu.ItemColor}" Width="32" Height="32">
				<Icon24 Value="{Property _thisSubMenu.ItemIcon}" Color="{branco}" Alignment="Center" />
			</Circle>
		</StackPanel>
		
		<StackPanel Alignment="BottomRight">
			<Each Items="{Property this.ButtonsList}">
				<Panel Alignment="CenterRight" Margin="0,6,0,6" MinHeight="32">
					<WhileFalse Value="{isOpen}">
						<Change Target="MainIcon.Value" Value="{EditIcon}"/>
						<Change Target="Submenu.Opacity" Value="0" Duration="0.1" DurationBack="0.5" Easing="CubicOut" EasingBack="CubicIn"/>
					</WhileFalse>


					<!-- Print the Item -->
					<SubMenuItem ItemIcon="{icon}" ItemColor="{color}" ItemLabel="{label}" IsOpen="{isOpen}"/>
				</Panel>
			</Each>
		</StackPanel>
	</Panel>
	<DropShadow Distance="2" Spread="0.1" Color="#0004" />
</Panel>

Here’s how my Icons and Colors are defined (snippets)

ResCores.ux

<ResourceContainer ux:Class="CoresInclude">
	<!-- Azuis -->
	<float4 ux:Value="#0D1C2F" ux:Global="azul03" />
	<float4 ux:Value="#1A385D" ux:Global="azul02" />
	<float4 ux:Value="#26548B" ux:Global="azul01" />
	<float4 ux:Value="#3370BA" ux:Global="azul00" />
	<float4 ux:Value="#6694CB" ux:Global="azul10" />
	<float4 ux:Value="#99B8DD" ux:Global="azul20" />
	<float4 ux:Value="#CCDBEE" ux:Global="azul30" />
    ...
</ResourceContainer>

ResIcones.ux

<ResourceContainer ux:Class="IconesInclude">
	<!--Icons -->
	<string ux:Value="&#xE147;" ux:Global="AddMarkerIcon" />
	<string ux:Value="&#xE560;" ux:Global="AnnotationsIcon" />
	<string ux:Value="&#xE226;" ux:Global="AttachIcon" />
	<string ux:Value="&#xE5C4;" ux:Global="BackIcon" />
	<string ux:Value="&#xE24D;" ux:Global="FileIcon" />
    ...
</ResourceContainer>

MainView.js

function FloatingButton(label, color, icon) {
	this.label = label;
	this.color = color;
	this.icon = icon;
}

var DefaultFloatingButtonsList = Observable(
	new FloatingButton("Excluir", "vermelho00", "DeleteIcon"),
	new FloatingButton("Baixar", "verde00", "DownloadIcon"),
	new FloatingButton("Ordenar", "dourado00", "FilterIcon")
);

module.exports = {
	labelFiltro : labelFiltro,
	mudarFiltro : mudarFiltro,
	nomeBotao : nomeBotao,

	returnEventArgs : returnEventArgs,
	eventArg : eventArg,

	DefaultFloatingButtonsList : DefaultFloatingButtonsList,
	// vermelho00 : "vermelho00",
	// verde00 : "verde00",
	// dourado00 : "dourado00",
	// DownloadIcon : "DownloadIcon",
	// DeleteIcon : "DeleteIcon",
	// EditIcon : "EditIcon",
	// FilterIcon : "FilterIcon",
   ...
};
</JavaScript>

I will take a look at your code and try to run it later after work. I am also using Fuse on a Windows 10 machine. Best of luck to you in resolving the issue!

Oh and of course, the error I get is:

ERROR: Unable to convert vermelho00 to float4
    System.Exception occured.

       at Fuse.Scripting.Marshal.ToFloat4(Object o) in C:\ProgramData\Uno\Packages\FuseCore\0.33.5\Scripting\$.uno:line 1068
       at Fuse.Scripting.Marshal.TryConvertTo(Type t, Object o) in C:\ProgramData\Uno\Packages\FuseCore\0.33.5\Scripting\$.uno:line 1123
       at Fuse.Scripting.Marshal.TryConvertTo[T](Object obj, T& value) in C:\ProgramData\Uno\Packages\FuseCore\0.33.5\Scripting\$.uno:line 1089
       at Fuse.Reactive.DataBinding`1.TryPushAsMarshalledValue(Object newValue) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\$.uno:line 364
       at Fuse.Reactive.DataBinding`1.PushValue(Object newValue) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\$.uno:line 307
       at Fuse.Reactive.DataBinding`1.NewValue(Object value) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\$.uno:line 293
       at Fuse.Reactive.SegmentObserver.HandlePath(Object dc, String path) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 294
       at Fuse.Reactive.SegmentObserver.HandleNewDataContext(Object val) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 253
       at Fuse.Reactive.SegmentObserver.HandleObject(ObjectMirror obj) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 264
       at Fuse.Reactive.SegmentObserver.HandlePath(Object dc, String path) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 325
       at Fuse.Reactive.SegmentObserver.Init(Object dc, String path) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 238
       at Fuse.Reactive.BindAttempt.TryBind() in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 140
       at Fuse.Reactive.BindAttempt..ctor(PathObserver po) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 131
       at Fuse.Reactive.PathObserver.RestartBinding() in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 96
       at Fuse.Reactive.BindAttempt.Restart() in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 179
       at Fuse.Reactive.BindAttempt.OnDataContextChanged(Object sender, EventArgs args) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\Subscription\$.uno:line 184
       at System.EventHandler.Invoke(Object sender, EventArgs e)
       at Fuse.Node.OnDataContextChanged() in C:\ProgramData\Uno\Packages\FuseCore\0.33.5\$.uno:line 3606
       at Fuse.Reactive.JavaScript.SetDataContext(Object newDc) in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\$.uno:line 1199
       at Fuse.Reactive.JavaScript.EvaluateDataContext.SetDataContext() in C:\ProgramData\Uno\Packages\Fuse.Reactive\0.33.5\$.uno:line 1228
       at Fuse.UpdateManager.ProcessPostActions() in C:\ProgramData\Uno\Packages\FuseCore\0.33.5\$.uno:line 9907

EDIT: Sorry for my noobness

Cheers

Blade

Fernando Lins; why are you using ux:Global and not ux:Key? ResourceContainer was made for the ux:Key case. (you can read about the details here: https://www.fusetools.com/docs/uno/ux/resource)

Tree-local resources (ux:Key) you can use the {DataToResouce binding} syntax

Hi Kristian,

Since the documentation gives the example <float4 ux:Global="Red" ux:Value="#f00" />, I thought that was my mistake and changed the code. But using ux:Key, ResourceContainer and {DataToResource someResource} will give me the same error.

As you can see in the topmost post I tried passing "{Resource brightRed}" (float4 Resource) and "{Resource DeleteIcon}"; then moved on to DataToResource as David suggested and finally tried ux:Global instead of ux:Key.

Thanks

Issue fixed!

I thought that setting {icon} and {color} would return a {Resource someResource} set in JavaScript, but it’s actually the other way around.

If anyone is interested the code below to the class will draw an Android - like Floating Buttons Menu:

<Panel ux:Class="TWAndroidFloatingMenu" ButtonsList="{DefaultFloatingButtonsList}" Alignment="BottomRight" Margin="24">
	<object ux:Property="ButtonsList" />
	
	<JavaScript>
	var Observable = require("FuseJS/Observable");

	var isOpen = Observable(false);

	function toggleOpen() {
		isOpen.value = !isOpen.value;
	}

	module.exports = {
		isOpen : isOpen,
		toggleOpen : toggleOpen,
	};
	</JavaScript>
	
	<Circle Width="48" Height="48" Color="{Resource roxo00}" ux:Name="MainFloatingButton" Alignment="BottomRight">
		<Icon24 Value="{Resource CloseIcon}" Alignment="Center" Color="{Resource branco}" ux:Name="MainIcon"/>
		<Clicked>
			<Callback Handler="{toggleOpen}" />
		</Clicked>
	</Circle>

	<Panel ux:Name="Submenu" Alignment="BottomRight">
		<Translation RelativeTo="Size" RelativeNode="MainFloatingButton" Y="-1" />
		<Translation X="-8" />
		<!-- SubMenuItem Class -->
		<StackPanel ux:Class="SubMenuItem" Orientation="Horizontal" Width="100%" ItemIcon="{Resource DownloadIcon}" ItemColor="{Resource vermelho00}" ItemLabel="Item" IsOpen="false" ux:Name="_thisSubMenu">
			<float4 ux:Property="ItemColor" />
			<string ux:Property="ItemIcon" />
			<string ux:Property="ItemLabel" />

			<bool ux:Property="IsOpen" />

			<WhileFalse Value="{Property _thisSubMenu.IsOpen}">
				<Move RelativeTo="Size" Y="1" Duration="{UIdelay}" DurationBack="{UIdelay}" Easing="CubicOut" EasingBack="CubicIn" />
			</WhileFalse>

			<Rectangle CornerRadius="2" Color="{Resource branco}" Padding="4" Margin="0,0,6,0" ux:Name="Label">
				<WhileFalse Value="{Property _thisSubMenu.IsOpen}">
					<Move X="-1" RelativeTo="Size" Duration="{UIdelay}" DurationBack="{UIdelay}" Easing="CubicOut" EasingBack="CubicInOut" />
					<Change Target="Label.Opacity" Value="0" Duration="{UIdelay}" DurationBack="{UIdelay}" Easing="CubicOut" EasingBack="CubicInOut"/>
				</WhileFalse>
				<Text Value="{Property _thisSubMenu.ItemLabel}" Alignment="Center" Color="{Resource preto}" FontSize="13" />
			</Rectangle>

			<Circle Color="{Property _thisSubMenu.ItemColor}" Width="32" Height="32">
				<Icon24 Value="{Property _thisSubMenu.ItemIcon}" Color="{Resource branco}" Alignment="Center" />
			</Circle>
		</StackPanel>
		
		<StackPanel Alignment="BottomRight">
			<Each Items="{Property this.ButtonsList}">
				<Panel Alignment="CenterRight" Margin="0,6,0,6" MinHeight="32">
					<WhileFalse Value="{isOpen}">
						<Change Target="MainIcon.Value" Value="{Resource EditIcon}"/>
						<Change Target="Submenu.Opacity" Value="0" Duration="0.1" DurationBack="{UIdelay}" Easing="CubicOut" EasingBack="CubicIn"/>
					</WhileFalse>


					<!-- Print the Item -->
					<SubMenuItem ItemIcon="{DataToResource icon}" ItemColor="{DataToResource color}" ItemLabel="{label}" IsOpen="{isOpen}"/>
				</Panel>
			</Each>
		</StackPanel>
	</Panel>
	<DropShadow Distance="2" Spread="0.1" Color="#0004" />
</Panel>

You will need to set it up in Javascript as such:

var nI = 0;

function FloatingButton(label, color, icon, UIdelay) {
	this.label = label;
	this.color = color;
	this.icon = icon;
	this.UIdelay = UIdelay;
}

function returnDelay()
{
	// This function returns an increasing delay so that 
        // each menu item animates in at a different time. The rate set here is 0.2 seconds per menu item.

	nI = (nI + 0.2);
	return nI;
}

var DefaultFloatingButtonsList = Observable(
	new FloatingButton("Excluir", "vermelho00", "DeleteIcon", returnDelay()),
	new FloatingButton("Baixar", "verde00", "DownloadIcon", returnDelay()),
	new FloatingButton("Ordenar", "dourado00", "FilterIcon", returnDelay()),
	new FloatingButton("Apresentar", "azul01", "PlayIcon", returnDelay())
);

You will also need to define the resources for the colors and the icons like:

<!-- Colors -->
<float4 ux:Value="#FEB02E" ux:Key="dourado00" />
<float4 ux:Value="#5859d2" ux:Key="roxo00" />
<float4 ux:Value="#0caf41" ux:Key="verde00" />
<float4 ux:Value="#ef374b" ux:Key="vermelho00" />
<float4 ux:Value="#26548B" ux:Key="azul01" />


<!-- Google Icon Font Values -->
<string ux:Value="&#xE872;" ux:Key="DeleteIcon" />
<string ux:Value="&#xE5DB;" ux:Key="DownloadIcon" />
<string ux:Value="&#xE3C9;" ux:Key="EditIcon" />