Structure for bigger applications using incr_dom

jsoo

#1

Hello,

I am trying out js_of_ocaml and I had some questions.

  1. What is current recommended way to handle client side navigation

  2. I have been trying out incr_dom and I understand the basic application interface, but i’m curious how would a bigger project which might need composing multiple Applications (App_intf in incr_dom)

I don’t know if i’m asking the right questions. I’ll keep experimenting with incr_dom but I was hoping someone who with more experience using OCaml on the client side, would be able to chime in.

Best,
Anurag


#2

Not sure about (1). For (2), I think you would be able to compose App_intf modules by composing Model and State, and delegating to view and friends.

For example, if you had an app My_app and components Widget1 and Widget2, you could have something like:

module My_app = struct
  module Model = struct
    type t =
      { w1_model : Widget1.Model.t
      ; w2_model : Widget2.Model.t
      }
    let view model ~inject =
      let open Incr_dom.Incr.Let_syntax in
      let w1_dom_node = Widget1.view (model >>| (fun t -> t.w1_model)) in
      ...
  end
end

Other functions and State would be defined similarly, but (disclaimer!) I haven’t tested this idea, so it may take some tweaking to get working.


#3

Thanks! That makes sense.


#4

I have been trying out some options for client side routing. This is what I have so far:

I am sure there might be a cleaner way to do this, and I haven’t handled any errors, etc. But the idea was to bind to the dom hashchange event for the lifecycle of the application, and then trigger an event that the app can then handle and dispatch things as needed.

I will add a few more helpers to deal with the HTML5 history API, and maybe it will be a decent start for a client side routing solution.


#5

This is pretty cool. One suggestion: instead of using an Ivar.t (which can only be filled once), you might consider using an Mvar.t, which can be filled multiple times and you can create a Pipe.Reader.t from it.

Also an option (and probably much simpler): your route_change_event could accept a callback which will be notified for each hashchange event. That way, you do not have to keep removing and re-adding the event listener, e.g.,

let route_change_event ~f =
  ...
  Dom_html.handler (fun ev -> f (Navigation.location_of_js (Dom_html.window##.location)))

And then just call route_change_event ~f:(fun loc -> schedule ...).


#6

Ha!! I saw Bvar and Ivar but somehow missed Mvar in the documentation!

Accepting a callback in the route_change_event does look simpler. I’ve got rid of the watch_route_changes method. And as you said, now i don’t have to keep removing and re-adding the event. Thank you!