How to set focus on an element in Elm? - html

How can I set the focus on a Html element in Elm? I tried to set the autofocus attribute on the element and it only sets the focus on the page load.

The focus function in the elm-lang/dom package is used to set focus with a Task (without using any ports or JavaScript).
Internally it uses requestAnimationFrame to ensure any new DOM updates are rendered before it tries to find the DOM node to focus on.
An example use:
type Msg
= FocusOn String
| FocusResult (Result Dom.Error ())
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FocusOn id ->
( model, Dom.focus id |> Task.attempt FocusResult )
FocusResult result ->
-- handle success or failure here
case result of
Err (Dom.NotFound id) ->
-- unable to find dom 'id'
Ok () ->
-- successfully focus the dom
Full example on Ellie

A workaround for this is to use Mutation Observers. Insert this JavaScript either in your main HTML page or in the main view of your Elm code:
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
handleAutofocus(mutation.addedNodes);
});
});
var target = document.querySelector('body > div');
var config = { childList: true, subtree: true };
observer.observe(target, config);
function handleAutofocus(nodeList) {
for (var i = 0; i < nodeList.length; i++) {
var node = nodeList[i];
if (node instanceof Element && node.hasAttribute('data-autofocus')) {
node.focus();
break;
} else {
handleAutofocus(node.childNodes);
}
}
}
Then create HTML elements by including Html.Attributes.attribute "data-autofocus" "".

With elm/html 0.19 you can set the Html.Attrbutes autofocus to True
input [ onInput Code, autofocus True ] []

I spent quite a bit of time exploring this recently. Unfortunately, I don't think it is possible with the existing elm-html library. However, I came up with a hack that utilizes css animations to trigger an event and embed that in pure js.
Here is my hack in Elm using a script node and a style node. It is very ugly in my opinion.
import Html exposing (div, button, text, input, node)
import Html.Events exposing (onClick)
import Html.Attributes exposing (type', class)
import StartApp.Simple
main =
StartApp.Simple.start { model = model, view = view, update = update }
model = []
view address model =
-- View now starts with a <style> and <script> (hacky)
(node "style" [] [ Html.text style ]) ::
(node "script" [] [Html.text script ]) ::
(button [ onClick address AddInput ] [ text "Add Input" ]) ::
model |>
div []
type Action = AddInput
update action model =
case action of
AddInput -> (Html.p [] [input [type' "text", class "focus"] []]) :: model
-- Use pure string css (hacky)
style = """
.focus {
animation-name: set-focus;
animation-duration: 0.001s;
-webkit-animation-name: set-focus;
-webkit-animation-duration: 0.001s;
}
#-webkit-keyframes set-focus {
0% {color: #fff}
}
#keyframes set-focus {
0% {color: #fff}
}
"""
-- Cheating by embedding pure javascript... (hacky)
script = """
var insertListener = function(event){
if (event.animationName == "set-focus") {
event.target.focus();
}
}
document.addEventListener("animationstart", insertListener, false); // standard + firefox
document.addEventListener("MSAnimationStart", insertListener, false); // IE
document.addEventListener("webkitAnimationStart", insertListener, false); // Chrome + Safari
"""

In Elm 0.19, use Browser.Dom.focus:
import Browser.Dom as Dom
import Task
type Msg
= NoOp
focusSearchBox : Cmd Msg
focusSearchBox =
Task.attempt (\_ -> NoOp) (Dom.focus "search-box")
You can choose to ignore if focusing fails like above or do something by triggering an update message.

Elm 0.19's Browser.Dom.focus is the modern solution
import Browser.Dom as Dom
import Task
type Msg
= NoOp
| Focus String
focusElement : String -> Cmd Msg
focusElement htmlId =
Task.attempt (\_ -> NoOp) (Dom.focus htmlId)
update : Msg -> Model -> (Model, Cmd Msg)
update msg =
case msg of
Focus htmlId ->
( model, focusElement htmlId )
NoOp ->
( model, Cmd.none )

Related

Composables in Pager With Tabs Composed Too Quickly

Trying to implement accompanist pager with tabs to achieve something like instagram's page displaying followers, following and subscription - 3 tab menu with pager basically. This is the code I am using.
fun UsersPager(
myDBViewModel: MyDBViewModel
) {
val tabData = listOf(
"FOLLOWING" to Icons.Filled.PermIdentity,
"ALLUSERS" to Icons.Filled.PersonOutline,
"FOLLOWERS" to Icons.Filled.PersonOutline
)
val pagerState = rememberPagerState(
0
)
val tabIndex = pagerState.currentPage
val coroutineScope = rememberCoroutineScope()
Column {
TabRow(
selectedTabIndex = tabIndex,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
Modifier.pagerTabIndicatorOffset(pagerState, tabPositions)
)
}
) {
tabData.forEachIndexed { index, pair ->
Tab(
selected = tabIndex == index,
onClick = {
coroutineScope.launch {
Log.d("MP18", "click on Tab num: $index")
pagerState.animateScrollToPage(index)
}
},
text = {
Text(text = pair.first)
},
icon = {
Icon(imageVector = pair.second, contentDescription = null)
})
}
}
HorizontalPager(
state = pagerState,
itemSpacing = 1.dp,
modifier = Modifier
.weight(1f),
count = tabData.size
) { index ->
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
when (index) {
1 -> ShowMyFollowees(myDBViewModel = myDBViewModel)
2 -> ShowMyUsers(myDBViewModel = myDBViewModel)
3 -> ShowMyFollowers(myDBViewModel = myDBViewModel)
}
}
}
}
}
Then 3 composables follow this pattern to fetch data from API and display them:
#Composable
fun ShowMyUsers(
myDBViewModel: MyDBViewModel,
) {
val pageLoadedTimes by myDBViewModel.pageLoadedTimes.observeAsState(initial = null)
val myUsersList by myDBViewModel.myUsersList.observeAsState(initial = emptyList())
val loading by myDBViewModel.loading.observeAsState(initial = myDBViewModel.loading.value)
if (myUsersList.isNullOrEmpty() && pageLoadedTimes == 0 && !loading!!) {
LaunchedEffect(key1 = Unit, block = {
Log.d("MP18", "launchedEffect in ScreenMyAccount.ShowMyUsers")
myDBViewModel.getFirstPageUsers()
})
}
ListMyUsers(myUsers = myUsersList, myDBViewModel = myDBViewModel)
}
#Composable
fun ListMyUsers(
myUsers: List<MyUser>,
myDBViewModel: MyDBViewModel
) {
val pageLoadedTimes by myDBViewModel.pageLoadedTimes.observeAsState(initial = myDBViewModel.pageLoadedTimes.value)
val loading by myDBViewModel.loading.observeAsState(initial = myDBViewModel.loading.value)
Log.d(
"MP18",
"comp ShowMyUsers and pageLoadedTimes is: $pageLoadedTimes and loading is: $loading"
)
Column(
modifier = Modifier
.fillMaxSize()
.background(color = Color.Red)
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp)
) {
itemsIndexed(
items = myUsers
) { index, user ->
myDBViewModel.onChangeProductScrollPosition(index)
val numRec = pageLoadedTimes?.times(PAGE_SIZE)
Log.d(
"MP188",
"in composable, page: $pageLoadedTimes, index: $index, loading: $loading, numRec: $numRec"
)
//we should query and display next page if this is true:
if ((index + 1) >= (pageLoadedTimes?.times(PAGE_SIZE)!!) && !loading!!) {
myDBViewModel.getNextPageUsers()
}
ShowSingleUser(
index = index,
pageLoadedTimes = pageLoadedTimes!!,
user = user,
myDBViewModel = myDBViewModel
)
}
}
}
}
In composables that are available, there's an API call (through ViewModel) which gets data from backend in order to populate some vars in viewModel. The problem I have is that when first tab is clicked, also the neighbouring composable gets composed and thus I'am making 2 API calls and "preparing" second tab data even if the user might never click on that tab. This is not what I want. I'd like to fetch data from tab2 and later tab3 only when there's a click on them. I hope I am clear in what's bothering me.
This is the expected behavior of the pager as the pager has been implemented by using LazyRow in accompanist pager. Basically, pager loads the second page before you scroll to it as LazyLayout is implemented in that way. If you want to cancel that you can do something like this, which I use in my code also:
// In anywhere of your composable
SideEffect {
if(currentShownItemIndex == pagerState.currentPage) {
// Make api call...
}
}
This should ensure that you are making your api call if and only if you are on the correct index
Edit: You can use Launched Effect if you want, I used SideEffect as it is easier to write and does not rely on any key and I needed a coroutine scope simply :d
Finally, this does not prevent the composition of the page in index+1 however prevents the unnecessary api call made by pager.
I found the solution for this. I added another variable in viewModel:
private val _pageInPager = MutableLiveData(0)
val pageInPager: LiveData<Int> = _pageInPager
fun setPageInPager(pageNum: Int) {
Log.d("MP188", "setPageInPager to: $pageNum")
_pageInPager.value = pageNum
}
Then in composable:
if user clicks on tab:
onClick = {
coroutineScope.launch {
Log.d("MP18", "click on Tab num: $index")
pagerState.animateScrollToPage(index)
myDBViewModel.setPageInPager(index)
}
},
or move the pager(slider):
myDBViewModel.setPageInPager(pagerState.currentPage)
I have the exact page in the variable: myDBViewModel.pageInPager, so I can add checker in LaunchedEffect before making an API call:
if (myUsersList.isNullOrEmpty() && pageLoadedTimes == 0 && !loading!! && pageInPager == 1) {
LaunchedEffect(key1 = Unit, block = {
Log.d("MP18", "launchedEffect in ScreenMyAccount.ShowMyUsers")
myDBViewModel.getFirstPageUsers()
})
I think this works ok now. Thank you #Subfly.

How to view and bind an array with swing builder

Currently, this shows 2 variables on the screen. Whenever that variable changes, it is also shown on the GUI.
I would like to bind array indexes in a similar way. For example: x[1],
and whenever x[1] changes, so does the value shows on GUI
EDIT: array x doesn't update, I also don't want the whole array but each index on a new line.
import groovy.swing.SwingBuilder
import groovy.beans.Bindable
#Bindable
class controller{
boolean stop = false;
String status = "RUNNING";
String name = "";
String[] x= new String[5];
}
def ctrl = new controller();
def UI = new SwingBuilder().edt {
frame(title: 'Menu', size: [220, 150], locationRelativeTo: null, show: true, alwaysOnTop: true){
gridLayout(cols:1, rows: 5)
label(text: bind(source: ctrl, sourceProperty: 'status', converter: { v -> v? "Status: $v": ''}))
label(text: bind(source: ctrl, sourceProperty: 'name', converter: { v -> v? "$v": ''}))
label(text: bind(source: ctrl, sourceProperty: 'x', converter: { v -> v? "$v": ''}))
}
}
for(i = 0; i < 5 ; i++){
sleep(500);
ctrl.name+= "Hi there ";
ctrl.x[i] = "T"+i;
}
ctrl.status = "DONE";
sleep(1000);
UI.dispose();
You can't observe changes on an array using #Bindable in this way. The AST transformation is a convenience for registering methods that rely on PropertyChange and PropertyChangeListener.
Changes made to name and status properties work because you're replacing their value, thus a PropertyChangeEvent is triggered for every change. However for the x property you're not changing the array reference, but the elements within the array.
You have two options to make this work:
either observe changes on the index itself (add an Integer property to the controller class).
use an ObservableList instead of an array.
Personally I prefer option #1. You will have to use a converter such as
label(text: bind(source: ctrl, sourceProperty: 'index', converter: { v -> v != -1? ctrl.x[v]: ''}))
Notice the use of -1 to check for a selected index as a value of 0 would make the previous check (v?) fail due to Groovy Truth.
The controller class can be updated as follows
#Bindable
class controller{
boolean stop = false;
String status = "RUNNING";
String name = "";
String[] x= new String[5];
int index = -1;
}

Right Click on a Button / Scala

I'm currently trying to code a minesweeper using scala, but I can't find the way to listen to a right click on a button.
I've already searched on the Internet the way to do it, but I definitely was not able to find it.
If anyone could help me out, I would be really grateful :)
Thanks,
Schnipp
(Note: Scala is a new language to me and I am not a Java user, so I am sorry if my questions sound dumb)
EDIT:
I am trying to find (or implement) a function 'ButtonClickedRight' that could listen to a right-click on a button.
like this
import scala.swing._
import scala._
import scala.swing.event._
object Right extends MainFrame with App {
title = ""
visible = true
val b = new button("")
listenTo(b)
reactions += {
case ButtonClicked(`b`) => *code*
case ButtonClickedRight(`b`) => *code*
}
}
EDIT 2 --
I would like to know if the user has clicked on the Button "1" or not. The problem I have is that this code prints "Mouse clicked at " + e.point+" type "+e.modifiers when I click on the label but not on the button.
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
contents += new Button("1")
contents += new Label("2")
listenTo(mouse.clicks)
reactions += {
case e: MouseClicked =>
println("Mouse clicked at " + e.point+" type "+e.modifiers)
}
}
def top = new MainFrame {
contents = ui
visible = true
preferredSize = new Dimension(500,500)
}
}
Button events are fired through a specific publisher .mouse.clicks.
import scala.swing._
import scala.swing.event._
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
val button = new Button("1")
contents += button
contents += new Label("2")
listenTo(button.mouse.clicks) // !
reactions += {
case evt # MouseClicked(`button`, pt, _, _, _) =>
val which = evt.peer.getButton
if (which > 1) {
println(s"Mouse clicked at (${pt.x}; ${pt.y}) - button: $which")
}
}
}
lazy val top = new MainFrame {
contents = ui
size = new Dimension(500,500)
}
}
Note that at least on Linux my right button has number 3 not 2. You could also use the triggersPopup flag, but then you must ensure to monitor both MousePressed and MouseReleased, as this flag is platform-dependent.
I think that you are on the right path, for my understanding of scala swings I think that the problem is that you are not attaching the listener correctly. For one I would assign the button to a value and call listenTo only on it:
val button = new Button("1")
listenTo(button)
Then, in the reactions, I would write the pattern checking in the event that it comes from the button (probably redundant if you only call listenTo passing the button) and that it has the correct button:
case ButtonClicked(b) if b == button && b.peer.getButton == MouseEvent.BUTTON_2 => ...
So the code you provided in your edit would become:
object App extends SimpleSwingApplication {
lazy val ui = new GridPanel(2,1) {
val button = new Button("1")
contents += button
contents += new Label("2")
listenTo(button)
reactions += {
case evt # MouseClicked(b, pt, _, _, _) if b == button && evt.peer.getButton == java.awt.event.MouseEvent.BUTTON2 =>
println(s"Mouse clicked at (${pt.x}; ${pt.y}) - button: ${evt.peer.getButton}")
}
}
def top = new MainFrame {
contents = ui
visible = true
preferredSize = new Dimension(500,500)
}
}
The following works for me:
new Button {
listenTo(mouse.clicks)
reactions += {
case MouseClicked(_, _, c, _, _) => handleClick(c == 0)
}
}
def handleClick(isLeftClick: Boolean): Unit = {
//
}

Scala Swing Box Subscribes Multiple Events

I'm writing a GUI in Scala, and I've run across a strange problem while trying to register Button Events in a foreach statement: It should be that for every element objecti in a list of objects (object0 ... objectn), a corresponding Button x = buttoni is retrieved and a given Box is subscribed to it with box.listenTo(x). When the Button is pushed, some action related to the objecti should be performed (in this case, println("Event triggered: " + event)):
import scala.swing.ComboBox
import scala.collection.mutable.Buffer
import scala.swing.Button
import scala.swing.event.ButtonClicked
import scala.swing.Action
import scala.swing.SimpleSwingApplication
import scala.swing.MainFrame
import scala.swing.GridPanel
import scala.swing.BorderPanel
object EventSet extends SimpleSwingApplication {
object PhoneKeyEvent extends Enumeration {
val Key1 = Value("1")
val Key2 = Value("2")
}
/* Constants */
private val DisplayHistory = Buffer[String]()
private val KeypadKeyEvents = List(
PhoneKeyEvent.Key1, PhoneKeyEvent.Key2)
private val PhoneKeyEventButtonNames = Map(
PhoneKeyEvent.Key1 -> "1",
PhoneKeyEvent.Key2 -> "2"
)
/* End constants */
private var PhoneKeyEventButtons = Map[PhoneKeyEvent.Value, Button]()
private def createDisplay() : ComboBox[String] = {
new ComboBox(DisplayHistory) {
// Listen to keypad keys
// Get the set of all keypad key events
val keypadEvents = List(PhoneKeyEvent.Key1, PhoneKeyEvent.Key2)
println("keypadEvents: " + keypadEvents)
keypadEvents.foreach({ event =>
println("event: " + event)
// Listen to each button representing a keypad key event
var keypadEventButton = PhoneKeyEventButtons(event)
println("keypadEventButton: " + keypadEventButton)
listenTo(keypadEventButton)
reactions += {
case ButtonClicked(keypadEventButton) => {
// TODO: fix strange bug here: adds all possible inputs
println("Event triggered: " + event)
// selection.item = selection.item + event
}
}
})
}
}
private def createPhoneControllerPanel() : BorderPanel = {
new BorderPanel() {
val keypadControlPanel = createPhoneKeyEventTypeControlPanel(KeypadKeyEvents)
add(keypadControlPanel, BorderPanel.Position.Center)
add(createDisplay(), BorderPanel.Position.North)
focusable = true
requestFocus
}
}
/**
* Creates a new {#link Button} for a given {#link PhoneKeyEvent} and adds
* the button to the global map of such buttons to their respective events;
* that means multiple buttons cannot be created for the same key event.
*/
private def createPhoneKeyEventButton(phoneKeyEvent: PhoneKeyEvent.Value) : Button = {
// Only one button can be created per key event
require(!PhoneKeyEventButtons.contains(phoneKeyEvent),
{System.err.println("A Button for the PhoneKeyEvent " + phoneKeyEvent + "has already been created.")})
val keyEventButtonName = PhoneKeyEventButtonNames(phoneKeyEvent)
val result = new Button(Action(keyEventButtonName) {
println("Key event button pressed: " + phoneKeyEvent)
})
// Add the button to the map of all created key event buttons
PhoneKeyEventButtons += phoneKeyEvent -> result
return result
}
private def createPhoneKeyEventTypeControlPanel(keyEvents : Iterable[PhoneKeyEvent.Value]) : GridPanel = {
new GridPanel(4, 3) {
// Get the intersection of all key events of the given type and the events with button names
keyEvents.foreach(phoneKeyEvent => contents += createPhoneKeyEventButton(phoneKeyEvent))
}
}
override def top = new MainFrame {
contents = createPhoneControllerPanel()
}
}
However, I get some very strange behaviour, where clicking any Button results in all such object actions are triggered -- See the program output:
keypadEvents: List(1, 2)
event: 1
keypadEventButton: scala.swing wrapper scala.swing.Button$$anon$1[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=
javax.swing.plaf.BorderUIResource$CompoundBorderUIResource#7633f09,flags=296,maximumSize=,minimumSize=,preferredSize=,de
faultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14]
,paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,te
xt=1,defaultCapable=true]
event: 2
keypadEventButton: scala.swing wrapper scala.swing.Button$$anon$1[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=
javax.swing.plaf.BorderUIResource$CompoundBorderUIResource#7633f09,flags=296,maximumSize=,minimumSize=,preferredSize=,de
faultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14]
,paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,te
xt=2,defaultCapable=true]
Key event button pressed: 1
Event triggered: 1
Event triggered: 2
Key event button pressed: 2
Event triggered: 1
Event triggered: 2
I'm completely at a loss as to why this is happening; I'm quite new at Scala anyway, so it's quite unfamiliar territory, but I've tried fiddling with a lot of stuff and snooped around in the Swing source code, and still clueless... how can every value of a reference inside of a loop be used in every iteration? or how can every event be triggered by Swing at once? or...?
Edit: Here are two minimised versions, both of which behave differently:
import scala.swing.SimpleSwingApplication
object ButtonEvents extends SimpleSwingApplication {
import scala.swing.Button
import scala.swing.event.ButtonClicked
import scala.swing.Action
import scala.swing.MainFrame
import scala.swing.FlowPanel
override def top = new MainFrame {
contents = new FlowPanel {
val button1 = new Button(Action("1") {
println("Button 1 pressed")
})
contents += button1
val button2 = new Button(Action("2") {
println("Button 2 pressed")
})
contents += button2
val buttons = List(button1, button2)
buttons.foreach({ button =>
listenTo(button)
reactions += {
case ButtonClicked(button) => {
println("Event triggered: " + button.text)
}
}
})
}
}
}
Prints:
Button 1 pressed
Event triggered: 1
Event triggered: 1
Button 2 pressed
Event triggered: 2
Event triggered: 2
And a version which seems to behave correctly (but I'm not sure why):
import scala.swing.SimpleSwingApplication
object ButtonEvents extends SimpleSwingApplication {
import scala.swing.Button
import scala.swing.event.ButtonClicked
import scala.swing.Action
import scala.swing.MainFrame
import scala.swing.FlowPanel
override def top = new MainFrame {
contents = new FlowPanel {
val button1 = new Button(Action("1") {
println("Button 1 pressed")
})
contents += button1
val button2 = new Button(Action("2") {
println("Button 2 pressed")
})
contents += button2
val buttons = Map("1" -> button1, "2" -> button2)
buttons.foreach({ eventButton =>
listenTo(eventButton._2)
reactions += {
case ButtonClicked(eventButton._2) => {
println("Event triggered: " + eventButton._1)
}
}
})
}
}
}
Prints (correct):
Button 1 pressed
Event triggered: 1
Button 2 pressed
Event triggered: 2
In the line
reactions += {
case ButtonClicked(keypadEventButton) => {
you are creating a new val keypadEventButtonand assigning it to whatever is inside ButtonClicked(). Changing the line to case ButtonClicked(abstractButton) will still work and display same problem.
I'm guessing you are expecting this to match the use of keypadEventButton on the preceding lines. You probably want to create one reaction and then use the abstractButton to tell what button has been pressed.
#andy is correct. A good IDE like IDEA will highlight "suspicious shadowing by a variable pattern", since you're binding a new variable in the pattern match. Scala allows you to shadow variables as much as you want, within nested code blocks, for instance:
scala> val a = 1; {val a = 2; println(a)}; println(a)
2
1
a: Int = 1
So what does the following return?
val a = 1
2 match {
case a => "it was 1"
case _ => "something else"
}
It returns "it was 1" because a is shadowed. Now try:
2 match {
case `a` => "it was 1"
case _ => "something else"
}
This returns "something else" because we used backticks to refer to the value of he previously defined variable. (Also try this where the variable begins with a capital letter...)
So you just need to add backticks, i.e.
case ButtonClicked(`button`) => {

Scala Lift - Dynamically called function

I've got a function which loads various models, and currently have this kind of setup:
if(message == "user") {
var model = User.findAll(
("room" -> "demo")
)
} else if (message == "chat") {
var model = Chat.findAll(
("room" -> "demo")
)
}
This is really clunky as I aim to add lots more models in future, I know in javascript you can do something like this:
var models = {
"user" : load_user,
"chat" : load_chat
}
Where "load_user" and "load_chat" would load the respective models, and so I can streamline the whole thing by doing:
var model = models[message]();
Is there a way I can do something similar in Scala, so I can have a simple function which just passes the "message" var to a List or Object of some kind to return the relevant data?
Thanks in advance for any help, much appreciated :)
In Scala you can do:
val model = message match {
case "user" => loadUser() // custom function
case "chat" => loadChat() // another custom function
case _ => handleFailure()
}
You can as well work with a Map like you did in your JavaScript example like so:
scala> def loadUser() = 1 // custom function
loadUser: Int
scala> def loadChat() = 2 // another custom function
loadChat: Int
scala> val foo = Map("user" -> loadUser _, "chat" -> loadChat _)
foo: scala.collection.immutable.Map[java.lang.String,() => Int] = Map(user -> <function0>, chat -> <function0>)
scala> foo("user")()
res1: Int = 1
Pay attention to the use of "_" in order to prevent evaluation of loadUser or loadChat when creating the map.
Personally, I'd stick with pattern matching.