Async function called in a slinky/scalajs setup causes render to fail - scala.js

I'm unsure if this is a question for the general scala.js world or specifically slinky/slinky-native/slinky-hot
FYI, using
libraryDependencies += "me.shadaj" %%% "slinky-native" % "0.7.0"
libraryDependencies += "me.shadaj" %%% "slinky-hot" % "0.7.0"
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1")
I have an async function that returns login status with facebook
object LoginController {
def userFBLoginStatus: Future[LoginStatus] = {
FBAccessToken.getCurrentAccessToken.toFuture.map {
This internally calls a static function import from a javascript library
#native
#JSImport("react-native-fbsdk-next", JSImport.Default)
object FBAccessToken extends js.Object {
def getCurrentAccessToken: Promise[FBAccessToken | Null] = native
}
I call it in the main App component like so
#react class App extends Component {
type Props = Unit
type State = Boolean
def initialState = false
override def componentDidMount(): Unit = {
LoginController.userFBLoginStatus.map {
case LoggedIn => setState(true)
case LoggedOut => setState(false)
}
}
override def render(): ReactElement = {
if (state)
View(//welcome view)
else
View(//login page view)
The intention being that the view should change based on the output of the async function called in componentDidMount
sbt clean fastOptJS works fine but on rendering the app fails with
TypeError: undefined is not an object (evaluating 'this$.then')
This error is located at:
in App$ (at renderApplication.js:50)
in RCTView (at View.js:32)
in View (at AppContainer.js:92)
in RCTView (at View.js:32)
in View (at AppContainer.js:119)
in AppContainer (at renderApplication.js:43)
in DanceApp(RootComponent) (at renderApplication.js:60)
ERROR TypeError: undefined is not an object (evaluating 'this$.then')
If I comment out the async call in componentDidMount, the rendering works fine. See the (evaluating 'this$.then'), looks like its looking for a Promise.then?
Equivalent react code works e.g. where fetchWells() returns a javascript Promise
class Map extends Component {
constructor () {
super()
this.state = { wells: [] }
}
componentDidMount() {
this.props.fetchWells()
.then(res => this.setState({ wells: res.wells }) )
}
render () {
const { wells } = this.state
return wells.length ? this.renderWells() : (
<span>Loading wells...</span>
)
}
}
UPDATE: following the answer changing it to works!
#native
#JSImport("react-native-fbsdk-next", "AccessToken")
object FBAccessToken extends js.Object {
def getCurrentAccessToken(): Promise[FBAccessToken | Null] = native
}

From what I see in the documentation here, getCurrentAccessToken is a function returning a Promise, so it needs to be declared with ():
def getCurrentAccessToken(): Promise[FBAccessToken | Null] = native
It's also worth double-checking the import path, since that documentation suggests that it's inside a top-level object called AccessToken, and not as the default export.

Related

How can I log a warning when I "halt()" in Scalatra?

In my Scalatra routes, I often use halt() to fail fast:
val user: User = userRepository.getUserById(params("userId"))
.getOrElse {
logger.warn(s"Unknown user: $userId")
halt(404, s"Unknown user: $userId")
}
As shown in the example, I also want to log a warning in those cases. But I'd like to avoid the code duplication between the halt() and the logger. It would be a lot cleaner to simply do:
val user: User = userRepository.getUserById(params("userId"))
.getOrElse(halt(404, s"Unknown user: $userId"))
What would be the best way of logging all "HaltExceptions" in a cross-cutting manner ?
I've considered:
1) Overriding the halt() method in my route:
override def halt[T](status: Integer, body: T, headers: Map[String, String])(implicit evidence$1: Manifest[T]): Nothing = {
logger.warn(s"Halting with status $status and message: $body")
super.halt(status, body, headers)
}
Aside from the weird method signature, I don't really like this approach, because I could be calling the real halt() by mistake instead of the overridden method, for example if I'm halting outside the route. In this case, no warning would be logged.
2) Use trap() to log all error responses:
trap(400 to 600) {
logger.warn(s"Error returned with status $status and body ${extractBodyInSomeWay()}")
}
But I'm not sure it's the best approach, especially since it adds 201 routes to the _statusRoutes Map (one mapping for each integer in the range...). I also don't know how to extract the body here ?
3) Enable some kind of response logging in Jetty for specific status codes ?
What would be the best approach to do this? Am I even approaching this correctly?
The easiest solution is doing it in a servlet filter like below:
package org.scalatra.example
import javax.servlet._
import javax.servlet.http.HttpServletResponse
class LoggingFilter extends Filter {
override def init(filterConfig: FilterConfig): Unit = ()
override def destroy(): Unit = ()
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
chain.doFilter(request, response)
val status = response.asInstanceOf[HttpServletResponse].getStatus
if (status >= 400 && status <= 600) {
// Do logging here!
}
}
}
Register this filter in your Bootstrap class (or it's possible even in web.xml):
package org.scalatra.example
import org.scalatra._
import javax.servlet.ServletContext
class ScalatraBootstrap extends LifeCycle {
override def init(context: ServletContext): Unit = {
context.addFilter("loggingFilter", new LoggingFilter())
context.getFilterRegistration("loggingFilter")
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
// mount your servlets or filters
...
}
}
In my opinion, Scalatra should provide a way to trap halting easier essentially. In fact, there is a method named renderHaltException in ScalatraBase, it looks to be possible to add logging by overriding this method at a glance:
https://github.com/scalatra/scalatra/blob/cec3f75e3484f2233274b1af900f078eb15c35b1/core/src/main/scala/org/scalatra/ScalatraBase.scala#L512
However we can't do it actually because HaltException is package private and it can be accessed inside of org.scalatra package only. I wonder HaltException should be public.

Class Member becomes undefined

I am quite new to TypeScript and I experience a strange problem at the moment. I create an instance of my main class when the document is ready, using JQuery.
var main: MainApp;
$(document).ready(function () {
main = new MainApp();
});
The simplified MainApp Class:
class MainApp {
// Helper Objects
net: AppNetworking;
urlHelper: UrlHelper;
cat: Category;
// Construction
constructor() {
this.net = new AppNetworking();
this.urlHelper = new UrlHelper();
}
// Ajax Callback with Data needed to initialize the "cat" object
private AjaxCallback(categoryData){
this.cat = new Category(categoryData);
}
// Event Handler for an HTML-Element
// As it would be called anonymously in JS I decided to make it a static function
static onClickSendButton(): void{
// Using some members of the MainApp
var hostUrl: string = main.urlHelper.getQueryStringParam("HostUrl");
if (main.cat.isValidCategory()) {
main.sendCategory();
}
}
sendCategory(): boolean {
// Some logic to send data via AJAX
}
}
The function is being registered to the onClick Event of a Button on construction of the MainApp Class.
$("#btnSendCat").click(MainApp.onClickSendButton);
When the function onClickSendButton() gets called, it produces the error:
Uncaught TypeError: Cannot read property 'isValidCategory' of undefined
When debugging, the urlHelper Instance is defined, but the cat Instance is undefined. As I do not touch the instance cat anywhere in my application, I'm really confused how it is undefined. Also when checking the main variable all members are defined!
Am I doing anything illegal here? Could there be issues with that code?
Completely revised answer. I actually answered with the two most common scenarios for this error, but actually your problem is different.
The usual answers are
Make sure you are referencing .js files, not .ts files
Make sure you are loading scripts in the correct order
In your case, this is not the problem and your code is sufficient to recreate the issue.
I have put together the following test, filling in the blanks - and it works as expected.
app.ts
declare var main: MainApp;
class AppNetworking {
}
class UrlHelper {
getQueryStringParam(input: string) {
console.log('Got here getQueryStringParam');
return input;
}
}
class Category {
isValidCategory() {
console.log('Got here isValidCategory');
return true;
}
}
class MainApp {
// Helper Objects
net: AppNetworking;
urlHelper: UrlHelper;
cat: Category;
// Construction
constructor() {
this.net = new AppNetworking();
this.cat = new Category();
this.urlHelper = new UrlHelper();
}
// Event Handler for an HTML-Element
// As it would be called anonymously in JS I decided to make it a static function
static onClickSendButton(): void{
// Using some members of the MainApp
var hostUrl: string = main.urlHelper.getQueryStringParam("HostUrl");
if (main.cat.isValidCategory()) {
main.sendCategory();
}
}
sendCategory(): boolean {
// Some logic to send data via AJAX
return true;
}
}
index.html snip
<div id="btnSendCat">BTN SEND CAT</div>
<script src="app.js"></script>
<script src="Scripts/jquery-2.1.1.min.js"></script>
<script>
var main;
$(document).ready(function () {
main = new MainApp();
$("#btnSendCat").click(MainApp.onClickSendButton);
});
</script>
The result of running this test is the following output in the console window:
"Got here getQueryStringParam" app.js:10
"Got here isValidCategory" app.js:19
I left some important parts of my App out, I'm sorry. Later in the project I used to reinitialize that Category Object. This re initialization was done in an AJAX-Callback Function. This function runs outside of my Object and this wont be my MainApp Class but the Window. I think it's what you call an anonymous function in JavaScript.
I fixed that issue by taking use of my global main Variable
class MainApp {
// Called anonymous so it should be a static function
private AjaxCallback(categoryData){
// this.cat = new Category(categoryData); ! this will be the Window Instance and not a MainApp Instance
main.cat = new Category(categoryData); // Initialization using the "main" variable
}
}
The call in my onClickSendButton Method to this.cat succeeds now, as this.cat was reinitialized correctly.
This video helped me a lot in my researches: Understanding "this" in TypeScript

Xtext: JvmModelInferrer initialize field

I'd like to generate a List field into my class generated from my DSL and initialize it like this:
private List<MyObject> myObjects= Lists.newArrayList();
The only way I know for this, is to append some text to the initializer:
members += appRule.toField("myObjects", appRule.newTypeRef(List, it.newTypeRef(MyObject))) [
initializer = [append('''Lists.newArrayList()''')]
]
However, using this approach the JvmModelInferrer won't import the Guava Strings library, thus will raise compilation issues. Is there any way to overcome this obstacle?
If I understand your issue (as you are referring to the Guava Strings library that is not used in the code :) ), your problem is, that the class reference Lists is not imported.
For such constructs, we have a helper method in EMF-IncQuery that serializes a type reference the same way parameters are serialized. This functionality relies on the injectable TypeReferenceSerializer class.
def referClass(ITreeAppendable appendable, EObject ctx, Class<?> clazz, JvmTypeReference... typeArgs) {
val ref = ctx.newTypeRef(clazz, typeArgs)
if (ref != null) {
appendable.serialize(ref, ctx)
} else {
//Class resolution error - error handling required here
//A fallback to writing out the fqn of the class
appendable.append(clazz.canonicalName)
}
}
def serialize(ITreeAppendable appendable, JvmTypeReference ref, EObject ctx) {
typeReferenceSerializer.serialize(ref, ctx, appendable)
}

Adding functionality to Grails restfulcontroller

I'm having a very simple restful controller, which looks like this:
class PersonController extends RestfulController<Person> {
static responseFormats = ['json', 'xml']
PersonController() {
super(Person)
}
}
However, now I want to add a search option to this. What is the Grails way of making this possible?
I thought of adding the following:
def search(Map params) {
println params
}
But that makes Grails (2.3) crash (| Error Fatal error during compilation org.apache.tools.ant.BuildException: Compilation Failed (Use --stacktrace to see the full trace)).
So what is the right way of adding this? I'm looking for some solution which I can call using http://localhost:8080/foo/person/search?q=erik
This is my UrlMappings:
static mappings = {
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
"/rest/persons"(resources:'Person')
I've changed the above to:
def search() {
println params
}
And that doesn't give the compilation error anymore, but I still get this error:
TypeMismatchException occurred when processing request: [GET] /declaratie-web/rest/medicaties/search - parameters:
q: erik
Provided id of the wrong type for class nl.Person. Expected: class java.lang.Long, got class java.lang.String. Stacktrace follows:
org.hibernate.TypeMismatchException: Provided id of the wrong type for class nl.Person. Expected: class java.lang.Long, got class java.lang.String
I also found out that it doesn't matter how I call the controller:
http://localhost:8080/foo/person/search?q=erik
http://localhost:8080/foo/person/search222?q=erik
http://localhost:8080/foo/person/search39839329?q=erik
All fails with the above error, so it seems my method is ignored (maybe caused by my URLmapping?)
You really aren't being RESTful by doing that. q should just be a parameter for the index action. You can override that method to include your functionality.
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
def c = Person.createCriteria()
def results = c.list(params) {
//Your criteria here with params.q
}
respond results, model:[personCount: results.totalCount]
}
#james-kleeh solution is right, but you can do it more clean by override the listAllResources method which is called by index
#Override
protected List<Payment> listAllResources(Map params) {
Person.createCriteria().list(params) {
// Your criteria here with params.q
}
}

Why can't I instantiate a Groovy class from another Groovy class?

I have two classes. One.groovy:
class One {
One() {}
def someMethod(String hey) {
println(hey)
}
}
And Two.groovy:
class Two {
def one
Two() {
Class groovy = ((GroovyClassLoader) this.class.classLoader).parseClass("One.groovy")
one = groovy.newInstance()
one.someMethod("Yo!")
}
}
I instantiate Two with something like this:
GroovyClassLoader gcl = new GroovyClassLoader();
Class cl = gcl.parseClass(new File("Two.groovy"));
Object instance = cl.newInstance();
But now I get groovy.lang.MissingMethodException: No signature of method: script13561062248721121730020.someMethod() is applicable for argument types: (java.lang.String) values: [Yo!]
Any ideas?
Seems like it is occurring due to the groovy class loader method being called: the string one is to parse a script in text format. Using the File one worked here:
class Two {
def one
Two() {
Class groovy = ((GroovyClassLoader) this.class.classLoader).parseClass("One.groovy")
assert groovy.superclass == Script // whoops, not what we wanted
Class groovy2 = ((GroovyClassLoader) this.class.classLoader).parseClass(new File("One.groovy"))
one = groovy2.newInstance()
assert one.class == One // now we are talking :-)
one.someMethod("Yo!") // prints fine
}
}