Is there any better way to write this Kotlin-javascript function?


#1

This is some javascript code, which is a directive in angularjs:

todomvc.directive('todoFocus', function todoFocus($timeout) {
    return function (scope, elem, attrs) {
        scope.$watch(attrs.todoFocus, function (newVal) {
            if (newVal) {
                $timeout(function () {
                    elem[0].focus();
                }, 0, false);
            }
        });
    };
});

And following is my Kotlin code:

val xxx = {(timeout: Timeout) ->
    {(scope: BaseScope, elem: Elem, attrs: Attrs) ->
        scope.`$watch`(attrs.todoFocus, { (newVal: Any) ->
            if(newVal as Boolean) {
                timeout({ elem[0].focus() }, 0, false);
            }
        })
    }
}

I think the kotlin-js version is less readable compared to the javascript one.

Is there any better way to write it? And if kotlin provide something like the javascript, that we can use fun(...) {} to define function literals just as javascript?


Questions about the Javascript API
#2

Hi! At first, you can omit type declarations if they are clear from the context (and therefore can be inferred). Like when you invoke

fun test(): (Int) -> Int {   fun inner(i: Int) = i   return ::inner }


#3

Which version of Kotlin are you using? My kotlin plugin doesn't allow me to write: `::myfun`


#4

And I can't ignore types since the `directive(...)` method is very flexiable, I often declare it as `Any` or something.

So in my own function, I have to declare the types explicitly.


#5

This feature is not supported now in js-backend.


#6

Can you show your declarations for todomvc.directive and scope.$watch


#7

Kotlin code:

  fun directive(name: String, def: (vararg Any)->Any): Unit

  fun $watch(exp: Any, todo: (vararg Any) -> Unit, deepWatch: Boolean)
  fun $watch(exp: Any, todo: (vararg Any) -> Unit)

#8

I think you have wrong declarations.

For $watch it should be look like:

native("$watch") fun watch(watchExpression: String): () -> Unit = js.noImpl

native("$watch")
fun watch(watchExpression: String, listener: String): () -> Unit = js.noImpl
native("$watch")
fun watch(watchExpression: String, listener: String, objectEquality: Boolean): () -> Unit = js.noImpl

native("$watch")
fun watch<T>(watchExpression: String, listener: (newValue: T, oldValue: T, scope: Scope) -> Any): () -> Unit = js.noImpl
native("$watch")
fun watch<T>(watchExpression: String, listener: (newValue: T, oldValue: T, scope: Scope) -> Any, objectEquality: Boolean): () -> Unit = js.noImpl

native("$watch")
fun watch(watchExpression: (scope: Scope) -> Any, listener: String): () -> Unit = js.noImpl
native("$watch")
fun watch(watchExpression: (scope: Scope) -> Any, listener: String, objectEquality: Boolean): () -> Unit = js.noImpl

native("$watch")
fun watch<T>(watchExpression: (scope: Scope) -> Any, listener: (newValue: T, oldValue: T, scope: Scope) -> Any): () -> Unit = js.noImpl
native("$watch")
fun watch<T>(watchExpression: (scope: Scope) -> Any, listener: (newValue: T, oldValue: T, scope: Scope) -> Any, objectEquality: Boolean): () -> Unit = js.noImpl


For directive i wrote:

native fun directive(name: String, def: (Timeout)->Any): (scope: Scope, elem: Elem, attrs: Attrs) -> Unit = js.noImpl

But I'm sure that `def` has the wrong type. Unfortunately I am not familiar with AngularJS for fix it.

And now you can write:

  directive("todoFocus") { (timeout: Timeout) ->   {(scope: Scope, elem: Elem, attrs: Attrs) ->            watch<Boolean>(attrs.todoFocus) { (newVal, oldVal, scope) ->            if(newVal) {                    timeout({ elem[0].focus() }, 0, false);            }            }   }   }

Next, you can add helper function postLink like:

inline fun postLink(f: (scope: Scope, elem: Elem, attrs: Attrs) -> Unit) = f

then:

  directive("string") { (timeout: Timeout) ->   postLink {(scope, elem, attrs) ->  // <-------------------------------------------- without type declaration here            watch<Boolean>(attrs.todoFocus) { (newVal, oldVal, scope) ->            if(newVal) {                    timeout({ elem[0].focus() }, 0, false);            }            }   }

But now it add unnecessary function call in generated js.


#9

Thanks for you detailed answer, yours are more accuracy than mine.

But methods like “directive”, we can give it a specific type to the parameters, since there are ofter many directives in a project, and each has a different types. So I use “vararg” in the declaration, and specify the types when I define a directive.


#10

Then the compiler can not help you detect type errors.

P.S. I’m a little updated prev message – made watch declaration better…


#11

Usefull resources: http://code.google.com/p/closure-compiler/source/browse/contrib/externs/angular.js http://code.google.com/p/closure-compiler/source/browse/contrib/externs/angular.jshttps://github.com/borisyankov/DefinitelyTyped/tree/master/angularjs


#12

It looks much better now, thank you again!

And I still hope Kotlin providing better syntax to allow me returning a function easily.


#13
I forgot, also you can write so:

``

watch<Boolean>(attrs.todoFocus) {

}


#14

I think such example would look difficult/ugly in almost any language.


#15

Thank you, even better :)


#16

Agree. And the javascript version may be the most readable one.