I have some Scala.js code which works fine when the target-browser is Chrome but not when it is Firefox. I have been told that I should not use srcElement but instead target. However I can't work out how to access target.
private def TD(locId: Location, personId: Person): HTMLTableCellElement = {
val res = td(style := myPeopleTable_td).render
res.onclick = { (e: dom.Event) => c.clickTD(e.srcElement)}
res.id = Id(locId, personId).toString
cells += (Id(locId, personId) -> res)
res
}
def clickTD(element: Element): Unit = {
val idOption = Id.parse(element.id)
idOption.foreach(id => {
locIdx = Location.indexOf(id.locId)
personIdx = Person.indexOf(id.personId)
localSync()
v.personValueBox.focus()
})
}
By way of explanation c is the Controller and v is the View. The first method is in the View and and second is in the Controller. The first method is setting up a cell in an HTML table, including what happens when the user clicks on the cell. In clickTD() the code needs to know what has been clicked - it needs to somehow get at the HTML element's 'id' which has been pre-set so that model objects can be looked up.
If I try to replace e.srcElement with e.target this is the result:
[info] Compiling 1 Scala source to C:\dev\v2\atmosphere\js\target\scala2.11\classes...
[error] C:\dev\v2\atmosphere\js\src\main\scala\simple\View.scala:145: type mismatch;
[error] found : org.scalajs.dom.raw.EventTarget
[error] required: org.scalajs.dom.Element
[error] (which expands to) org.scalajs.dom.raw.Element
[error] res.onclick = { (e: dom.Event) => c.clickTD(e.target)}
[error] ^
[error] one error found
[error] (atmosphereJS/compile:compile) Compilation failed
[error] Total time: 1 s, completed 15-Jun-2015 02:14:41
Thanks #ochrons, your suggestion of putting res instead of e.target works. Unfortunately with the other controls I'm having an 'order of instantiation' problem. In the following I am trying to move the commented-out line to where it can see res:
private def button_TD_Tog(idT: Row): HTMLTableCellElement = {
val idsStr: String = idT.toString
val buttonWidget = button(
style := myToggleButton,
name := "button",
id := idsStr
//onclick := { (e: dom.Event) => c.clickToggleButton(e.srcElement)}
)
val res = td(style := myToggleButtonCol, buttonWidget).render
buttonWidget.onclick = { (e: dom.Event) => c.clickToggleButton(res)}
res.id = idsStr
buttonTDs += (idT -> res)
res
}
I (think I) need both res and buttonWidget to exist before assigning to onclick, but the compiler does not recognize onclick. I found myself searching for some API documentation to help me out here. I doubt that it exists (I've found Scala.js documentation to be excellent, but not comprehensive). Perhaps I should be looking to the DOM itself...
And so now to an example of a simple general solution that seems to work for all cases, as per your advice to force types:
c.clickPushButton(e.target.asInstanceOf[dom.Element])}
If you only need to know that your td element has been clicked, then you can directly use c.clickTD(res) in the closure. But if there are elements within the td that can be clicked, then you can use the event's target by just casting it as an Element. In such case your inner elements must bubble the click event up to the td element.
The DOM is not entirely type-safe, so sometimes you just need to "know" what you are dealing with. The documentation typically assumes JavaScript, so they don't really care about things like type-safety.
Related
I am writing a parser in which I have the following function:
def lastop:(Either[RDD[(Int,Array[Float])], Float], Either[RDD[(Int,Array[Float])], Float]) => RDD[(Int,Array[Float])] = add
In which "add" is a function to perform addition. Then I want to use it in my program like the following line:
terms.foreach(t =>
t match { case nums ~ op => lastop = op; stack = reduce(stack ++ nums, op)}
I am getting the following error:
[error] /home/mahsa/calculator/temp/ScalaParser.scala:183: reassignment to val
[error] t match { case nums ~ op => lastop = op; stack = reduce(stack ++ nums, op)}
[error] ^
Can't figure how to solve this error!
You want to store a changing reference to the function you want to invoke. If you are storing and reassigning something, that implies you need a var, not a val or a def. Try declaring lastop like:
var lastop:(Either[RDD[(Int,Array[Float])], Float], Either[RDD[(Int,Array[Float])], Float]) => RDD[(Int,Array[Float])] = add
Note that you will still need to invoke lastop like a function, since retrieving the var's value will return a function. It's a subtle but significant difference.
My feeble Scala skills have me baffled about the right way to do something. The code below compiles (and works) only when I include the t match {... line. If I eliminate that line and of course, the diagnostic println on the preceding line, I get the compile time error as shown. Apparently the compiler regards the return of the fold as Unit (surprise to me). That might be reasonable, but I don't understand it. Would someone please enlighten me about a better way to code this, and perhaps give me more insight?
[error] /home/bill/activator/cherry/app/controllers/Application.scala:34: type mismatch;
[error] found : Unit
[error] required: play.api.mvc.Result
[error] }
[error] ^
Source:
def ptweets = Action { implicit request =>
import play.api.data._
val rqForm = Form(Forms.mapping(
"k" -> Forms.number,
"who" -> Forms.text,
"what" -> Forms.text)(TweetInquiry.apply)(TweetInquiry.unapply))
val t = rqForm.bindFromRequest.fold(
formWithErrors => BadRequest("That's not good"),
rq => Ok((views.html.properForm("POST tweets TBD.")(Html("<em>Blah</em>"))))
) // expect a play.api.mvc.Result
println(t.getClass.getName) // this confirms it in both run-time cases
t match { case v:Result => v } // yet this is required for compile
}
as m-z said in the comments, change
val t = rqForm.bindFromRequest.fold(
formWithErrors => BadRequest("That's not good"),
rq => Ok((views.html.properForm("POST tweets TBD.")(Html("<em>Blah</em>"))))
) // expect a play.api.mvc.Result
println(t.getClass.getName) // this confirms it in both run-time cases
t match { case v:Result => v } // yet this is required for compile
to just:
rqForm.bindFromRequest.fold(
formWithErrors => BadRequest("That's not good"),
rq => Ok((views.html.properForm("POST tweets TBD.")(Html("<em>Blah</em>"))))
)
The fold is evaluating to a Result, but in the code you posted, you're assigning that Result to the value t. Thus, instead of the Action block evaluating to the result of the fold, it's evaluating to an assignment (which is Unit, see here).
i'm writing a play 2.3 application using secure social and reactivemongo library, with scala.
Now i'm trying to implement the UserService[T] trait but i'm getting compile errors on the updatePasswordInfo method.
This is the method:
def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
implicit val passwordInfoFormat = Json.format[PasswordInfo]
//the document query
val query = Json.obj("providerId" -> user.providerId,
"userId" -> user.userId
)
//search if the user exists
val futureUser: Future[Option[LoginUser]] = UserServiceLogin.find(query).one
futureUser map {
case Some(x) => val newPassword = Json.obj("passswordInfo" -> info)// the new password
UserServiceLogin.update(query, newPassword) //update the document
val newDocument: Future[Option[LoginUser]] = UserServiceLogin.find(query).one
newDocument map {
case Some(x) => x
case None => None
} //return the new LoginUser
case None => None
}
}
And this is the compiler error:
/Users/alberto/git/recommendation-system/app/security/UserService.scala:203: type mismatch;
[error] found : scala.concurrent.Future[Product with Serializable]
[error] required: Option[securesocial.core.BasicProfile]
[error] newDocument map {
What's wrong?
If you map over a Future[A] you'll end up with a Future[B], where T is the type returned from the lambda you pass to map.
Since that lambda is returning a Future[B] in this case you end up with a Future[Future[B]], which doesn't match the expected type.
The easy fix is to use a flatMap, which takes a lambda going from A to Future[B].
Also, you're returning an Option[LoginUser] but the method is expected to return an Option[BasicProfile]. The compiler inferred a common supertype, which in this case is Product with Serializable, since they're both case classes.
To sum it up
scala.concurrent.Future[Product with Serializable]
^_____________________^^_________________________^
1 2
use flatMap instead of map
return a BasicProfile instead of LoginUser, or change the method return type to Future[Option[LoginUser]]
By the way, there's a lot of room for improvement, as you could use a for-comprehension and the OptionT monad transformer from scalaz to make the whole thing prettier.
Here's an example
import scalaz._; import Scalaz._
val newPassword = Json.obj("passswordInfo" -> info)
(for {
// this is done only for failing fast in case the user doesn't exist
_ <- optionT(UserServiceLogin.find(query).one)
_ <- optionT(Future.successful(Some(UserServiceLogin.update(query, newPassword))))
updatedUser <- optionT(UserServiceLogin.find(query).one)
} yield updatedUser).run
By the way, this works under the assumption that update is a sync call, which might (and I hope) not be the case. If it returns a Future[T] just change the code to
_ <- optionT(UserServiceLogin.update(query, newPassword).map(Some(_)))
or if it already returns a Future[Option[T]]
_ <- optionT(UserServiceLogin.update(query, newPassword))
If you really want to do the find to fail fast (though it is not so useful) and then reload the updated user from the database, something like this should do without the need for using scalaz :
def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
implicit val passwordInfoFormat = Json.format[PasswordInfo]
//the document query
val query = Json.obj("providerId" -> user.providerId,
"userId" -> user.userId)
val newPassword = Json.obj("passswordInfo" -> info)
//update the document
for {
userO <- UserServiceLogin.find(query).one[BasicProfile] //fail fast (not sure this is really useful)
updatedUser<-UserServiceLogin.update(query, newPassword).map(_=>userO).recover{case _ =>None}
actualUser <- UserServiceLogin.find(query).one[BasicProfile]
} yield actualUser
}
There are several ways in which your code can be improved.
For example, you don't need to find the user before firing the query.
Also, it would be good to check if your query actually succeeded (if the API allows it).
Third, I am not sure in which way LoginUser corresponds to the BasicProfile. Your code doesn't seem to do any kind of conversion. If LoginUser is a subclass of BasicProfile, or can be cast to BasicProfile somehow, you can try something like this:
def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
implicit val passwordInfoFormat = Json.format[PasswordInfo]
//the document query
val query = Json.obj("providerId" -> user.providerId,
"userId" -> user.userId
)
UserServiceLogin.update(query, newPassword) //update the document
for {
user <- UserServiceLogin.find(query).one
} yield user.map(_.asInstanceOf[BasicProfile]) //return the new LoginUser
}
How do I use for comprehensions with if guard?
type Error = String
type Success = String
def csrfValidation(session:Session, body:JsValue):Either[Error,Success] = {
val csrfRet = for (csrfSession <- csrfStateSessionValidation(session).right;
csrfReq <- csrfStateReqBodyValidation(body).right if (csrfSession == csrfReq)) yield (csrfReq)
if (csrfRet.isRight)
Right(csrfRet)
else {
Logger.warn("request and session csrf is not the same")
Left("Oops,something went wrong, request and session csrf is not the same")
}
}
I got this error when using it.
'withFilter' method does not yet exist on scala.util.Either.RightProjection[Error,Success], using `filter' method instead
EDIT:
I got another error. I think it returns an option result when if guard is used.
[error] type mismatch;
[error] found : Option[scala.util.Either[Nothing,controllers.ProfileApiV1.Success]]
[error] required: scala.util.Either[?,?]
[error] csrfReq <- csrfStateReqBodyValidation(body).right if (csrfSession == csrfReq)) yield (csrfReq)
EDIT2
This is what I did to fix above error. I also move if-guard to later process.
val result = for {
foo <- Right[String,String]("teststring").right
bar <- Right[String,String]("teststring").right
} yield (foo, bar)
result fold (
ex => Left("Operation failed with " + ex),
v => v match {
case (x,y) =>
if (x == y) Right(x)
else Left("value is different")
}
)
I believe what you are seeing is a compiler warning and not an actual error. The RightProjection does not support withFilter which is what is "preferred" (but not yet required) for a guard condition, so plain old filter is used instead. As to the difference to these functions and why this came about, check out the link below for an explanation.
http://scala-programming-language.1934581.n4.nabble.com/Rethinking-filter-td2009215.html#a2009218
Add either a semi-colon or a new line just before the if.
I have a test along these lines:
httpClient.post(anyString, anyString) returns (first, second)
//do my thing
there were two(httpClient).post(anyString, anyString)
This works fine, but I want to verify that the first call passes a different body than the second call. The body is rather big and I don't want to do precise matching on a strict example. I've tried this:
there was one(httpClientMock).postMessage(anyString, argThat(contain("FOO"))
there was one(httpClientMock).postMessage(anyString, argThat(contain("FOO"))
That makes Mockito complain:
InvalidUseOfMatchersException:
[error] Invalid use of argument matchers!
[error] 2 matchers expected, 3 recorded:
I've also tried:
there was one(httpClientMock).postMessage(argThat(contain("foo")), argThat(contain("FOO")))
there was one(httpClientMock).postMessage(argThat(contain("foo")), argThat(contain("FOO")))
which results in:
Wanted 1 time:
[error] -> ...
[error] But was 2 times. Undesired invocation: ...
It seems to me that something like this should be possible, but I can't seem to figure it out. Insights?
I think that this is more of a problem with Mockito to begin with. When you're using Mockito with specs2 and you're in doubt, always drop down to the direct Mockito API:
// simplified httpClient with only one parameter
val httpClient = mock[HttpClient]
httpClient.post(anyString) returns ""
httpClient.post("s1")
httpClient.post("s2")
// forget specs2
// there was two(httpClient).post(anyString)
org.mockito.Mockito.verify(httpClient, org.mockito.Mockito.times(1)).post("s1")
// I guess that you don't want this to pass but it does
org.mockito.Mockito.verify(httpClient, org.mockito.Mockito.times(1)).post("s1")
One possible way around this is to define a matcher that will check the successive values of an argument:
there was two(httpClient).post(consecutiveValues(===("s1"), ===("s2")))
And the consecutiveValues matcher is defined as such:
import matcher._
import MatcherImplicits._
// return a matcher that will check a value against a different
// `expected` matcher each time it is invoked
def consecutiveValues[T](expected: Matcher[T]*): Matcher[T] = {
// count the number of tested values
var i = -1
// store the results
var results: Seq[(Int, MatchResult[T])] = Seq()
def result(t: T) = {
i += 1
// with Mockito values are tested twice
// the first time we return the result (but also store it)
// the second time we return the computed result
if (i < expected.size) {
val mr = expected(i).apply(Expectable(t))
results = results :+ (i, mr)
mr
} else results(i - expected.size)._2
}
// return a function that is translated to a specs2 matcher
// thanks to implicits
// display the failing messages if there are any
(t: T) => (result(t).isSuccess,
results.filterNot(_._2.isSuccess).map { case (n, mr) =>
s"value $n is incorrect: ${mr.message}" }.mkString(", "))
}
You can test the code above. The failure messages are not the best but do the trick. In this situation:
httpClient.post("s1")
httpClient.post("s2")
there was two(httpClient).post(consecutiveValues(===("s1"), ===("s3")))
You will see:
[error] x test
[error] The mock was not called as expected:
[error] httpClient.post(
[error] value 1 is incorrect: 's2' is not equal to 's3'
[error] );
[error] Wanted 2 times:
[error] -> at ...
[error] But was 1 time:
[error] -> at ...