Scala.js event handling - scala

I'm writing a little Scala.Js app that uses two event handlers: one for an input field's onkeyup event and another for a button's onclick event.
The two event handlers share quite a bit of common coding, but as soon as I try to optimise the event handler coding into a single function that returns an event handler function, it compiles correctly, but events are no longer trapped in the browser.
In the following code in function main, the event handler for btn.onclick works fine, but the event handler for cityNameInput.onkeyup no longer works. All I did was copy the coding assigned directly to the event handler, and put it in a function called keystrokeHandler that returns a Function1[dom.Event, _]. This compiles OK, but the onkeyup event is no longer trapped in the browser.
def keystrokeHandler(userInput: String, responseDiv: dom.Element): Function1[dom.Event,_] =
(e: dom.Event) => {
// The city name must be at least 4 characters long
if (userInput.length > 3) {
responseDiv.innerHTML = ""
val xhr = buildXhrRequest(userInput, searchEndpoint)
xhr.onload = (e: dom.Event) => {
val data: js.Dynamic = js.JSON.parse(xhr.responseText)
// Can any cities be found?
if (data.count == 0)
// Nope, so show error message
responseDiv.appendChild(p(s"Cannot find any city names starting with ${userInput}").render)
else {
// Build a list of weather reports
buildSearchList(data, responseDiv)
}
}
// Send XHR request to OpenWeather
xhr.send()
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Main program
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#JSExport
def main(container: dom.html.Div): Unit = {
container.innerHTML = ""
val cityNameInput = input.render
val btn = button.render
val weatherDiv = div.render
cityNameInput.defaultValue = owmQueryParams.get("q").get
btn.textContent = "Go"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Button onclick event handler
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
btn.onclick = (e: dom.Event) => {
if (cityNameInput.value.length > 3) {
weatherDiv.innerHTML = ""
val xhr = buildXhrRequest(cityNameInput.value, weatherEndpoint)
xhr.onload = (e: dom.Event) => {
val data = js.JSON.parse(xhr.responseText)
// Can the city be found?
if (data.cod == "404")
// Nope, so show error message
weatherDiv.appendChild(p(s"City ${cityNameInput.value} not found").render)
else {
// So first add the div containing both the weather information
// and the empty div that will hold the slippy map.
// This is needed because Leaflet writes the map information to an
// existing DOM element
val report = new WeatherReportBuilder(data)
weatherDiv.appendChild(buildWeatherReport(report, 0))
buildSlippyMap("mapDiv0", report)
}
}
// Send XHR request to OpenWeather
xhr.send()
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Input field onkeyup event handler
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cityNameInput.onkeyup = keystrokeHandler(cityNameInput.value, weatherDiv)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Write HTML to the screen
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
container.appendChild(
div(
h1("Weather Report"),
table(
tr(td("Enter a city name (min 4 characters)"), td(cityNameInput)),
tr(td(), td(style := "text-align: right", btn))
),
weatherDiv
).render
)
}
What's the problem here?
Should the keystrokeHandler function return some special Scala.Js event handler type? Or is it something else?
Thanks
Chris W

I think the problem is here:
cityNameInput.onkeyup = keystrokeHandler(cityNameInput.value, weatherDiv)
The event handler is triggered, but the userInput is frozen to cityNameInput.value at the time the handler was created, instead of varying with the current value of cityNameInput.value. Indeed, that line is equivalent to
val userInput = cityNameInput.value
cityNameInput.onkeyup = keystrokeHandler(userInput, weatherDiv)
which makes it obvious that cityNameInput.value is only evaluated once.
Instead, you should give cityNameInput itself as parameter to keystrokeHandler, and access cityNameInput.value inside the anonymous function, so that it is evaluated every time the function (handler) is called.

Related

Concurrent requests with Vert.x

Consider the following Vert.x (version 3.5.3) code.
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
Handler<HttpServerRequest> handler = req -> {
System.out.println(req.path() + " - start");
vertx.executeBlocking(f -> {
sleep(5000);
f.complete();
}, false, ar -> {
req.response().end();
System.out.println(req.path() + " - end");
});
};
vertx.createHttpServer().requestHandler(handler).listen(8080);
}
To test the server, I run the following javascript code in the browser's console.
for (let i = 1; i < 10; i++) {
fetch('http://localhost:8080/test' + i).then(data => console.log('test' + i));
}
These requests result in the following server side output.
/test1 - start
/test2 - start
/test3 - start
/test4 - start
/test5 - start
/test6 - start
>>>>>5 seconds later<<<<<
/test1 - end
/test2 - end
/test7 - start
/test3 - end
/test8 - start
/test4 - end
/test5 - end
/test6 - end
/test9 - start
>>>>>5 seconds later<<<<<
/test7 - end
/test8 - end
/test9 - end
The ">>>>>5 seconds later<<<<<" lines are shown only to emphasize pauses in the output. It seems only 6 requests are concurrently processed at a time, despite the fact that I'm using executeBlocking with "ordered=false". I was expecting to see all 9 requests starting in the output and, five seconds later, all ending.
Why this happens? Can this behaviour be changed? Am I doing something wrong?
Browsers open a limited number of connections to a single destination. In your case, this limit seems to be 6.
See Max parallel http connections in a browser?

Grails findAllByRole() error

I am trying to send email to all users with admin role when list() action has been called.
Inside my list method I put the following code:
def admins = User.findAllByRole("ROLE_ADMIN")
//def approverEmails = User.findByRole("ROLE_APPROVER").collect { it.email }
notifierService.sendApproverRequestEmail(admins)
flash.message = message(code: 'default.created.message', args: [message(code: 'project.label', default: 'Project'), projectInstance.id])
redirect(action: "show", id: projectInstance.id)
But Grails doesn't recognize findAllByRole() method. What am I doing wrong? Is there any other way to send message from service when certain actions in controller are called.
Here is also my service code:
def sendApprovalRequestEmail( def users ) {
users.each { -> user
mailService.sendMail{
to user.email
from "padre#everyonecounts.com"
subject "New project needs approval."
body "Hi ${user.username}! " +
"New project has been requested and needs your approval."
}
}
}
Here is the error:
URI
/PaDRe/project/list
Class
org.codehaus.groovy.grails.exceptions.InvalidPropertyException
Message
No property found for name [role] for class [class com.everyonecounts.padre.User]
Around line 21 of grails-app\controllers\com\everyonecounts\padre\ProjectController.groovy
18: params.max = Math.min(params.max ? params.int('max') : 10, 100)
19: [projectInstanceList: Project.list(params), projectInstanceTotal: Project.count()]
20:
21: def admins = User.findAllByRole("ROLE_ADMIN")
22: //def approverEmails = User.findByRole("ROLE_APPROVER").collect { it.email }
23: notifierService.sendApproverRequestEmail(admins)
24:
Trace
Line | Method
->> 108 | methodMissing in org.grails.datastore.gorm.GormStaticApi
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 21 | list in ProjectController.groovy
| 895 | runTask . . . in java.util.concurrent.ThreadPoolExecutor$Worker
| 918 | run in ''
^ 662 | run . . . . . in java.lang.Thread
Here is my User class
package com.everyonecounts.padre
class User{
transient springSecurityService
String username
String password
String email
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static constraints = {
username blank: false, unique: true
password size: 5..80, blank: false
}
static mapping = {
password column: '`password`'
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
}
The problem you are having is because you don't understand the default spring security implementation. To get a list of users with a given role:
UserRole.findAllByRole(Role.findByAuthority("ROLE_ADMIN"))*.user
Your problem probably is that User.findAllByRole() is expecting a Role as the argument, not a String.
There is a relevant example in 'Querying Associations' subsection of http://grails.org/doc/2.2.x/guide/single.html#finders (shown below)
def author = Author.findByName("Stephen King")
def books = author ? Book.findAllByAuthor(author) : []

Error creating Job (factory-bean returned null) in Grails quartz plugin

I am using Grails 2.2.2 (GGTS - 3.3.0M1) with Quartz plugin 1.0-RC7.
With the following job, I get an error saying that the job instantiation failed when testing with 'grails run-app'. I see the same error if I explicitly call triggerNow() from a controller. The error is as follows:
| Error 2013-05-07 10:50:20,260 [quartzScheduler_QuartzSchedulerThread] ERROR core.ErrorLogger - An error occured instantiating job to be executed. job= 'GRAILS_JOBS.mypkg.auth.OAuthJob'
Message: Job instantiation failed
Line | Method
->> 134 | initialize in org.quartz.core.JobRunShell
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
^ 387 | run in org.quartz.core.QuartzSchedulerThread
Caused by BeanCreationException: Error creating bean with name 'mypkg.auth.OAuthJob': factory-bean 'mypkg.auth.OAuthJobClass' returned null
I created the job with the 'grails create-job' command which added a file under grails-app/jobs/mypkg/auth/OAuthJob.groovy
package mypkg.auth
import org.quartz.JobExecutionContext
import org.quartz.JobExecutionException
//import org.grails.plugins.quartz.JobManagerService
class OAuthJob {
def AuthService
def grailsApplication
static triggers = {
// Execute job once in 11 hours (42600 seconds)
simple name = "OAuthTokenTrigger", repeatInterval: 426000000l
}
def group = "OAuthJobGroup"
def execute(JobExecutionContext context) throws JobExecutionException {
try {
log.debug("OAuthJob has been invoked, now requesting token")
// Call AuthService get token
AuthService.getToken(context.mergedJobDataMap)
} catch (Throwable e) {
throw new JobExecutionException(e.getMessage(), e)
}
}
}
I have added a 'compile ":quartz:1.0-RC7"' dependency to BuildConfig.groovy.
What am I missing? It doesn't matter if I have the 'group' defined, I get the error nevertheless. I do not see any more errors about why bean creation returns null.
I'm thinking this not part of the jobs container...
def grailsApplication <<<- why you referencing this and never using it? :)
another thread/blog or some other craptacular grails documentation methodology had this hint about using one your domain objects to get a handle on the ctx, spring goodness.
def grailsApplication = new MyDomain().domainClass.grailsApplication
def ctx = grailsApplication.mainContext

Can we use the same API that Google Latitude APP is using for discovering location?

I have a device that sends me
Mobile Country Code (MCC)
Mobile Network Code (MNC)
Location area code (LAC)
and
Cell ID
of each closest mobile tower in my region. Google Latitude APP is using this info to detect my location. I would like to make my own app that could use same technique, but I can't find any Google API description.
There is a possibility to use OpenCellID for my purpose, but there are no CellIDs available for country where I live.
There are some posts where are outdated links to API which I need https://developers.google.com/gears/ and http://www.google.com/glm/mmap but seems like none of them work anymore.
You can try using google Maps api:
https://developers.google.com/maps/?hl=es
They there's an option &sensor=true to detect if you are trying to get the latitude and longitude from a device.
Here is a PHP script that takes parameters mcc, mnc, lac and cid and returns latitude:longitude if a result is available, NOT_FOUND if no result is found. This makes use of the same Google API you've mentioned.
<?php
$data =
"\x00\x0e".
"\x00\x00\x00\x00\x00\x00\x00\x00".
"\x00\x00".
"\x00\x00".
"\x00\x00".
"\x1b".
"\x00\x00\x00\x00".
"\x00\x00\x00\x00".
"\x00\x00\x00\x03".
"\x00\x00".
"\x00\x00\x00\x00".
"\x00\x00\x00\x00".
"\x00\x00\x00\x00".
"\x00\x00\x00\x00".
"\xff\xff\xff\xff".
"\x00\x00\x00\x00"
;
$mcc = substr("00000000".dechex($_REQUEST["mcc"]),-8);
$mnc = substr("00000000".dechex($_REQUEST["mnc"]),-8);
$lac = substr("00000000".dechex($_REQUEST["lac"]),-8);
$cid = substr("00000000".dechex($_REQUEST["cid"]),-8);
$init_pos = strlen($data);
$data[$init_pos - 38]= pack("H*",substr($mnc,0,2));
$data[$init_pos - 37]= pack("H*",substr($mnc,2,2));
$data[$init_pos - 36]= pack("H*",substr($mnc,4,2));
$data[$init_pos - 35]= pack("H*",substr($mnc,6,2));
$data[$init_pos - 34]= pack("H*",substr($mcc,0,2));
$data[$init_pos - 33]= pack("H*",substr($mcc,2,2));
$data[$init_pos - 32]= pack("H*",substr($mcc,4,2));
$data[$init_pos - 31]= pack("H*",substr($mcc,6,2));
$data[$init_pos - 24]= pack("H*",substr($cid,0,2));
$data[$init_pos - 23]= pack("H*",substr($cid,2,2));
$data[$init_pos - 22]= pack("H*",substr($cid,4,2));
$data[$init_pos - 21]= pack("H*",substr($cid,6,2));
$data[$init_pos - 20]= pack("H*",substr($lac,0,2));
$data[$init_pos - 19]= pack("H*",substr($lac,2,2));
$data[$init_pos - 18]= pack("H*",substr($lac,4,2));
$data[$init_pos - 17]= pack("H*",substr($lac,6,2));
$data[$init_pos - 16]= pack("H*",substr($mnc,0,2));
$data[$init_pos - 15]= pack("H*",substr($mnc,2,2));
$data[$init_pos - 14]= pack("H*",substr($mnc,4,2));
$data[$init_pos - 13]= pack("H*",substr($mnc,6,2));
$data[$init_pos - 12]= pack("H*",substr($mcc,0,2));
$data[$init_pos - 11]= pack("H*",substr($mcc,2,2));
$data[$init_pos - 10]= pack("H*",substr($mcc,4,2));
$data[$init_pos - 9]= pack("H*",substr($mcc,6,2));
if ((hexdec($cid) > 0xffff) && ($mcc != "00000000") && ($mnc != "00000000")) {
$data[$init_pos - 27] = chr(5);
} else {
$data[$init_pos - 24]= chr(0);
$data[$init_pos - 23]= chr(0);
}
$context = array (
'http' => array (
'method' => 'POST',
'header'=> "Content-type: application/binary\r\n"
. "Content-Length: " . strlen($data) . "\r\n",
'content' => $data
)
);
$xcontext = stream_context_create($context);
$str=file_get_contents("http://www.google.com/glm/mmap",FALSE,$xcontext);
if (strlen($str) > 10) {
$lat_tmp = unpack("l",$str[10].$str[9].$str[8].$str[7]);
$lon_tmp = unpack("l",$str[14].$str[13].$str[12].$str[11]);
$lon = $lon_tmp[1]/1000000;
$lat = $lat_tmp[1]/1000000;
echo $lat.":".$lon;
}
else{echo "NOT_FOUND";}
?>
Save the code as a php file (I've used locate.php in the example below), upload it to your web host and you can make a query as :
http://yourwebhost.com/locate.php?mcc=XXX&mnc=XXX&lac=XXXXX&cid=XXXXXXXX
You can then parse the response obtained to get latitude and longitude information.

help with perl foreach loop

I need help figuring out where to call my send_mail (). Where I currently place it in the code, it sends out an email for every condition and each email it sends it adds another record as part of the set. I'm only interested in sending a single email with the collected records, the last email shown (msg4). Can I do this within the same loop? I'm not sure.
Example:
(msg1)
Service: MST Engine - Stopped - Manual
(msg2):
Service: MST Engine - Stopped - Manual
Service: MST Logging - Stopped - Manual
(msg3):
Service: MST Engine - Stopped - Manual
Service: MST Logging - Stopped - Manual
Service: MST Server - Stopped - Manual
(msg4): (intersted in only this email)
Service: MST Engine - Stopped - Manual
Service: MST Logging - Stopped - Manual
Service: MST Server - Stopped - Manual
Service: MST Formatter - Stopped - Manual
Here is the main piece where I set the conditions:
(I'm using Win32::OLE package has a method in(COLLECTION). So its not an array reference.)
foreach my $serv (in $servSet)
{
next if $serv->{started};
my $sname = $serv->{name};
my $sstate = $serv->{started};
my $ssmode = $serv->{startmode};
$winsvcs .= "Service: $sname - $servicestate[$sstate] - $ssmode\n";
send_email();
}
Move the send_email call out of the loop or it will call it every time it goes through the loop. I assume the function just sends the contents of $winsvcs.
my $winsvcs = '';
foreach my $serv (in $servSet) {
next if $serv->{started};
my $sname = $serv->{name};
my $sstate = $serv->{started};
my $ssmode = $serv->{startmode};
$winsvcs .= "Service: $sname - $servicestate[$sstate] - $ssmode\n";
}
send_email();