Fork me on GitHub
#reagent
<
2019-07-26
>
Ashley Smith11:07:31

Not sure whether to ask this here or in #re-frame but I wanted to ask how someone would go about allowing different URLs in your re-frame app? Something like this: https://reforum-app.herokuapp.com/general I'm looking for a way of having my single page app display different 'pages' depending on the URL, so that people can share a URL to get to a resource without having to operate the app and navigate there manually?

oconn11:07:48

@ashley Sounds like your trying to implement routing? I’d take a look at some of the libs out there. https://github.com/juxt/bidi is the one I use. I ended writing a lib that wraps it and stores route state in re-frame https://github.com/oconn/re-frame-routing.

Ashley Smith11:07:29

I actually saw your library! is that what I'm looking for then

Ashley Smith11:07:33

sorry I was cooking!

oconn12:07:14

No worries. Could you explain a little more what you’re trying to work through? If it’s what it sounds like - you want to swap out parts of the view depending on where the users navigates to without refreshing the page?

oconn12:07:58

And deep linking sounds like what the second part of your question is getting at.

oconn12:07:19

If that’s the case than something like this is what you’re looking for.

Ashley Smith13:07:36

On a basic level, I want exactly what that reforum app is doing, but in clojure and not standard JS 🙂 I was going to go for a regular website at first, but I figured that going for an app allows me to do a little bit more than a website, and so when the basics are done I can take it further. I was in the shower sorry for the delay @oconn

Ashley Smith13:07:06

@oconn could you explain a little bit more about the README snippets?

(ns app.events.core
  (:require [re-frame-routing.core :as rfr]

            [app.router.core :as router]))

(rfr/register-events {:routes router/routes})
Does this expect me to have a file called project/router/clore.cljs? Or is that part of your stuff?

oconn14:07:59

So there are two things you need - 1) a route file (uses bidi routing) and 2) reagent views that are connected to each route. Let me see if I can put a quick example together.

Ashley Smith14:07:33

(def my-routes ["/" {"index.html" :index
                           "article.html" :article}])

Ashley Smith14:07:03

I can understand the reagent views I think, I just didn't know what it was really asking for with {:routes router/routes} as in, what I need to set up

oconn14:07:05

yeah that bidi’s routing so something like (def routes ["/" :home]) would point to the home component

Ashley Smith14:07:15

okay! Learning

Ashley Smith14:07:36

(for now my entire front end is in one file just so I can refactor it in my own time)

oconn14:07:33

and then you would mount a router component that would look something like…

oconn14:07:41

(defmulti containers identity)

(defmethod home 
  :home [] [home-container])

(defn router []
  (let [route (re-frame/subscribe [:router/route])]
    (fn []
      [containers @route])))

oconn14:07:31

This would be your router component that renders your view. So when a user visits / it would render :home

oconn14:07:37

You would also want to subscribe to path params and pass that to all of those to your view as well. So if you have something like /user/:user-uuid/profile your view will have access to user-uuid without having to subscribe to that data directly.

Ashley Smith14:07:04

sorry I'm just processing the information I have right now 🙂

Ashley Smith14:07:17

so do I pass the router into reagent/render? Or am I misunderstanding?

oconn14:07:57

I’ll put a quick gist together with a full example. Then I’ll update the readme 🙂

Ashley Smith14:07:29

sorry for making you go through this!!

oconn14:07:45

no worries

Ashley Smith14:07:11

It might be an idea, to set it up with regards to re-frames simple example (that's what I'm running) https://github.com/Day8/re-frame/blob/master/examples/simple/src/simple/core.cljs

Ashley Smith14:07:50

So that people know how to go from a simple one -page (as I imagine people first app will just be one page, like mine) to a multi page one with routing if that's what they're using your library for

Ashley Smith14:07:38

I don't want to start making seperate files other than my core.cljs until I understand every step on it's own so I can learn to group things for myself you know? 🙂

oconn14:07:09

I did not test this so let me know if you run into any issues

Ashley Smith14:07:01

giving it whirl

Ashley Smith14:07:04

So is router just like any other component?

Ashley Smith14:07:05

okay that makes a lot of sense 🙂

Ashley Smith14:07:02

typo on path-parms line 31 🙂

Ashley Smith14:07:54

Hmm, weirdly, I get the page not found even though I don't have any extra paths in my URL bar?

Ashley Smith14:07:17

https://gist.github.com/Ashe/6c94a677371fe0a87b8d4feddce24901 I include the router you made on line 204, other than that your stuff is at the top

Ashley Smith14:07:29

While I don't understand why it's not finding the home route, what's a little more pressing is that because I only have index.html navigating somewhere else in the URL takes me to the figwheel page

oconn14:07:01

So yeah - router is just a component

oconn14:07:37

Yeah so the router seems to be working if page not found is showing…

oconn14:07:52

so get rid of the leading slashes on the other two routes

oconn15:07:11

(def routes ["/" {"" :home
                 "home" :home
                 "sign-in" :sign-in}])

Ashley Smith15:07:19

okay! I did that

Ashley Smith15:07:02

I think it's something fundamentally wrong though, and not your program. All of this only exists in index.html, right? So changing the URL changes the page and cannot load any JS, right?

oconn15:07:02

Oh and for the other problem you need to set up a push-state server

oconn15:07:35

so / will render index /anything-else needs to hit a server and server up your index.html file

oconn15:07:29

figwheel supports this out the box I believe

Ashley Smith15:07:20

well Im using ring so this shouldn't be too hard

Ashley Smith15:07:31

oh hang on ring is my backend though

oconn15:07:26

yeah so when the user requests any route you server up /index.html

oconn15:07:23

this is a basic dev one I use

(ns clj.server
  (:require [ring.adapter.jetty :refer [run-jetty]]
            [ring.util.response :refer [not-found file-response]]
            [ring.middleware.content-type :refer [content-type-response]]
            [ring.middleware.file :refer [file-request]]
            [ring.middleware.not-modified :refer [not-modified-response]]))

(defonce resource-dir "resources/public")

(defn handler [req]
  (let [is-file (some-> req
                        :uri
                        (clojure.string/split #"/")
                        last
                        (clojure.string/includes? "."))]
    (if is-file
      (if-let [res (file-request req resource-dir)]
        (-> res
            (not-modified-response req)
            (content-type-response req))
        (not-found "Not Found"))
      (assoc-in (file-response resource-dir)
                [:headers "Content-Type"]
                "text/html;charset=utf8"))))

Ashley Smith15:07:35

I think I've realised a massive flaw in what I'm doing then So. I have my CLJ code compiled into a jar file, that is spun up by docker. My CLJS code is compiled into a .js file, which combined with my index.html file creates the front end. Ring is on the back end and has routes, for AJAX calls. My front end doesn't use ring or anything, but it does use Jetty in the sense that figwheel shares it as a dependency.. so I need so somehow edit the server that figwheel gives me? Or maybe this is a flaw, and that my backend and front end should be the same program (in the sense that the server serves a webpage with the compiled CLJS?) Gosh there's a lot to learn when it comes to web development

Ashley Smith15:07:46

my back end can handle any requests I throw at it like you suggest. Typing in different URLs to the server allows me to call functions (so I have localhost:8080/count-up/10 returns a list of 1-10)

Ashley Smith15:07:41

this will do it right?

oconn15:07:48

yeah so for figwheel development of SPA apps I use that server I pasted above with

^{:open-file-command "emacs",
  :ring-handler clj.server/handler,
  :ring-server-options {:port 4001},
  :watch-dirs ["src"]}
{:main app.core
 ;; other clojurescript settings  
}

Ashley Smith15:07:35

Okay! So with those settings, figwheel boots up my server without me needing to run docker, thats nice 🙂

Ashley Smith15:07:06

Im still not quite so sure if this will help me though, as it's my backend that is now responding to the URL address and not my front end

Ashley Smith15:07:22

maybe the solution is to have a front-end server too

Ashley Smith15:07:43

thats got to be actually I think

Ashley Smith15:07:12

im sorry for sending lots of messages haha thinking to myself

danielneal15:07:47

You could put the backend routes all under /api/ for example, and then server the same frontend cljs for any frontend routes, and let the frontend router handle the url

Ashley Smith15:07:23

im currently in IRC just asking about best practice setc

Ashley Smith15:07:19

In my mind, I need two servers, one for front and one for back, as serving web pages might strain the backend? But at the same time, it would also be really easy to just make the backend serve a webpage with the correct javascript on?

danielneal15:07:55

I think you should be able to get a long way with one server - definitely to start off with

Ashley Smith15:07:58

okay im going to split them

Ashley Smith15:07:05

the webdev irc said it'd be good and good to learn

Ashley Smith15:07:10

I don't think it'll be hard either 🙂

Ashley Smith17:07:53

Well, my front end now has its own little server, but router is still saying Page not found despite there now being things in the URL hmm