When writing code for JavaScript with KotlinJs, we usually have to handle the this
carefully.
So we usually need this function:
inline fun <T> thisAs() = js("this")
and use it in somewhere:
this.click = {
thisAs<MyVueComponent.Data>().username = "changed"
}
Due to VueJs' inner logic, I have to generate JavaScript code this.username
(this
is hardcode), but which is ugly, I don't want to write thisAs<MyVueComponent.Data>().username
every time I need a JavaScript this
.
So I tried to improve it by defining a function defineMethods
, like following:
external interface VueComponent<DATA, METHODS> {}
fun <DATA, METHODS> VueComponent<DATA, METHODS>.defineMethods(block: M.(() -> DATA) -> Unit): METHODS {
val methods = jsObj<M>()
block(methods, ::thisAs)
return methods
}
And I hope the inline function thisAs
is still a inline function even if I pass it to the block
as ::thisAs
, so I can rewrite the code like this:
this.methods = this.defineMethods { data ->
this.click = {
data().username += "changed!" // I want to write `data()` to generate a JavaScript `this`
}
}
But sadly, the function data
(which is ::thisAs
) is not inline anymore, the code is not working properly.
Is there any way to solve this?
Update:
I'm facing the same problem. I have a similar function for casting Javascript's this object as you do. However, I've extended it with a lambda function with a receiver (the same thing that is used for type-safe builders in Kotlin except that my function returns Unit and is inlined).
inline fun <T> withJsThis(body: T.() -> Unit) {
js("this").unsafeCast<T>().body()
}
It can be easily used in methods as well as in lifecycle hooks (created, mounted, etc.). It needs one extra line of code but it's clean and produce exactly the expected this.variable when translated from Kotlin to Javascript.
class LoginComponent {
class Data {
var username: String = "[email protected]"
var password: String = ""
}
val data = {
Data()
}
val template = """<form>
<input type="text" v-model="username" placeholder="Email" /><br />
<input type="password" v-model="password" placeholder="Password" /><br />
<input type="submit" v-on:click.prevent="showInfo" />
</form>"""
val methods = object {
val showInfo: () -> Unit = {
withJsThis<Data> {
console.log("METHOD: USER = $username, PASS = $password")
}
}
}
fun mounted() {
withJsThis<Data> {
console.log("MOUNTED: USER = $username, PASS = $password")
}
}
}