How to correctly save the value of a text input into the db in reframe? - clojurescript

I'm handling a text input using reframe like so:
[:input {
:type "text"
:value #(subscribe [:text-bar])
:on-change
#(dispatch [:text-bar-input (-> % .-target .-value)])
}]
;; event
(reg-event-db
:text-bar-input
(fn [db [_ input-value]]
(if-not (empty? input-value)
(assoc db :text-bar input-value)
(assoc db :text-bar input-value))))
;; sub
(reg-sub
:text-bar
(fn [db]
(:text-bar db)))
But upon inputting text, I get the following when a key is pressed, for example, key "p":
[Error] Error: No matching clause: p
(anonymous function) (router.js:624)
re_frame$router$_exception (router.js:192)
(anonymous function) (router.js:448)
(anonymous function) (router.js:514)
re_frame$router$_fsm_trigger (router.js:97)
(anonymous function) (router.js:550)
re_frame$router$_process_1st_event_in_queue (router.js:135)
(anonymous function) (router.js:291)
re_frame$router$_run_queue (router.js:173)
(anonymous function) (router.js:472)
(anonymous function) (router.js:514)
re_frame$router$_fsm_trigger (router.js:97)
(anonymous function) (router.js:336)
(anonymous function) (nexttick.js:211)
What am I doing wrong?

Related

Mounting initial ajax data in Re-frame

I see an example of Ajax call in the re-frame docs:
(reg-event-fx ;; <-- note the `-fx` extension
:request-it ;; <-- the event id
(fn ;; <-- the handler function
[{db :db} _] ;; <-- 1st argument is coeffect, from which we extract db
;; we return a map of (side) effects
{:http-xhrio {:method :get
:uri "http://json.my-endpoint.com/blah"
:format (ajax/json-request-format)
:response-format (ajax/json-response-format {:keywords? true})
:on-success [:process-response]
:on-failure [:bad-response]}
:db (assoc db :loading? true)}))
Can I just call the event in the main function:
(reframe/dispatch-sync [:request-it])
to load the initial values? I need to load the initial values and then render the views.
UPDATE
I did it using this function:
(reframe/reg-event-db
:process-response
(fn
[db [_ response]]
(-> db
(assoc :loading? false) ;; take away that "Loading ..." UI
(assoc :test (js->clj response))
(assoc :questions (js->clj (:questions response))))))

Clojurescript namespace as argument

Say I have the following Clojurescript code:
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns] (the-ns/foo))
; now I want to see 1
(other/thing one)
; now I want to see 2
(other/thing two)
How can I achieve this with Clojurescript?
one and two has the same "interface".
PS I know I can pass a function as an argument, but that doesn't answer the question. (e.g. the namespace might have many functions, and I don't want to pass them all)
 Tried ns-resolve
boot.user=> (ns one)
nil
one=> (defn foo [] 1)
#'one/foo
one=> (ns two)
nil
two=> (defn foo [] 2)
#'two/foo
two=> (ns other (:require [cljs.analyzer.api :as api]))
nil
other=> (defn thing [the-ns] (let [f (api/ns-resolve the-ns 'foo)] (f)))
#'other/thing
other=> (other/thing 'one)
java.lang.NullPointerException:
other=> (one/foo)
1
other=> (two/foo)
2
(Yes, there's no trace after java.lang.NullPointerException:, and I go on to show the initial namespaces resolve in the REPL session,)
If I move away from the contrived example, and try this in my Clojurescript project, I get this trace:
#object[Error Error: No protocol method IDeref.-deref defined for type null: ]
Error: No protocol method IDeref.-deref defined for type null:
at Object.cljs$core$missing_protocol [as missing_protocol] (http://0.0.0.0:8000/index.html.out/cljs/core.js:311:9)
at Object.cljs$core$_deref [as _deref] (http://0.0.0.0:8000/index.html.out/cljs/core.js:2164:17)
at cljs$core$deref (http://0.0.0.0:8000/index.html.out/cljs/core.js:4945:18)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity$3 (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:346:51)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:322:37)
at Function.cljs.analyzer.api.ns_resolve.cljs$core$IFn$_invoke$arity$2 (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:332:37)
at cljs$analyzer$api$ns_resolve (http://0.0.0.0:8000/index.html.out/cljs/analyzer/api.js:318:37)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:1:108)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:9:3)
at eval (eval at <anonymous> (http://0.0.0.0:8000/index.html.out/weasel/repl.js:30:495), <anonymous>:14:4)
You can use ns-resolve function to find a var in a namespace.
(ns one)
(defn foo [] 1)
(ns two)
(defn foo [] 2)
(ns other)
(defn thing [the-ns]
(let [f (ns-resolve the-ns 'foo)]
(f)))
(demo.other/thing 'one) ;; returns 1
(demo.other/thing 'two) ;; returns 2
But for this kind of polymorphic behavior, using protocols or multi-methods is more suitable.
UPDATE
The above code works only in Clojure, because ns-resolve does not exists in ClojureScript. In fact, ClojureScript does not have Vars.
But we can manually get the function from namespace object. We also need to mark functions with export metadata flag to prevent function names get "munged":
(ns demo.one)
(defn ^:export foo [] 1)
(ns demo.two)
(defn ^:export foo [] 2)
(ns demo.other)
(defn thing [the-ns]
(let [f (aget the-ns "foo")]
(f)))
(other/thing demo.one)
(other/thing demo.two)

How to 'turn on' cljs.spec

How do I make sure that clojure/cljs.spec is verifying function call arguments and return values?
Say I have this function:
(defn my-inc [x]
(inc x))
After which I have this:
(s/fdef my-inc
:args (s/cat :x number?)
:ret number?)
This code compiles because [cljs.spec.alpha :as s] has been required.
Now I call the function so as to hopefully generate an error:
(my-inc "Not a number")
I would like to see the fdef being used, and see the error message stating that my-inc cannot be called with a string. How do I make this happen in a very general way, for instance with a setting in project.clj or user.cljs?
With this code in user.cljs:
(:require
[cljs.spec.alpha :as s]
[cljs.spec.test.alpha :as ts])
(defn my-inc [x]
(inc x))
(s/fdef my-inc
:args (s/cat :x number?)
:ret number?)
(ts/instrument)
(defn x-1 []
(my-inc "Hi"))
I can call x-1 from the cljs/figwheel REPL, and get this failure message:
#error {:message "Call to #'cljs.user/my-inc did not conform to spec:\nIn: [0] val: \"Hi\" fails at: [:args :x] predicate: number?\n:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha50572]\n:cljs.spec.alpha/value (\"Hi\")\n:cljs.spec.alpha/args (\"Hi\")\n:cljs.spec.alpha/failure :instrument\n", :data #:cljs.spec.alpha{:problems [{:path [:args :x], :pred cljs.core/number?, :val "Hi", :via [], :in [0]}], :spec #object[cljs.spec.alpha.t_cljs$spec$alpha50572], :value ("Hi"), :args ("Hi"), :failure :instrument}}
I can also get conformance errors when working with a real project running code in a browser. The errors show up in the developer's console on the browser.
Put (ts/instrument) at the bottom of user.cljs to turn instrumentation on for all namespaces for development.
Edit
Just in case you are hit by this problem: https://dev.clojure.org/jira/browse/CLJS-1792
- the fix is to include [org.clojure/test.check "0.10.0-alpha2"] (probably with a more recent version) in your project.clj dependencies.
You can use instrument to check args conformance for certain symbols or all vars.
When called without arguments, instrument wraps all instrumentable vars into a function that checks the args before delegating to the original function.

Arity exception in clojure

I have this piece of code.
(defn get-movie [name-movie contents]
(loop [n (count contents) contents contents]
(let [movie (first contents)]
(if (= (:name (first contents)) name-movie)
(movie)
(recur (dec n) (rest contents))))))
I have sequence of maps ({:id, :name, :price} {} {}). I need to find the map with the :name as given by me (Matching movie). When I give
(get-movie "Interstellar" contents)
where contents is
({:id 10000 :name "Interstellar" :price 1}{:id 10001 :name "Ouija" :price 2}).
I am getting the following exception. :
clojure.lang.ArityException: Wrong number of args (0) passed to: PersistentArrayMap
AFn.java:437 clojure.lang.AFn.throwArity
AFn.java:35 clojure.lang.AFn.invoke
C:\Users\Shalima\Documents\Textbooks\Functional Programming\Programs\Assignment5.clj:53 file.test/get-movie
C:\Users\Shalima\Documents\Textbooks\Functional Programming\Programs\Assignment5.clj:77 file.test/eval6219
I have been sitting with this for sometime now and still couldnt figure out what went wrong. What am I doing wrong here ?
You are invoking movie (a map) like a function. Maps can be invoked with a key for lookup, but there is no 0-arity form. Presumably you just want to return the movie rather than invoke it (by surrounding it with parentheses).
(defn get-movie [name-movie contents]
(loop [n (count contents) contents contents]
(let [movie (first contents)]
(if (= (:name (first contents)) name-movie)
movie ;; don't invoke
(recur (dec n) (rest contents))))))
Not important to the question, but a simpler way to write this loop with destructuring would be:
(defn get-movie [name-movie contents]
(loop [[{n :name :as movie} & movies] contents]
(if (= n name-movie)
movie ;; don't invoke
(recur movies))))
And if you wanted to move to higher order sequence functions and away from the lower level loop entirely you could do something like:
(defn get-movie [name-movie contents]
(first (filter #(= name-movie (:name %)) contents)))

How to call om.dom/render-to-str in Emacs nrepl?

I would like to display the html output of the following object:
(defn search-input [_ owner]
(reify
om/IInitState
(init-state [_]
{:text nil})
om/IRenderState
(render-state [this state]
(dom/input
#js {:type "text"
:value (:text state)
:className "form-control"
:onChange (fn [event] (handle-change event owner state))}))))
There is a render-to-str method in om.dom. But if I type
om.dom/render-to-str
in the ClojureScript repl all I get is nil. And calling om.dom/render-to-str gives the correspondign error message.
TypeError: 'undefined' is not an object (evaluating 'om.dom.render_to_str.call')
The strange thing: Code completion in the repl gives me the render-to-str call.
Ok the problem with om.dom/render-to-str returning nil is solved. The problem was that I didn't connect to a real browser repl but a headless repl. Therefore no index.html was loaded and therefore not react.js was loaded.
But now calling
(dom/render-to-str (search-input nil {}))
returns
"Error evaluating:" (dom/render-to-str (search-input nil {})) :as "om.dom.render_to_str.call(null,om_oanda.core.search_input.call(null,null,cljs.core.PersistentArrayMap.EMPTY));\n"
#<Error: Invariant Violation: renderComponentToString(): You must pass a valid ReactComponent.>
Error: Invariant Violation: renderComponentToString(): You must pass a valid ReactComponent.
After some more tests I think I have to change the call like this:
(dom/render-to-str (om.core/build search-input a-cursor {}))
So the last question is: How do I create a cursor.
(defn render-to-str
"Equivalent to React.renderComponentToString"
[c]
(js/React.renderComponentToString c))
Try calling the function with the component as an argument.