<@UDV7KQT43> The release notes for build228 ask ab...
# compose-web
g
@Oleksandr Karpovich [JB] The release notes for build228 ask about use cases for accessing the event target on an input. One use case is to limit the types of characters that can be entered into a TextInput.
🙏 1
Here is an example for a use case to only allow digits to be entered into a text input (for example, for a credit card number entry field). The first version will do this however the 2nd version will allow any character to be entered. I believe this is because in the 2nd version the filter prevents the watched variable text2 from changing, so the function does not recompose, and the browser has a backing field to the textbox which holds the temporary state of the input element exactly as entered by the user. In the first version, this backing field is overwritten on each input with a filtered version of the text, which contains only digits and effectively prevents entering disallowed characters.
Copy code
@Composable
fun FilteredInput() {
    var text by remember { mutableStateOf("0") }
    var text2 by remember { mutableStateOf("0") }

    Div {
        Text("Using event.target.value")
        TextInput(value = text) {
            onInput { event ->
                text = event.value.filterToIntegerChars()
                event.target.value = text
            }
        }
    }

    Div {
        Text("Not using event.target.value")
        TextInput(value = text2) {
            onInput { event ->
                text2 = event.value.filterToIntegerChars()
            }
        }
    }
}

fun String.filterToIntegerChars(): String {
    return this.filter {
        it in '0'..'9'
    }
}
h
Just another problem with `Input`s managing its own state. Your solution is more a hack 🙂 Maybe Compose could force recompose each
Input
after
onChange
, ignoring the (not managed) value state... Interesting, even
neverEqualsPolicy()
does not work too
o
thank you Greg for the use cases (this one and with a checkbox)! I will play with these cases as well.
neverEqualsPolicy()
 does not work too
I think it works - it makes the recomposable scope get recomposed. But text2 value remains the same, so TextInput does't get recomposed (even though the outer scope is recomposing) because it has its own recomposable scope. Philip, you're right that the main issue here is that Input has its own internal state. here is an option, which is rather imperative than declarative:
Copy code
Div {
        Text("Not using event.target.value")
        TextInput(value = text2) {
            onBeforeInput {
                val char = it.data?.get(0)
                if(char != null && char !in '0'..'9') {
                    it.preventDefault()
                }
            }
            onInput { event ->
                text2 = event.value//.filterToIntegerChars()
            }
        }
    }
its advantage is that you can avoid using
event.target.text=...
which also was imperative. I think ideally this should work:
Copy code
onInput { event ->
                text2 = event.value.filterToIntegerChars()
            }
so we'll think what we can do about it just in case: NumberInput would work as you expect. But as for general use cases, you have the point.
g
I found another trick, set an attribute: attr("inputmode", "decimal")
Unfortunately this still doesn't prevent the user from entering non-numbers, such as 1.05.07.
Another use case needing access to the event target is the FileInput. Here is a way I have found to get access to the FileList:
Copy code
onChange {
    it.target.files?.let { fileList ->
        fileList[0]?.let { file ->
            <http://console.info|console.info>("selected file: ${file.name}")
        }
    }
}
The SyntheticInputEvent has a dataTransfer member, but I tried the below code which can also be used with a drop handler, but dataTransfer is null so it doesn't work.
Copy code
onInput{
    it.dataTransfer?.apply{
        val file: File? = files[0]
        if (file != null) {
            <http://console.info|console.info>("selected file: ${file.name}")
        } else {
            <http://console.info|console.info>("null file")
        }
    }
}
Another useful feature would be to somehow provide access to the underlying Input element's click() function, outside of an event handler on the element itself, so the click() function can be called to open the file picker, e.g. from an event handler on a button.
h
🙏 1