greencatsoft / scalajs-angular

AngularJS Binding for Scala.js
Apache License 2.0
252 stars 42 forks source link

UIRouter's State.resolve has wrong type? #99

Closed jeffmath closed 7 years ago

jeffmath commented 8 years ago

Per this UI-Router doc page, shouldn't the type of the resolve field in the State trait of UIRouter.scala be js.Dictionary[js.Function], instead of js.Function?

Also. could you provide an example of populating this field as part of a State definition? Preferrably, one which employs the $transition$ object as described in this section? I'm having a terrible time trying to specify these things properly in scalajs-angular. Thanks if you can help.

jeffmath commented 8 years ago

If I make the above change to type of the resolve field, I run into a further issue where I am unable to successfully inject a service I have created into the function which fetches the data for one of the resolve keys,

Here I have tried to boil down my relevant code to a much simpler example:

object MyApp extends JSApp
{
    def main(): Unit =
    {
        val module = Angular.module("app", [])
        module.factory[FooServiceFactory]
        module.config[StatesConfig]
    }
}

class StatesConfig($stateProvider: StateProvider) extends Config
{
    override def initialize(): Unit =
    {
        val state = State(url = "/", template = "Hello world!")
        state.resolve = 
            Dictionary[js.Function](
                "data" -> ((fooService: FooService) => fooService.foo())
            )
        $stateProvider.state("state1", state)
    }
}

@injectable("fooService")
class FooService(http: HttpService)
{
    def foo(): js.Any = { new js.Object() }
}

@injectable("fooService")
class FooServiceFactory(http: HttpService) extends Factory[FooService]
{
    override def apply() = new FooService(http)
}

If I were to run this app (though I'm not sure if the above compiles) and visit the only state, I would expect from my experiences with my actual app to get an error like this in the console:

client-jsdeps.js:24141 Error: [$injector:unpr] Unknown provider: fooService$2Provider <- fooService$2

Would you know why Angular can't find FooService's provider in this case?

mysticfall commented 7 years ago

You were correct in assuming that the resolve API has a wrong method signature. And it turned out that there were other methods - namely, Route.resolve, and ModalOptions.resolve - which shared such a problem with State.resolve.

Interestingly, only ModalOptions.resolve seems to allow assigning plain Scala/JS objects as its parameters directly, while all of them accepts a function with dependencies as its arguments.

However, we cannot just pass a js.Function, because it's not possible to resolve its dependencies in Scala code, and that's why the above example won't work as expected.

Fortunately, now we have ServiceDefinition API, which can be used to convert a Scala class to such a function, like Factory[A], for instance.

I just committed necessary changes to make such a usage possible, so now you can write:

class MyParameterFactory(foo: FooService) extends Factory[MyParam] {
  def apply(): MyParam = ???
}

//...

state.resolve = Dictionary("myParam" -> ServiceDefinition[MyParamFactory])

Please let me know if it doesn't work. I tested basic functionalities myself, but it's hard to be thorough without working test cases.

Sorry for the very late response!

jeffmath commented 7 years ago

Thank you for coming up with a fix. I will try it out soon, hopefully.