Fork me on GitHub
#clojurescript
<
2021-09-11
>
maverick06:09:58

Can anyone help me how do I edit form with default values using re-frame

David Vujic08:09:19

I think you could populate the value conditionally with a default:

:value (or @value "hello World")

David Vujic08:09:59

Alternatively, pre-populate the re-frame db according to the form ids. Or am I misunderstanding your question?

maverick18:09:08

@U018VMC8T0W okay so if I go with the first one it will be able to track my changes when I am editing the form ?

👍 2
maverick18:09:39

I want use the same form but I will use it to update details

David Vujic11:09:31

I think you can give it a try! 😄

maverick06:09:01

(defn text-input [id label] (let [value (re-frame/subscribe [::subs/form id])] [:div.field [:label.label label] [:div.control [:input.input {:value @value :on-change #(re-frame/dispatch [::events/update-form id (-> % .-target .-value)]) :type "text" :placeholder "Text input"}]]]))

Bryce Tichit15:09:25

Hello, What is your way of passing env variables down to CLJS and JS code? Ideally I'd like to set them up so I can acces them using process.env I searched a lot of the internet without finding a good solution that would allow javascript and clojurescript code to access env variables Thanks a lot for the help 😛

Bryce Tichit15:09:03

Options I checked out already, 1. Inline environement variables with babel in the final JS, this could work but not the best since Closure redefine process.env to global.env looks like 2. Closure-define: Looks good but I did not understand how to access the variables from pure JS code 3. Using dotenv npm module, this would be the best but I didn't manage to make it work in CLJS code

thheller15:09:12

which platform are you targetting? browser or node?

Bryce Tichit15:09:20

I'm targetting browser

Bryce Tichit15:09:36

Maybe I did not understand everything and process.env is specific to node?

thheller15:09:57

yes, process.env belongs to node

thheller15:09:18

someone just wrote a plugin for webpack/babel that allows you to reference them at build time

Bryce Tichit15:09:36

Amazing do you have the github url?

thheller15:09:39

no clue, they are pretty common though. I wouldn't recommend using them for CLJS anyways

Bryce Tichit15:09:01

What's your recommandation for passing env vars to the runtime then?

thheller15:09:19

what is your intent with those? just something that should be inlined at build time?

Bryce Tichit15:09:29

Yes inlined at build time is enough

Bryce Tichit15:09:47

Which is why babel could do the job but I was thinking about something cleaner if that exists

thheller15:09:47

then use closure-defines

Bryce Tichit15:09:04

How can i access variables defined like this from pure JS code?

thheller15:09:14

define pure js code? meaning how is it intergrated into the build?

Bryce Tichit15:09:37

I have CLJS and JS code in my codebase, shadow-cljs will bundle all of them in 1 JS. The JS code must read the environement variable

Bryce Tichit15:09:01

Would be really good if my JS codebase could access variable defined using closure-define

thheller15:09:02

so the CLJS code does (:require ["./foo.js" :as foo]) or so?

thheller15:09:59

ok, so in your CLJS ns create a regular goog-define as described here https://shadow-cljs.github.io/docs/UsersGuide.html#closure-defines

thheller15:09:44

ah no wait your JS code was babel transpiled JSX right?

Bryce Tichit15:09:04

Haha good memory yes it is

Bryce Tichit15:09:10

Are you suggesting to inline the env at this moment?

Bryce Tichit15:09:16

Not a bad idea

thheller15:09:36

yeah normally you could access the CLJS code from JS

thheller15:09:45

but the babel transpiled commonjs can't

Bryce Tichit15:09:01

Oh really did not know that

thheller15:09:03

at least not directly

thheller15:09:34

well with ESM you could do import { THAT_GOOG_DEFINE } from "goog:that.cljs.namespace"

thheller15:09:50

unfortunately that doesn't work with commonjs

Bryce Tichit15:09:58

Ok I see makes sense

Bryce Tichit15:09:03

So my JS cannot access CLJS

Bryce Tichit15:09:24

Means that I have only 2 options 1. Using dotenv from npm 2. inline env variable with babel

Bryce Tichit15:09:46

In the end my env vars are just a map, should not be so hard to pass it to both JS and CLJS

thheller15:09:56

it can access it, just not in a nice way 😛

Bryce Tichit15:09:40

I will check that out, thanks for the help ! 😄

thheller15:09:49

after the goog-define you can do (js/goog.exportSymbol "MY_ENV_VAR" the-define)

thheller15:09:57

and then just access the MY_ENV_VAR in the JS code

thheller15:09:04

not sure that actually works but it might

Bryce Tichit15:09:15

Looks very hacky If I may say

thheller15:09:26

environment variables to control build stuff suck 😛

thheller15:09:47

environment variables to be used at actual runtime are fine

Bryce Tichit15:09:00

i just want to pass the url of my database with env vars and others public keys used to interact with other services

Bryce Tichit15:09:06

It's completely runtime nop?

thheller15:09:19

so this isn't actually build configuration

thheller15:09:24

this is runtime configuration?

Bryce Tichit15:09:35

Haha sorry If I confused you but yes

thheller15:09:37

I keep this stuff in my HTML always

Bryce Tichit15:09:01

But you have a tool that insert it in your HTML?

thheller15:09:19

eg. <script>var MY_CONFIG = {js: "data"};</script>

thheller15:09:40

and then from CLJS just use js/MY_CONFIG or from JS

thheller15:09:52

I have servers that generate my HTML so yeah 😛

Bryce Tichit15:09:08

Ok very nice I'm interested

Bryce Tichit15:09:16

I will go the generated index.html approach 😛

thheller15:09:17

can also be a html meta attribute or so

Bryce Tichit15:09:39

It's true that it's cleaner like this

thheller15:09:00

yeah, lets you change this stuff at actual runtime and doesn't require rebuilding your code 😛

Bryce Tichit15:09:51

I need to get fluent on shadow-cljs build hook ASAP so I can implement all of these routine, could you recommend me the best ressource to build one? Thanks a lot for the help !

thheller15:09:39

don't start with a build hook. write a function. call the function from the REPL or command line

thheller15:09:51

as a separate command

thheller15:09:10

then if you really really want to and don't like that solution write a build hook using that actual function

Bryce Tichit15:09:59

Makes sense thanks

thheller15:09:03

build-hooks are generally overused. if your hook would run after the build completes it most likely shouldn't be a hook 😛

Bryce Tichit15:09:05

This hook looks perfect to build html

thheller15:09:44

yep, good example of something that shouldn't be a hook 😛

Bryce Tichit15:09:38

But in the end it makes sense that I want to customize the compilation by asking for a specific index.html nope?

Bryce Tichit15:09:52

If the build hook is not the solution if there something that makes more sense?

Bryce Tichit15:09:01

Faster than his shadow

thheller15:09:18

shadow-cljs run your.function/generate-index input.html somewhere/output.html

thheller15:09:43

or deps.edn or lein if you use those

thheller15:09:57

you can use a hook, just saying that it is much much simpler without

thheller15:09:11

and you have perfect control over when it runs and what it does

👍 2
Bryce Tichit15:09:22

All of this is amazing, thanks 😍 Last thing if my build command is actually npx shadow-cljs release app && shadow-cljs run ... && shadow-cljs run ... do you have a way of specify the workflow in the config file or it would become a hook? haha

thheller15:09:42

don't do the && 😛

thheller15:09:07

see the clj-run example in the link above

thheller15:09:27

just call (shadow/release! :my-app) (generate-my-html) in your code (instead of the rsync from the example)

Bryce Tichit15:09:29

Ok I did not get it at 100% but will work at it, thanks a lot for all the good work on shadow-cljs this tool is amazing 😄

Bryce Tichit15:09:45

did not get it but looks very good for sure

thheller15:09:46

remember. all you are writing is a clojure function. you can make that do whatever you want 🙂

Bryce Tichit15:09:16

but this shadow/release would be called from the init function?

Bryce Tichit15:09:29

Trivial answer I think but don't get it

thheller15:09:41

no, this is a clojure function

thheller15:09:49

it is not in your build config whatsoever

thheller15:09:19

so you create that function. and then instead of shadow-cljs release app you run shadow-cljs run your.release/function

Bryce Tichit15:09:20

I run one function that would call all the shadow/release

thheller15:09:04

so you create a release build and then generate your html and whatever else you may need

thheller15:09:14

all from the comfort of your clojure function 🙂

Bryce Tichit15:09:39

perfect and I can even read from a .env file and inject it into the html

Bryce Tichit15:09:47

Just added to the todo I will do it tomorrow 🙂

thheller15:09:55

eg. the (shadow/release :app) in the example is identical to running shadow-cljs release app

💪 2
Bryce Tichit15:09:02

ClojureScript and its ecosystem are just too good, it's hard to believe I just discovered it after 8 years of software engineering

thheller15:09:37

although you may want to use (shadow/release! :app), so the code throws an exception if the compilation fails for some reason. easy to miss errors otherwise

Bryce Tichit15:09:12

Roger that will use shadow/release! :app

thheller15:09:06

release also prints the error, just doesn't throw. so your function will continue to run which may or may not work depending on what it does 🙂

Bryce Tichit15:09:13

Obviously if the compiling fails we'd like to throw an error !

Bryce Tichit15:09:53

Thanks will use this thread as a doc to implement it tomorrow 🚀

👍 2
lilactown19:09:10

I'm writing a macro that needs to inspect forms that contain #js tagged literals

lilactown19:09:33

for some reason this fails tho:

shadow.user> cljs.tagged-literals/JSValue
Syntax error compiling at (*cider-repl Code/helix:localhost:8777(clj)*:0:0).
No such var: cljs.tagged-literals/JSValue

lilactown19:09:59

:thinking_face: why would the constructor exist but the type not

shadow.user> (ns-publics 'cljs.tagged-literals)
{read-uuid #'cljs.tagged-literals/read-uuid,
 valid-js-literal-key? #'cljs.tagged-literals/valid-js-literal-key?,
 read-inst #'cljs.tagged-literals/read-inst,
 read-queue #'cljs.tagged-literals/read-queue,
 *cljs-data-readers* #'cljs.tagged-literals/*cljs-data-readers*,
 ->JSValue #'cljs.tagged-literals/->JSValue,
 read-js #'cljs.tagged-literals/read-js}

lilactown19:09:14

I guess I can do this?

(cond
         (= (type x) (cljs.tagged-literals/->JSValue []))
         (doto (.-val x) prn)

p-himik19:09:52

Because JSValue is a Java class:

Clojure 1.10.3
user=> (defrecord X [])
user.X
user=> (ns new-ns)
nil
new-ns=> user.X
user.X
new-ns=> user/X
Syntax error compiling at (REPL:0:0).
No such var: user/X
new-ns=> user/->X
#object[user$eval137$__GT_X__149 0x62e7dffa "user$eval137$__GT_X__149@62e7dffa"]

p-himik19:09:19

(= (type x) (cljs.tagged-literals/->JSValue [])) is wrong - you gotta wrap the second argument with type as well.

p-himik19:09:04

Or you can use cljs.tagged_literals.JSValue. Note _ instead of - and . instead of /.

p-himik19:09:19

Better use it with :import in the ns form, of course.

Timofey Sitnikov22:09:40

Hello All, I am pulling my hair out over something probably super simple. I have a Material UI icon, I just need to make it green. Material UI provides this example:

<Avatar className={classes.green}>
  <AssignmentIcon />
</Avatar>
So in my code I try to do this:
(mui/avatar {:variant   "rounded"
             :className "classes.green"}
    (mui/icon-Done))
(mui/typography {} "Email Is Verified"))
The mui/avatar is defined like this: (def avatar (interop/react-factory core/Avatar)). When the image gets rendered, this is the browser HTML I get:
<div class="MuiAvatar-root MuiAvatar-rounded classes.green MuiAvatar-colorDefault"><svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"></path></svg></div>
Is there a different way to specify the class so that Material UI knows to make it green?

dpsutton22:09:21

can you link to the example? I suspect that isn't "classes.green" but is an import and the .green property is accessed

dpsutton22:09:51

if you expand the code snippet (the first button above the snippet) you see

export default function LetterAvatars() {
  const classes = useStyles();

  return (
    <div className={classes.root}>
      <Avatar>H</Avatar>
      <Avatar className={classes.orange}>N</Avatar>
      <Avatar className={classes.purple}>OP</Avatar>
    </div>
  );
}

Timofey Sitnikov00:09:56

@U11BV7MTK, I guess this is what I am struggling with, how do you pass className={classes.orange} to the react? I tried every possible way, :className "classes.green" does not seem to work.

dpsutton00:09:46

You passed a string. It’s a JavaScript object

Timofey Sitnikov00:09:48

so passing :className "classes.green" should work?

dpsutton00:09:01

no. that is a string

dpsutton00:09:25

it is making a javascript object

dpsutton00:09:46

root: {
    display: 'flex',
    '& > *': {
      margin: theme.spacing(1),
    },
  },
  pink: {
    color: theme.palette.getContrastText(pink[500]),
    backgroundColor: pink[500],
  },
  green: {
    color: '#fff',
    backgroundColor: green[500],
  },
and then classes.green refers to that object

Timofey Sitnikov00:09:20

so I have to define interop for the class, correct?

Timofey Sitnikov00:09:37

OK, that makes sense, when I look at the example browser html, it shows style .jss278 like so

Timofey Sitnikov00:09:35

I guess the question is how do you call it the JS object from CLJS?

p-himik03:09:54

First of all, it's not "calling a JS object", it's just passing it. If you see a.b in JS, it will be (.-b a) in CLJS, maybe with ^js before the definition/usage of a, depending on the context - more info here: https://code.thheller.com/blog/shadow-cljs/2017/11/06/improved-externs-inference.html In this particular case, classes.green will become (.-green classes), and you would need to use ^js classes where you define classes. Or just use (.-green ^js classes). And you just pass it to the component in question:

[:> Avatar {:class-name (.-orange classes)}
 "N"]
But only if life were that easy. The problem is that useStyles that creates classes is a hook. And for hooks to work in Reagent, you have to jump through some hookps. Or switch this particular Material UI styling approach to a different one. If you want to use that approach with hooks, you should read this: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#hooks And alternative styling approaches should be described in the Material UI documentation. An alternative-alternative approach is to use an existing wrapper. https://github.com/arttuka/reagent-material-ui is a pretty good one because it defines each component in a separate namespace, caring about your bundle size. It also has an example in the repo where they show one styling approach.

metal 2
2
David Vujic09:09:27

Material-UI v4 relies heavily on Hooks to add styling in a component. The reagent-material-ui ClojureScript library helps a lot with the interop and how to create hooks. There’s a styles/make-styles function there that makes life as a ClojureScript developer a lot easier. The reagent components need to use the functional style, i.e. [:f> my-component] to be able to use Hooks. Material-UI v5 (beta) has simplified the styling a lot, with the sx arg. That also simplifies styling via ClojureScript a whole lot. Here’s an example, using Material-UI v5, reagent-material-ui in ClojureScript: https://github.com/DavidVujic/clojurescript-amplified/blob/main/src/main/app/components/greetings.cljs#L11

David Vujic09:09:09

To add a background-color, I think this would be a way (using Material-UI v5 and reagent-material-ui):

[avatar {:sx 
           {:width   56
            :height  56
            :bgcolor "green"}}
        "Hello World"]

2