is there a way to change the URL from within a controller?
I have a scenario where I have a component with lists of items, a component that displays a selected item, and a search component. When I type in the search bar, it fetches data form the server and modifies the list of items. But I want to add the search query to the url. Currently I’m sending an event from the search component to trigger the xhr call. But can’t find a way to update the URL.
@roberto you can use the redirect function in the controller https://keechma.com/api/keechma/keechma_controller/#var-redirect
yeah, I figured that out, now I’m stuck with something else
I redirect, and it gets parsed in the params
function
but I can’t do a controller/execute
from there
you have two options:
1. reset search controller on every change of search params and make a request
2. handle the :route-changed
command in the handler function
:route-changed
will be sent to every running controller every time something in the url is changed
so your params function could only check for the existence of the search params and then you can handle it manually in the handler function
route params that were present when the controller was started are readable from (:route-params controller)
ah, yeah, I think I found a way. I trigger a “redirect” within the function that gets called to do the search. I don’t need to worry about parsing the route in the params function to trigger another call.
I was doing something silly, took me a while to figure out how things work. I was triggering a do-search
message, which redirected.
and then I was trying to trigger the real search message in the params function
I noticed one of my controllers gets started over and over tho, there is probably something wrong. Everytime my search params is triggered, the start
function for one of my other controllers gets triggered.
how does the params
function look for that controller?
(params [_ route]
;;Only run this controller when the route is home or "recordings".
(prn "ROUTE IN RECORDINGS: " route)
(let [route-data (:data route)]
(if (or (and (= (:section route-data) "recordings")
(:page route-data))
(= (:section route-data) "home"))
{:section (:section route-data)
:page (:page route-data)
:query (:query route-data)}
nil)))
you’re returning query
inside the object so that’s probably why
it’s different every time the route changes
ok, so everytime the return value of params
changes, the start
function is called?
yes
I thought it was only called on initialization of the controller
yeah, it shuts down the old controller
and initializes the new one
it’s basically restarting the controller
stop
will be called on the old controller instance first
ohhhh, ok. So , to sum it up, if params
changes, we get a new controller.
yep
hmmm, ok, I see. Thanks, this is very helpful.
yeah, if you want to manually handle the query params (without controller restart) read the original value from (:route-params controller)
and then listen to the :route-changed
command inside the handler function
I think I can use (:route-params this)
to get access to the query instead of returning it from params
although I would probably split it to a separate search
controller
cool
yeah, I have a separate search controller
but the list controller has to re-fetch the list from the server based on the changes in the query
so I still want to inspect the routes, but I don’t want to be restarting the controller, right?
yes
cool
there are more patterns you can use… you could have the search controller sending the message to the list controller whenever it restarts
so you would do something like (send-command this [:list-controller-topic :query] query-data)
from the start function of the search controller
ok, but that would mean the search
controller gets restarted with every change in the query, right?
yes
restarting the controller doesn’t re-render the component, right (unless the data changes)?
nope, and you can send the search command up to the search controller, debounce it there and redirect after let’s say 200ms
coo, that sounds much more cleaner. I was trying to debounce in the component itself.
which got nasty
yeah, that’s why controllers have the handler function, so you can use channels for that kind of logic
to send a command to another controller, how do I specify the name of the controller?
Do I need a handle on the instance of the controller?
this is the only example I could find https://github.com/keechma/keechma/blob/fd7550a5300ccc024297373d861e0bd30d1cc7a5/test/keechma/test/controller.cljs#L25
nevermind, I see I can get access to it from the app-db
hmmm, any idea why this isn’t working?
(let [recordings-ctrl (get-in @app-db-atom [:internal :running-controllers :recordings])]
(controller/is-running? recordings-ctrl)) ;; This is returning true
(controller/send-command recordings-ctrl :search-recording [args]))
I’m getting a Uncaught Error: nth not supported on this type cljs.core/Keyword
error
so when you use send command
it always gets routed through the controller manager
so you need to specify both topic and command
so instead of :search-recording
it would be [:controller-topic :search-recording]
where are you sending the command from?
if you’re sending it from another controller
from another controller
you don’t need to get the controller instance
just send it on the correct topic
topic being whatever was the key in the controllers map when starting the app
so you wound send it like this:
(controller/send-command this [:controller-topic :command-name] args)
great
that works
thanks