keechma 2016-07-04

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?

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.

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

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?

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?

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?

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)