Match not working

I am sure I am doing something very basic that is wrong here, but would appreciate any help. I’m still pretty new to Javascript and Fuse.

I think I have a fairly common set of states where there is a start button, a user input after the start, and then an assessment of user input. If it is “right” I want to go to the correct answer state and if not I want to go to the incorrect answer. So I decided to use “match” to drive the choice of correct or incorrect answer. I wanted to have four states, but I couldn’t get a SET command to compile within a CASE UX tag. The error I got was:
Set’ does not have an attached property called ‘stateGroup.Active’, nor does it support implicit set-properties. (I would love to know why CASE doesn’t support SET as that would mean I could just have four states and dispense with the WHILETRUE/TOGGLE, but that isn’t my key question).

To get around that I used the WHILETRUE and TOGGLE that has worked for me for other pieces of my app.

The story is that I have a logic error where the statements inside of the CASE are never executed regardless of user input. I would like to know what I need to change to make it so the statements inside of either of the two cases (correct/incorrect) will be executed. Any help would be great.

The UX code is:

<App>
	
	<JavaScript File="MainView.js" />
	<!-- Define two states start and get input -->
	<StateGroup ux:Name="stateGroup">
        <State ux:Name="startState">
        	<Callback Handler="{start}" />
            <Change startPanel.Visibility="Visible"/>
            <Change getInput.Visibility="Hidden" />
            <Change incorrectPanel.Visibility="Hidden" />
            <Change correctPanel.Visibility="Hidden"   />
        </State>
        <State ux:Name="getInputState">
        	<Change startPanel.Visibility="Hidden"/>
            <Change getInput.Visibility="Visible" />
            <Change incorrectPanel.Visibility="Hidden" />
            <Change correctPanel.Visibility="Hidden"   />
        </State>
    </StateGroup>

    <!-- processing for correct state -->

	<WhileTrue ux:Name="setCorrect" Value="false">
         <Change startPanel.Visibility="Hidden"/>
	     <Change getInput.Visibility="Hidden" />
	     <Change incorrectPanel.Visibility="Hidden" />
	     <Change correctPanel.Visibility="Visible"   />
    </WhileTrue>

	<!-- processing for incorrrect state -->
	 <WhileTrue ux:Name="setIncorrect" Value="false">
	 	<Change startPanel.Visibility="Hidden"/>
	    <Change getInput.Visibility="Hidden" />
	    <Change incorrectPanel.Visibility="Visible" />
	    <Change correctPanel.Visibility="Hidden"   />
    </WhileTrue>


	<!-- Display Start Button, once clicked change state to get input -->
	<Panel ux:Name="startPanel" Width="150" Height="150" Visibility="Visible" Alignment="Center">
		 <SolidColor ux:Name="someColor"/>
		 <Text Value="Start" Alignment="Center" FontSize="24" TextColor="Black" />
		 <Clicked>
				<Callback Handler="{start}" />
				<Set stateGroup.Active="getInputState"/>
		 </Clicked>
		 <WhilePressed>
                <Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
         </WhilePressed>
	</Panel>

	<!-- set up generic text input class -->
	<TextInput ux:Class="MyTextInput" FontSize="20" PlaceholderColor="#ccc" Padding="5" CaretColor="Black" MaxLength="7">
    		<Rectangle Layer="Background" CornerRadius="3">
       			 <Stroke Width="1" Color="#ccc" />
    			 	<SolidColor Color="Yellow" />
    		</Rectangle>
	</TextInput>
	
	<!-- Obtain input guess, once clicked change state to display response -->
	<StackPanel ux:Name="getInput" Color="Yellow" Visibility="Hidden" Alignment="Center">
		<Text Value="Guess: " FontSize="24" Color="Black" Alignment="Center"/>
		<Rectangle Alignment="Center" Color="Red" >
			<MyTextInput Value="{guess}" TextColor="Blue" PlaceholderText="Enter Value Here" InputHint="Integer" />
		</Rectangle>

		<Rectangle Alignment="Center" Color="Green" Height="75" Width="75">
			<Text Value="SUBMIT" Color="White" />
				<Clicked>
					<Callback Handler="{computeResponse}"/>
					<DebugAction Message="{correctAnswer}"/>
					<Match Value="{correctAnswer}" >
						<Case Bool="true">
							<Toggle Target="setCorrect"/>
						</Case>
						<Case Bool="false">
							<Toggle Target="setIncorrect"/>
						</Case>
					</Match>
				</Clicked>
				<WhilePressed>
					<Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
				</WhilePressed>
		</Rectangle>	
	</StackPanel>

	
	<!-- Respond to an incorrect the guess -->
	<Panel ux:Name="incorrectPanel" Width="150" Height="150" Visibility="Hidden">
		<StackPanel>
			<Rectangle Width="300" Height="300" >
				<Text Value="{responseText}" TextColor="White" TextAlignment="Center" FontSize="32" />
			</Rectangle>

	    	<SolidColor Color="Blue"/>
	    	<Button Text="Guess Again?">
	            <Clicked>  
	            	<Toggle Target="setIncorrect"/>
	            	<Set stateGroup.Active="getInputState"/>
	            </Clicked>
	            <WhilePressed>
    				<Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
				</WhilePressed>
	        </Button>
	   </StackPanel>
	</Panel>

	<!-- Respond to a correct guess-->
	<StackPanel ux:Name="correctPanel" Width="150" Height="150" Visibility="Hidden">
		<Button Text="Correct Answer">
	          <Clicked>  
	          		<Toggle Target="setCorrect" />
	                <Set stateGroup.Active="startState"/>
	           </Clicked>
	            <WhilePressed>
    				<Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
				</WhilePressed>
	      </Button>
	 </StackPanel>


</App>


The javascript is:

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

var correctAnswer = Observable(10)
var guess = Observable()


function start() {
	correctAnswer.value = false
}

function computeResponse() {
	if (guess.value == correctAnswer.value) {
		correctAnswer.value=true
	} 
}

module.exports = {
	    guess : guess,
	    computeResponse : computeResponse,
	    correctAnswer : correctAnswer,  
	    start : start
    }
		

Just realized I had a copy/paste error when I was making this example. The javascript should be:

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

var correctAnswer = Observable(10)
var guess = Observable()
var done = Observable(false)

function start() {
	done.value = false
}

function computeResponse() {
	if (guess.value == correctAnswer.value) {
		done.value=true
	} 
}

module.exports = {
	    guess : guess,
	    computeResponse : computeResponse,
	    correctAnswer : correctAnswer, 
	    done : done,
	    start : start
    }

and the key code that is not working references done. It is given below. Sorry for any confusion.

                    <Clicked>
					<Callback Handler="{computeResponse}"/>
					<DebugAction Message="{correctAnswer}"/>
					<Match Value="{done}" >
						<Case Bool="true">
							<Toggle Target="setCorrect"/>
						</Case>
						<Case Bool="false">
							<Toggle Target="setIncorrect"/>
						</Case>
					</Match>
				</Clicked>

Hi David :slight_smile:

1) The error with Set

This one is on us. <Set myobject.thatproperty="somevalue"/> is in reality short-hand for <Set Target="myobject.thatproperty" Value="somevalue"/>. However, there are some cases where the short-hand doesn’t resolve and you have to use the longer version (we have a ticket for this). This is the reason for the error you got.

2) Match / Case in UX

This is intended for managing different visual sub-trees based on a single value (ref: the docs and this post). It is not intended for implementing logical control flow and general state machines.
For instance: placing Match inside a Clicked trigger is not something you should be doing. It would make more sense to have Clicked change the value used in Match.

However, you don’t need to have much logic going on before you get to the point of “I should be managing more of this in JS and less in UX” :slight_smile: In this case I’d suggest that you update the value of the WhileTrues from JavaScript (via bound observables that are modified from a JS function called from your Clicked action), rather than trying to do it with Set or Toggle.

3) The Toggles don’t work

Again, this is because the different cases are expected to contain visual sub-trees (i.e. actual components such as panels, buttons, text labels ++) and not stand-alone actions (like toggle or set).
Think of the UX Match / Case as “what should I be showing on-screen while in specific states” and not a tool for discrete events or logic flow.

Hope this helps! :slight_smile:

Many thanks – I will definitely try to redo this so the javascipt drives control flow – I’d prefer that anyway, I just didn’t realize you could have WhileTrue talk to an observable. I see that you can now so I’ll give it a try.

Sorry to keep this going, but I have moved the logic to JS and I still do not get a display of my third state, the correct or incorrect answer. For now I am just trying to display a different color rectangle, but it never displays. I have debugging statements that show I am exactly where I think I am, but it still doesn’t display. Thanks in advance for any help.

<App>
	

	<JavaScript File="MainView.js" />
	<!-- Define two states start and get input -->
	<StateGroup ux:Name="stateGroup">
        <State ux:Name="startState">
        	<Callback Handler="{start}" />
            <Change startPanel.Visibility="Visible"/>
            <Change getInput.Visibility="Hidden" />
            <Change incorrectPanel.Visibility="Hidden" />
            <Change correctPanel.Visibility="Hidden"   />
        </State>
        <State ux:Name="getInputState">
        	<DebugAction Message="Returning to input state"/>
        	<Change startPanel.Visibility="Hidden"/>
            <Change getInput.Visibility="Visible" />
            <Change incorrectPanel.Visibility="Hidden" />
            <Change correctPanel.Visibility="Hidden"   />
        </State>
    </StateGroup>

    <!-- processing for correct state -->

	<WhileTrue ux:Name="setCorrect" Value="{correctResponse}">
		 <DebugAction Message="In While True Correct" />
         <Change startPanel.Visibility="Hidden" Duration="3.0" />
	     <Change getInput.Visibility="Hidden"   Duration="3.0" />
	     <Change incorrectPanel.Visibility="Hidden" Duration="3.0" />
	     <Change correctPanel.Visibility="Visible"   Duration="3.0" />
	      <DebugAction Message="Leaving While True" />
    </WhileTrue>

	<!-- processing for incorrrect state -->
	 <WhileTrue ux:Name="setIncorrect" Value="{incorrectResponse}">
	 	<DebugAction Message="In While True Not Correct" />
	 	<Change startPanel.Visibility="Hidden"/>
	    <Change getInput.Visibility="Hidden" />
	    <Change incorrectPanel.Visibility="Visible" />
	    <Change correctPanel.Visibility="Hidden"   />
	    <DebugAction Message="Leaving While True" />
    </WhileTrue>

    <StackPanel>
		<!-- Display Start Button, once clicked change state to get input -->
		<Panel ux:Name="startPanel" Width="150" Height="150" Visibility="Visible" Alignment="Center">
			 <SolidColor ux:Name="someColor"/>
			 <Text Value="Start" Alignment="Center" FontSize="24" TextColor="Black" />
			 <Clicked>
					<Callback Handler="{start}" />
					<Set stateGroup.Active="getInputState"/>
			 </Clicked>
			 <WhilePressed>
	                <Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
	         </WhilePressed>
		</Panel>

		<!-- set up generic text input class -->
		<TextInput ux:Class="MyTextInput" FontSize="20" PlaceholderColor="#ccc" Padding="5" CaretColor="Black" MaxLength="7">
	    		<Rectangle Layer="Background" CornerRadius="3">
	       			 <Stroke Width="1" Color="#ccc" />
	    			 	<SolidColor Color="Yellow" />
	    		</Rectangle>
		</TextInput>
		
		<!-- Obtain input guess, once clicked change state to display response -->
		<StackPanel ux:Name="getInput" Color="Yellow" Visibility="Hidden" Alignment="Center">
			<Text Value="Guess: " FontSize="24" Color="Black" Alignment="Center"/>
			<Rectangle Alignment="Center" Color="Red" >
				<MyTextInput Value="{guess}" TextColor="Blue" PlaceholderText="Enter Value Here" InputHint="Integer" />
			</Rectangle>

			<Rectangle Alignment="Center" Color="Green" Height="75" Width="75">
				<Text Value="SUBMIT" Color="White" />
					<Clicked>
						<DebugAction Message="Call compute response" />
						<Callback Handler="{computeResponse}"/>
					</Clicked>
					<WhilePressed>
						<Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
					</WhilePressed>
			</Rectangle>	
		</StackPanel>

		
		<!-- Respond to an incorrect the guess -->
		<StackPanel ux:Name="incorrectPanel" Width="150" Height="150">
				<Rectangle Width="300" Height="300" Color="Red"/>
		</StackPanel>

		
		<!-- Respond to a correct guess-->
		<StackPanel ux:Name="correctPanel" Width="150" Height="150">
				<Rectangle Width="300" Height="300" Color="Blue" />
		</StackPanel>
				

	</StackPanel>	 
</App>
var Observable = require('FuseJS/Observable');

var correctAnswer = Observable(10)
var guess = Observable()
var correctResponse = Observable()
var incorrectResponse = Observable()

function start() {
	correctResponse.value = false
}

function computeResponse() {
	incorrectResponse.value = false
	if (guess.value == correctAnswer.value) {
		correctResponse.value=true
	} else {
		incorrectResponse.value=true
	}
	console.log("Correct Response:")
	console.log (correctResponse.value)
	console.log("Incorrect Response:")
	console.log (incorrectResponse.value)
}

module.exports = {
	    guess : guess,
	    computeResponse : computeResponse,
	    correctAnswer : correctAnswer, 
	    correctResponse : correctResponse,
	    incorrectResponse : incorrectResponse,
	    start : start
    }
		

Thanks for your help.

I think the problem is that you have competing states. The states in the stategroup, as well as the WhileTrue, are not subroutines that are called once, they are (as the name implies) states which are applied constantly.

For instance, this means that when getInputState is active and setIncorrect is true there will be contention about getInput.Visibility and incorrectPanel.Visibility since they’re set to different values.

The naive way to deal with this would be to just take the contents of the two WhileTrue’s and instead make them into 2 new states in your stategroup. In this way they’ll never overlap (compete) with the other states.

However, it could also be a good idea to try splitting up the various states into parts that are independent and simplify the whole thing.
For instance: if you can never go directly from the startState to the correct/incorrect states then you probably shouldn’t need to deal with startPanel.Visibility in the two latter states.

Thanks – this version posted below now works. I am curious why I had to change visibility of the start panel in the two answer states. I tried it without it and it kept showing up. I am still not quite getting the feel of the event driven nature of UX, but I’m starting to get the hang of it. Any suggestions for improvements would be welcome as I think this is a common GUI pattern. Really appreciate your help.

<App>
	

	<JavaScript File="MainView.js" />
	<!-- Define two states start and get input -->
	<StateGroup ux:Name="stateGroup">
        <State ux:Name="startState">
        	<Change startPanel.Visibility="Visible"/>
            <Change getInput.Visibility="Hidden" />
        </State>
        <State ux:Name="inputState">
        	<Change startPanel.Visibility="Hidden"/>
            <Change getInput.Visibility="Visible" />
        </State> 

		<State ux:Name="correctAnswerState">
        	<Change getInput.Visibility="Hidden" />
            <Change correctPanel.Visibility="Visible"/>
            <Change startPanel.Visibility="Hidden" />
        </State> 

		<State ux:Name="incorrectAnswerState">
        	<Change getInput.Visibility="Hidden" />
            <Change incorrectPanel.Visibility="Visible" />
            <Change startPanel.Visibility="Hidden" />
        </State> 



    </StateGroup>

    <!-- processing for correct state -->

	<WhileTrue ux:Name="setCorrect" Value="{correctResponse}">
			<Set stateGroup.Active="correctAnswerState"/>
    </WhileTrue>

	<!-- processing for incorrrect state -->
	 <WhileTrue ux:Name="setIncorrect" Value="{incorrectResponse}">
	 		<Set stateGroup.Active="incorrectAnswerState"/>
	 </WhileTrue>

    <StackPanel>
		<!-- Display Start Button, once clicked change state to get input -->
		<Panel ux:Name="startPanel" Width="150" Height="150" Visibility="Visible" Alignment="Center">
			 <SolidColor ux:Name="someColor"/>
			 <Text Value="Start" Alignment="Center" FontSize="24" TextColor="Black" />
			 <Clicked>
					<Callback Handler="{start}" />
					<Set stateGroup.Active="inputState"/>
			 </Clicked>
			 <WhilePressed>
	                <Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
	         </WhilePressed>
		</Panel>

		<!-- set up generic text input class -->
		<TextInput ux:Class="MyTextInput" FontSize="20" PlaceholderColor="#ccc" Padding="5" CaretColor="Black" MaxLength="7">
	    		<Rectangle Layer="Background" CornerRadius="3">
	       			 <Stroke Width="1" Color="#ccc" />
	    			 	<SolidColor Color="Yellow" />
	    		</Rectangle>
		</TextInput>
		
		<!-- Obtain input guess, once clicked change state to display response -->
		<StackPanel ux:Name="getInput" Color="Yellow" Visibility="Hidden" Alignment="Center">
			<Text Value="Guess: " FontSize="24" Color="Black" Alignment="Center"/>
			<Rectangle Alignment="Center" Color="Red" >
				<MyTextInput Value="{guess}" TextColor="Blue" PlaceholderText="Enter Value Here" InputHint="Integer" />
			</Rectangle>

			<Rectangle Alignment="Center" Color="Green" Height="75" Width="75">
				<Text Value="SUBMIT" Color="White" Alignment="Center" />
				<Clicked>
					<DebugAction Message="Call compute response" />
					<Callback Handler="{computeResponse}"/>
				</Clicked>
				<WhilePressed>
					<Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
				</WhilePressed>
			</Rectangle>
		</StackPanel>

		
		<!-- Respond to an incorrect the guess -->
		<StackPanel ux:Name="incorrectPanel" Width="100" Height="100" Visibility="Hidden">
				<Rectangle Width="300" Height="300" Color="Red" >
					<Text Value="Sorry, not correct.  Try again"  TextColor="Black" TextAlignment="Center" Alignment="Center"/>
					<Grid Columns="2" Alignment="BottomLeft">
						<Text Value="Number of Gueses:" TextColor="Black" Alignment="Center"/>
						<Text Value="{totalGuesses}" TextColor="Black" Alignment="Center"/>
					</Grid>
					<Clicked>
						<Set stateGroup.Active="inputState"/>
					</Clicked>
					<WhilePressed>
						<Scale Factor=".50" Duration=".10" Easing="QuadraticInOut" />
					</WhilePressed>
				</Rectangle>
		</StackPanel>

		
		<!-- Respond to a correct guess-->
		<StackPanel ux:Name="correctPanel" Width="150" Height="150" Visibility="Hidden">			
				<Rectangle Width="300" Height="300" Color="Blue" >
					<Text Value="HOORAY, you got it right! " TextColor="Black" TextAlignment="Center" Alignment="Center" />
				</Rectangle>
				<Clicked>
					<Set stateGroup.Active="startState"/>
				</Clicked>
		</StackPanel>

				

	</StackPanel>	 
</App>
ar Observable = require('FuseJS/Observable');

var correctAnswer = Observable(10)
var guess = Observable()
var correctResponse = Observable()
var incorrectResponse = Observable()
var totalGuesses = Observable(0)

function start() {
	totalGuesses.value = 0
	correctResponse.value = false
}

function computeResponse() {
	incorrectResponse.value = false
	if (guess.value == correctAnswer.value) {
		correctResponse.value=true
	} else {
		incorrectResponse.value=true
	}
	++totalGuesses.value
}

module.exports = {
	    guess : guess,
	    computeResponse : computeResponse,
	    correctAnswer : correctAnswer, 
	    correctResponse : correctResponse,
	    incorrectResponse : incorrectResponse,
	    totalGuesses      : totalGuesses,
	    start : start
    }
		

I am curious why I had to change visibility of the start panel in the two answer states. I tried it without it and it kept showing up.

It’s because you’ve set it up to be visible by default :slight_smile: E.g. <Panel ux:Name="startPanel" Width="150" Height="150" Visibility="Visible" Alignment="Center"> (that said, the default is to be visible, so even without the explicit setting of the Visibility property here you’d get the same result).

Keep in mind that when using Change the target property will revert to its default value when the Change is no longer active. (Whereas if you use Set the target property will remain with the new value you gave it).

In other words: if you set the default visibility of startPanel to be Hidden, then you only need to change it to Visible in your startState and can ignore it in the other 3 states.

Thanks – that makes sense.