Using React Native MapView in ClojureScript Reagent - react-native

I am trying to use MapView from https://github.com/airbnb/react-native-maps using Reagent. It works fine but how could I get local state for MapView when onRegionChange event is fired? Trying to use current/component but it is always nil.
(def Expo (js/require "expo"))
(def map-view (r/adapt-react-class (.-MapView Expo)))
(defn my-map []
(r/create-class
{:component-did-mount (fn [this]
(println "component mount "))
:reagent-render (fn []
(let [this (r/current-component)]
[map-view {:style {:flex 1}
:initialRegion {:latitude 37.78825
:longitude -122.4324
:latitudeDelta 0.0922
:longitudeDelta 0.0421}
:onRegionChange (fn []
;; Would like to see state here.
(println (.. this -state)) )}]))}))

Region information
The onRegionChange callback has Region as argument. Region has the following signature:
type Region {
latitude: Number,
longitude: Number,
latitudeDelta: Number,
longitudeDelta: Number,
}
You can get the values from the Region by using goog.object/get.
If you get the region and extract the values from it your my-map looks like:
(defn my-map []
[map-view {:style {:flex 1}
:initialRegion {:latitude 52.3702
:longitude 4.89516
:latitudeDelta 0.0922
:longitudeDelta 0.0421}
:onRegionChange (fn [region]
(alert (str "Latitude: "
(goog.object/get region "latitude")
"\nLongitude: "
(goog.object/get region "longitude"))))}])
You can obtain the latitudeDelta and longitudeDelta in the same manner.
When you drag the map the latitude and longitude show up:
The component
If you want access to the component itself your code works fine, you just have to visualize it:
(defn jsx->clj
[x]
(into {} (for [k (.keys js/Object x)] [k (aget x k)])))
(defn my-map []
(let [this (r/current-component)]
[map-view {:style {:flex 1}
:initialRegion {:latitude 37.78825
:longitude -122.4324
:latitudeDelta 0.0922
:longitudeDelta 0.0421}
:onRegionChange (fn [region]
;; Would like to see state here.
(alert "The component..."
(str (jsx->clj this))))}]))
This prints something like:
Not sure if you can do anything with the component, I think you need the Region information.

Related

Correct way to implement drill-down tags in vue with vuetify

I am using a v-chip-group with v-chips to represent a tag cloud for records in my database. I have an object array with records that look something like { key:'device', count:100}. A record could have multiple tags, so as you click on a tag, a new query is made that filters on that tag, the result will then have a new tag cloud with a subset of the previous.
It looks something like this:
tag1 (1000), tag2 (100), tag3 (100)
When you click on tag1 you end up with:
tag1 (1000), tag3 (15) (no tag2 because there is no overlap between tag1 and tag2).
Here is the relevant template code:
<v-chip-group v-model="selectedTag" multiple #change="refresh">
<v-chip v-for="tag in tags" :key="tag.key" active-class="primary">
<v-avatar left class="grey">{{ tag.count }}</v-avatar>
{{ tag.key }}
</v-chip>
</v-chip-group>
The problem I have is that in the typescript I do something like this:
refresh() {
// get simple array of tag strings
const selectedTags = this.selectedTag.map((value: any) => {
if (this.tags && this.tags[value]) {
return this.tags[value].key
} else {
return null
}
}).filter((value: any) => {
return value != null
})
Promise.all([
...
ApiCall('GET', 'tags', {limit: 1000, tags: selectedTags}),
...
).then((values) => {
// decode response from server into new tags
this.tags = values[2].series['0'].values.map((item: any) => {
return {key: item.bucket, count: item.doc_count}
})
const newTags: number[] = []
this.tags.forEach((tag, index) => {
// find the new index of the previously selected tags and save them
if (selectedTags.find(st => {
return st === tag.key
})) {
newTags.push(index)
}
})
// update selectedTag with the new value
this.$set(this, 'selectedTag', newTags)
// did not work this.selectedTag = newTags
})
}
What I'm seeing is that when I click a chip, it correctly fires the #change event and calls refresh, but then when the refresh finishes, I see an additional refresh get called with an empty selectedTag, which clears my filters and recalls the above functionality.
Is there a way to get #change to fire when a chip is changed, but not fire (or filter it out) when the event is generated by changing the data referenced by v-model?

How to get result using better algorithm?

We have the list like below
List:
(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))
As the number in the string may be referred by different elements, the expected result is:
("herry","0,1,2,3,6"), ("herry","4"), ("John","5")
I worked out solution using scala, but it looks complicated, is there a more clean and easy way to work out the result? Thanks in advance!
Here is my solution in scala,
val foo=List(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))
println(GetValue)
def GetValue()={
foo.zipWithIndex.map((tuple: ((String, String), Int)) =>{
val tuples = getrelated(tuple._1, foo)
(tuple._2, tuples)
}).map((tuple: (Int, List[(String, String)])) => tuple._2)
.map((tuples: List[(String, String)]) => (tuples.head._1,tuples.map((tuple: (String, String)) => tuple._2)))
.map((tuple: (String, List[String])) => (tuple._1, tuple._2.mkString(",").split(",").distinct.sorted.mkString(",")))
.distinct
}
def getrelated(start:(String,String),fooList:List[(String,String)]):List[(String,String)]={
val fooListWithout = fooList.filter((tuple: (String, String)) => tuple != start)
val result=fooListWithout
.filter((tuple: (String, String)) => findmatching(tuple._2,start._2))
.flatMap((tuple: (String, String)) => start :: getrelated(tuple,fooListWithout))
if (result.isEmpty)
List(start)
else
result
}
def findmatching(key1:String,key2:String)={
(key1.split(",")++key2.split(","))
.groupBy(identity)
.mapValues((strings: Array[String]) => strings.size)
.exists((tuple: (String, Int)) => tuple._2>1)
}
Let me clarify the algorithm
if the number list has the overlapping number, then group these number as one element
if the number list has no overlapping number, then consider it as independent element
for example,
input: List(("herry","0,1,2"),("herry","1,3"),("herry","7,4"),("herry","4"),("John","5"))
expected output: List(("herry","0,1,2,3" ), ("herry","4,7"), ("John","5"))
input: List(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))
expected: List("herry","0,1,2,3,6"), ("herry","4"), ("John","5")
input: List(("herry","0,1"),("herry","2,3"),("herry","3,6"),("herry","4"),("John","5"))
expected: List("herry","0,1"),("herry","2,3,6"), ("herry","4"), ("John","5")
My assumption is that tuples for the same key that contain multiple values should be aggregated. It is unclear what should happen in the same value appears singly and as part of a list, e.g. ("herry", "4"), ("herry, "1,4") however it should be simple enough to remove any such cases
val data = List(("herry","0,1,2"),("herry","1,3"),("herry","3,6"),("herry","4"),("John","5"))// Start writing your ScalaFiddle code here
val (singles, multiples) = data.partition{case (name, list) => !list.contains(",")}
val multiplesAggregated = multiples
.groupBy{case (key, _) => key)
.map{
case (key, values) =>
key -> values.flatMap{case (_, numbers) => numbers.split(",")}.distinct.sorted.mkString(",")}.toList
println(multiplesAggregated ++ singles)
Output:
List((herry,0,1,2,3,6), (herry,4), (John,5))

Sequence Http.get in Elm

Below I have a button that attempts to load remote content ...
import Post exposing (Post)
import Html exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as Decode
type alias Model =
{ posts : List Post }
type Msg
= Search String
| PostsReceived (Result Http.Error (List Post))
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search s ->
let
cmd =
(Decode.list Post.decode)
|> Http.get ("/posts?author=" ++ s)
|> Http.send PostsReceived
in
( model, cmd )
PostsReceived (Ok posts) ->
{ model | posts = posts }
! []
PostsReceived (Err error) ->
( model, Cmd.none )
view : Model -> Html Msg
view model =
button
[ onClick (Search "amelia") ]
[ text "Read posts by Amelia" ]
This is a valid Elm program, only there's one little problem: The API doesn't allow me to search by string. This is not allowed
/posts?author=amelia => Malformed Request Error
However, this is allowed
/posts?author=2 => [ {...}, {...}, ... ]
So I must first fetch an author to get his/her id, and then I can fetch posts using the author's id...
/author?name=amelia => { id: 2, name: "amelia", ... }
/posts?author=2
How can I sequence one request after the next? Ideally I'd like to cache the authors somewhere in the model so we're only requesting ones that we haven't seen before.
You can use Task.andThen to chain two tasks together. Assuming that the /posts response includes the author ID, you can then add that author ID into you model when you handle the response.
Search s ->
let
getAuthor =
Author.decode
|> Http.get ("/author?name=" ++ s)
|> Http.toTask
getPosts author =
(Decode.list Post.decode)
|> Http.get ("/posts?author=" ++ author.id)
|> Http.toTask
cmd =
getAuthor
|> Task.andThen getPosts
|> Task.attempt PostsReceived
in
( model, cmd )
I've got this compiling at https://ellie-app.com/DBJc6Kn3G6a1 if that helps
You can chain together tasks using Task.andThen. You'll first have to convert the web requests to tasks using Http.toTask:
postsByAuthorName : String -> Cmd Msg
postsByAuthorName name =
Http.get ("/author?name=" ++ name) (Decode.field "id" Decode.int)
|> Http.toTask
|> Task.andThen (\id ->
Http.get ("/posts?author=" ++ toString id) (Decode.list decodePost)
|> Http.toTask)
|> Task.attempt PostsReceived
A a dictionary and a couple more Msg options should do it.
You'll have to write the decoder for the Author response, but other than that this should work
type alias Model =
{ posts : List Post
, authors : Dict String Int }
type Msg
= Search String
| SearchAuthor String
| AuthorReceived (Result Http.Error Int String)
| PostsReceived (Result Http.Error (List Post))
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Search author ->
case (Dict.get author model.authors) of
Nothing ->
let
cmd =
(Decode.list Post.decode)
|> Http.get ("/author?name=" ++ author)
|> Http.send AuthorReceived
in
(model,cmd)
Just num ->
let
cmd =
(Decode.list Author.decode)
|> Http.get ("/posts?author=" ++ num)
|> Http.send PostsReceived
in
( model, cmd )
AuthorReceived (Ok number name) ->
let
updatedAuthors = Dict.inster name number model.authors
cmd =
(Decode.list Post.decode)
|> Http.get ("/posts?author=" ++ number)
|> Http.send PostsReceived
in
{model | authors = updatedAuthors } ! [cmd]
AuthorReceived (Err error) ->
(mode, Cmd.none )
PostsReceived (Ok posts) ->
{ model | posts = posts }
! []
PostsReceived (Err error) ->
( model, Cmd.none )
view : Model -> Html Msg
view model =
button
[ onClick (Search "amelia") ]
[ text "Read posts by Amelia" ]

Elm 0.17: Task.perform and Maybe

I'm hoping someone could help me with Task.perform as I don't really understand how to handle a Maybe response - and the docs aren't making things clearer for me.
In my model I have results which Maybe a list of items or Nothing.
-- model
type alias Item =
{ name : String}
type alias Model =
{ results : Maybe (List Item) }
model = {
results = Nothing
}
I perform a Task and decode it like so:
-- Task
fetch : String -> Cmd Msg
fetch query =
let url =
"https://some_url" ++ query
in
Task.perform FetchFail FetchSuccess (Http.get decode url)
-- decoder
decoder: Json.Decoder (List Item)
decoder =
Json.at ["data"] (Json.list nestedListDecoder)
-- nestedListDecoder
nestedListDecoder : Json.Decoder Item
nestedListDecoder =
Json.object1 Item
("name" := Json.string)
I then handle the response in update:
-- update
type Msg
= FetchSuccess (Maybe (List Item))
| FetchFail Http.Error
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FetchSuccess results ->
case results of
Nothing ->
( { model | results = Nothing}, Cmd.none)
Just res ->
( { model | results = res }, Cmd.none)
FetchFail err ->
-- ... handle error
And cater for the Maybe in the view:
-- view
result : Item -> Html Msg
result item =
li [] [ text item.name ]
view : Model -> Html Msg
view model =
ul [ ] (List.map result (Maybe.withDefault [] model.results))
I am getting this error when dealing with Maybe of results.
198| Task.perform FetchFail FetchSuccess (Http.get repos url)
^^^^^^^^^^^^^^^^^^
Function `perform` is expecting the 3rd argument to be:
Task Http.Error (Maybe (List Repo))
But it is:
Task Http.Error (List Repo)
Can anyone advise where else I need to cater for the Maybe ?
A simple tweak to your decoder should fix it. The decoder just needs to use Json.Decode.maybe:
decoder: Json.Decoder (Maybe (List Item))
decoder =
Json.maybe <| Json.at ["data"] (Json.list nestedListDecoder)

Clojure web app sql insert only last row when deployed to Tomcat

Good afternoon,
I am using Clojure 1.8, PostgreSQL 9.4 and YeSQL 0.5.2
I have an web app that has a page to insert multiple prices. I get the prices from a HTML Form using Post method. It comes as a map of vector for me. I convert it to a vector of maps so it is easier to manipulate the data. Then I delete previous entries if exists and I do the treatments to the fields and call the DB transaction to insert into the database.
When I use lein ring server it works fine. But when I deploy to Tomcat 9 using lein ring uberwar, the app inserts only the last row.
Here are the my web page:
Each row in the table will be a SQL Insert
My connection:
(ns satus-cotacao.model.connection)
(def db-spec {:classname "org.postgresql.Driver"
:subprotocol "postgresql"
:subname "//localhost/database" ;; 192.168.0.x:port when using other database from the internal network.
:user "root"
:password "root"})
And my defqueries
(ns satus-cotacao.model.cotacao_model
(:require ...
[satus-cotacao.model.connection :refer [db-spec]]
...)
(defqueries "satus_cotacao/model/cotacao.sql"
{:connection db-spec})
The Post request that calls the lcto-cotacao-page using the Form as parameter
POST "/cotacao/:cod" [cod & form :as session] (lcto-cotacao-page cod form session))
The lcto-cotacao-page function to call the Delete and the Insert functions.
(defn lcto-cotacao-page
[cod lcto session]
(try
(cotacao/deleta-lcto-cotacao! cod (:identity session))
(cotacao/insere-lctos-cotacoes! cod (:identity session) lcto)
(layout/render "home.html" {:session session})
(catch Exception e
(do
(timbre/error e)))))
The delete-lcto-cotacao! function and SQL query
(defn deleta-lcto-cotacao!
[codcotacao enti_codigo]
(jdbc/with-db-transaction [tx db-spec]
(delete-lcto-cotacao! {:codcotacao codcotacao
:enti_codigo enti_codigo}
{:connection tx})))
-- name: delete-lcto-cotacao!
DELETE FROM lctocotacao
WHERE lcco_codentidade = :enti_codigo
AND lcco_codcontrole = :codcotacao
The delete works fine, it deletes all entries.
The insere-lctos-cotacoes! to convert from map of vector to vector of maps and create a recursive call through this vector
(defn insere-lctos-cotacoes!
[cod enti lctos]
(if (vector? (:lcco_codprod lctos))
(let [lcto-vec (util/from-map-of-vector-to-vector-of-maps lctos)]
(loop [lcto lcto-vec]
(when (not-empty lcto)
(insere-lcto-cotacao<! cod enti (first lcto))
(recur (rest lcto)))))
(insere-lcto-cotacao<! cod enti lctos)))
Then the function insere lcto-cotacao<! that treats the fields
(defn insere-lcto-cotacao<!
[cod enti lcto]
(let [lcto_codentidade enti
lcto_codcontrole cod
lcto_dtmvto (util/convert-to-data-type (.format (java.text.SimpleDateFormat. "MM/dd/yyyy") (Date.)))
lcto_codprod (Integer. (:lcco_codprod lcto))
lcto_qemb (bigdec (:lcco_qemb lcto))
lcto_qtde (bigdec (:lcco_qtde lcto))
lcto_fpgto (util/empty-field? (:lcco_fpgto lcto) false)
lcto_preco (util/empty-field? (:lcco_preco lcto) true)
lcto_codempresa (util/empty-field? (:lcco_codempresa lcto) false)
new-lcto (assoc lcto :lcco_codentidade lcto_codentidade
:lcco_codcontrole lcto_codcontrole
:lcco_dtmvto lcto_dtmvto
:lcco_codprod lcto_codprod
:lcco_preco lcto_preco
:lcco_qemb lcto_qemb
:lcco_qtde lcto_qtde
:lcco_fpgto lcto_fpgto
:lcco_codempresa lcto_codempresa)]
(insere-lcto-transaction<! (dissoc new-lcto :tpemb :descricao :codbarras :marca))))
And finally the insere-lcto-transaction<! and the SQL query
(defn insere-lcto-transaction<!
[lcto]
(jdbc/with-db-transaction [tx db-spec]
(insert-lcto-cotacao<! lcto {:connection tx})))
-- name: insert-lcto-cotacao<!
INSERT INTO lctocotacao (lcco_codentidade, lcco_codprod, lcco_preco, lcco_dtmvto, lcco_codempresa, lcco_fpgto, lcco_qemb, lcco_qtde, lcco_codcontrole)
VALUES(:lcco_codentidade, :lcco_codprod, :lcco_preco, :lcco_dtmvto, :lcco_codempresa, :lcco_fpgto, :lcco_qemb, :lcco_qtde, :lcco_codcontrole);
Well, thats it. What is strange is that it works fine when I do lein ring server, but when I deploy to lein ring uberwar it doens't work properly.
Also, I tried using Tomcat in Windows and Debian.
Does anyone have an idea why is it happening?
EDIT
After logging, I got some results:
When running local lein ring server
What the POST return into the var form is the same content as the key :param in my session
Also in session, I have the :form-paramsthat has the same content, but instead of using keys like :lcco_fpgto, it uses strings like '"lcco_fpgto"'
{:lcco_fpgto ["001" "001" "001" "001"],
:tpemb ["CX" "CX" "PT" "CX"],
:lcco_qtde ["150.00000" "1.00000" "30.00000" "1.00000"],
:descricao ["Cerveja 350ml Skola" "Adoc 100ml Assugrin Diet" "Cafe 500g Corol Torrado Moido" "Deterg Liq 500ml Alpes Neutro"],
:lcco_codprod ["19" "28" "64" "123"],
:lcco_preco ["1.00000" "2.00000" "3.00000" "4.00000"],
:codbarras ["7896232111149" "7896060010492" "7896184000010" "7896274800124"],
:lcco_codempresa ["001" "001" "001" "001"],
:lcco_qemb ["12.00000" "12.00000" "10.00000" "24.00000"],
:marca ["Skol" "Assugrin" "Corol" "Alpes"],
:__anti-forgery-token ["Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*" "Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*" "Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*" "Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*"]}
When running the deployed app:
The POST return is the same as the :param key inside session (Only the last element)
{:lcco_fpgto "001",
:tpemb "CX",
:lcco_qtde "1.00000",
:descricao "Deterg Liq 500ml Alpes Neutro",
:lcco_codprod "123",
:lcco_preco "4",
:codbarras "7896274800124",
:lcco_codempresa "001",
:lcco_qemb "24.00000",
:marca "Alpes",
:__anti-forgery-token "Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*"}
And what I found, is that the :form-params inside my session contains everything, but using strings instead of keys.
:form-params {
"lcco_preco" ["1" "2" "3" "4"],
"tpemb" ["CX" "CX" "PT" "CX"],
"descricao" ["Cerveja 350ml Skola" "Adoc 100ml Assugrin Diet" "Cafe 500g Corol Torrado Moido" "Deterg Liq 500ml Alpes Neutro"],
"lcco_codprod" ["19" "28" "64" "123"],
"lcco_fpgto" ["001" "001" "001" "001"],
"codbarras" ["7896232111149" "7896060010492" "7896184000010" "7896274800124"],
"marca" ["Skol" "Assugrin" "Corol" "Alpes"],
"lcco_qtde" ["150.00000" "1.00000" "30.00000" "1.00000"],
"__anti-forgery-token" ["Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*" "Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*" "Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*" "Unbound: #'ring.middleware.anti-forgery/*anti-forgery-token*"],
"lcco_qemb" ["12.00000" "12.00000" "10.00000" "24.00000"],
"lcco_codempresa" ["001" "001" "001" "001"]}
So, why is that happening? Is it wrong to use the var form that I get in here?
POST "/cotacao/:cod" [cod & form :as session] (lcto-cotacao-page cod form session))
Why is this difference happening?
EDIT 2
My middleware:
https://codeshare.io/mR7Zh