Prevent Multiple Clicks

What is the best way to ensure that a button cannot be clicked more than once?

I have a couple of possible solutions, but wonder if there is a best practice, sure-fire way, of achieving this.

My current approaches:

Approach 1

<Button ux:Name="testbutton" Clicked="{someJsMethod}">
  <Clicked>
    <Set Target="testbutton.IsEnabled" Value="false"/>
  </Clicked>
</Button>
  • Pro:
    • Declarative
    • Uses native UX
  • Con:
    • Not sure what order things will run in, or if it is deterministic
    • Not 100% sure if js won’t be called more than once.

Approach 2

//JS ClickingButton.js
var buttonEnabled = Observable(true);
var buttonClicked=function(){
  if(buttonEnabled.value){
    buttonEnabled.value = false;
    //Do whatever
  }
}
module.exports = {
  buttonEnabled: buttonEnabled,
  buttonClicked: buttonClicked
}
<JavaScript File="ClickingButton.js" />
<Button IsEnabled="{buttonEnabled}" Clicked="{buttonClicked}" />
  • Pro:
  • Enforces value in JS so I am sure it will not run the JS more than once
  • Con:
    • More verbose.
    • Not declarative

Are any of these the “best practice” or are there other ways of achieving a guaranteed single-fire of a JS method upon a button click?

I would prefer Approach 1. Its simple and the code tells me that the button will be disabled after being clicked once.

Only thing to keep in mind is that this can have an effect on animations. For example:

<Button ux:Name="button">
	<Clicked>
		<Set button.IsEnabled="false" />
	</Clicked>

	<WhileEnabled>
		<!-- enabled appearance -->
		<WhilePressed>
			<Scale Factor="2" Duration="2" />
		</WhilePressed>
	</WhileEnabled>

	<WhileDisabled>
		<!-- disabled appearance -->
	</WhileDisabled>
</Button>

In this case your Set would immediately stop the animation etc.

But you can fix that by setting a Delay on Set
https://www.fusetools.com/docs/fuse/triggers/actions/set_1

Would this guarantee that the JS callback <Button Clicked="{someMethod}" would not be called more than once?

Maybe my problem is that I don’t understand the call-order of things like Set. What would happen for instance if I clicked with two fingers, or clicked twice really fast? Would the Set trigger before the second click? Would that still be true if there was a delay?

If there is no delay on Set, it will happen immediately. It should not prevent any other Clicked from firing If you need a delay you might want to remove the <Clicked /> after the first firing.

<Button ux:Name="button">
	<WhileTrue Value="true" ux:Name="wt">
		<Clicked>
			<Set wt.Value="false" />
		</Clicked>
	</WhileTrue>
</Button>

But yeah, if you have a delay on Set you might end up firing clicked several times