package vue-jsoo

  1. Overview
  2. Docs
Binding of Vue_js

Install

Dune Dependency

Authors

Maintainers

Sources

vue-jsoo-0.3.tar.gz
md5=ba1262b631818c3d20eb872b0f6989de
sha512=d326ec29baa9abd5ad0629e3d854b78cb6846714d73b6517fa1f2341f909fe9b4cfd554c113674e133e23e8906d402ab0a3df4210dca1a775ea04c29486cb1cd

Description

Published: 24 Dec 2022

README

vue-jsoo

vue-jsoo is a js_of_ocaml binding of vue-js

There are separate implementations of vue components in Vue_components, vue router in Vue_router and vuex in Vuex. Vue_js also provides functors to help structure a vue project.

The API documentation is available here: API Reference

Simple Vue

In the .ml file:

open Js_of_ocaml

let data = object%js
  val message = string "Hello Vue.js!"
end

let methods = Mjs.L [
  "reverseMessage", Mjs.to_any (wrap_meth_callback (fun this () ->
     let s = Js.to_string this##.message in
     let n = String.length s in
     let s = String.mapi (fun i _c -> String.get s (n-i-1)) s in
     this##.message := Js.string s ]

let () =
  Vue_js.make ~data ~methods "app"

In the .html file:

<div id="app">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage()">Reverse Message</button>
</div>

Vue_component

In the .ml file:

open Mjs
open Vue_component

let template =
  "<div>\
   <h4>Component Name : {{ name }}</h4>\
   <input v-model='payload'/>\
   <button @click='increment()'>increment</button>\
   <span>count: {{ count }}</span>\
   </div>"

let data _this = object%js
  val mutable count = 0
  val mutable payload = 0
end

let methods = L [
  "increment_foo", to_any (wrap_meth_callback (fun this () ->
   this##.count := this##.count + this##.payload)) ]

let load () =
  make ~template ~props:(PrsArray [ "name" ]) ~methods ~data "component"

let () =
  load ();
  Vue_js.make "app"

In the .html file:

<div id="app"
  <component name="test"></component>
</div>

Vue_router

In the .ml file:

open Mjs
open Vue_router

let () =
  let router = make [
    { (empty "/foo") with
       component = Some { Vue_component.empty with template = Some "<div>foo</div>" } };
    { (Vue_router.empty "/bar") with
       component = Some { Vue_component.empty with template = Some "<div>bar</div>" } } ] in
  Vue_js.make ~router "app"

In the .html file:

<div id="app">
  <p>
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <router-view></router-view>
</div>

Vuex

In the .ml files:

open Js_of_ocaml
open Js
open Mjs
open Vuex

let () =
  let data = object%js
    val payload = 0
    val payload_async_ = 0
   end in

  let state : state t = object%js
    val mutable count = 0
    val mutable todos = array [|
      object%js val id = 1 val text = string "..." val done_ = _true end;
      object%js val id = 2 val text = string "..." val done_ = _false end;
    |]
  end in

  let increment state payload =
    let payload = int_of_float @@ float_of_number @@ coerce payload in
    state##.count := state##.count + payload in

  let done_todos state =
    to_any @@ manip_list (List.filter (fun x -> x##.done_ = _true)) state##.todos in

  let get_todo state =
    let f id =
    let todos = Mjs.to_list state##.todos in
    match List.find_opt (fun x -> x##.id = id) todos with
     | None -> undefined
     | Some todo -> def todo in
    to_any f in

  let increment_async store payload =
    ignore @@
    Dom_html.window##setTimeout
      (wrap_callback (fun () ->
         store.commit ~payload "increment" )) 1000. in

  let options = {
    (empty state) with
     mutations = L [ "increment", increment ];
     o_getters = L [ "doneTodos", done_todos; "getTodoById", get_todo ];
     actions = L ["incrementAsync", increment_async ];
   } in

  let store = make options in

  let methods = T (Table.merge [
    Map.mutations ["increment"];
    Map.actions ["incrementAsync"];
  ]) in

  let computed = T (Table.merge [
    Map.state ["count"; "todos"];
    Map.getters ["doneTodos"; "getTodoById"]]) in

  Vue_js.make ~data ~methods ~computed ~store "app"

In the .html file:

<div id="app">
  <div>
    <h4>Test mutation</h4>
    <input v-model="payload"/>
    <button @click="increment(payload)">increment</button>
    <span>state: {{ count }}</span>
  </div>

  <div>
    <h4>Test getters</h4>
    <div>{{ doneTodos }}</div>
    <div>{{ getTodoById(2) }}</div>
  </div>

  <div>
    <h4>Test action</h4>
    <input v-model="payload_async"/>
    <button @click="incrementAsync(payload_async)">increment</button>
    <span>state: {{ count }}</span>
  </div>
</div>

Simple Functor

In the .ml file:

open Js_of_ocaml
open Vue_js

module V = Make(struct
  let id = "app"

  class type data = object
    method message : Js.js_string Js.t Js.prop
  end

  type all = data
end)

let ()  =
  V.add_method0 "reverseMessage" (fun this ->
  let s = Js.to_string this##.message in
  let n = String.length s in
  let s = String.mapi (fun i _c -> String.get s (n-i-1)) s in
  this##.message := Js.string s)

let () =
  let data = object%js
    val message = "Hello world!"
  end in
  V.init data

Functor for SPA

In the .ml file:

open Js_of_ocaml
open Js

module Foo = struct
  include Vue_js.SPA(struct
     class type data = object
       method payload_foo_ : int prop
     end
     class type state = object
       method count_foo_ : int prop
     end
     class type all = object
       inherit data
       inherit state
     end
     let name = "foo"
     let template =
       "<div>
        <h4>Foo Page</h4>
        <input v-model='payload_foo'/>
        <button @click='increment_foo(payload_foo)'>increment</button>
        <span>state: {{ count_foo }}</span>
        </div>"
     let props =[]
  end)

  let data : data t = object%js val mutable payload_foo_ = 0 end
  let state : state t = object%js val mutable count_foo_ = 0 end
  let mutations = [
    "increment_foo", (fun state payload ->
        state##.count_foo_ := state##.count_foo_ +
          (int_of_float @@ float_of_number @@ Unsafe.coerce payload)) ]

end

module Bar = struct
  include Vue_js.SPA(struct
    class type data = object
      method payload_bar_ : int prop
    end
    class type state = object
      method count_bar_ : int prop
    end
    class type all = object
      inherit data
      inherit state
    end
    let name = "bar"
    let template =
      "<div>
       <h4>Foo Page</h4>
       <input v-model='payload_bar'/>
       <button @click='increment_bar(payload_bar)'>increment</button>
       <span>state: {{ count_bar }}</span>
       </div>"
    let props = []
  end)
  let data : data t = object%js val mutable payload_bar_ = 0 end

  let state : state t = object%js val mutable count_bar_ = 0 end
  let mutations = [
    "increment_bar", (fun state payload ->
      state##.count_bar_ :=
         state##.count_bar_ +
         (int_of_float @@ float_of_number @@ Unsafe.coerce payload)) ]

end

module Root = Vue_js.Root(struct
  class type data = object end
  class type state = object end
  class type all = object end
  let id = "app"
end)

let () =
  Root.add_spa @@ Foo.(make ~mutations ~state ~data ());
  Root.add_spa @@ Bar.(make ~mutations ~state ~data ());
  let app = Root.(init ~routes ~modules ~data ~state ()) in
  ignore app

In the .html file:

<div id="app">
  <p>
    <router-link to="/foo">Go to Foo</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <router-view></router-view>
</div>

Vue Compiler

To use vue in runtime-only build you can pre compile your templates and use the render and static_renders arguments of the different makers.

To compile these templates for jsoo we provide a js script (to use with nodejs): vue-jsoo-compiler.js

vue-jsoo-compiler <output_name_no_extension> <file1_html_no_extension> <file2_html_no_extension> ...

This will produce output_name.js file that can be used as jsoo stubs exposing for every file the function 'file1_render' and the array 'file1_static_renders'

PPX

To build simple apps, you can preprocess your ocaml code with vue-jsoo.ppx

type computed = {
  computed_prop : int
} [@@deriving jsoo]

type data {
  message: string
} [@@vue {id="app"; name="myApp"; computed=computed_jsoo]

let change_message app new_message =
  app##.message := new_message
[@@vue {name="change"}]

let computed_prop app =
  Ezjs_min.Optdef.option @@ int_ot_string_opt @@ to_string app##.message
[@@vue {computed}]

let () =
  let app = [%vue { message = "no message yet }] in
  ignore app

Dependencies (2)

  1. ezjs_min >= "0.2.1"
  2. dune >= "2.8"

Dev Dependencies (1)

  1. odoc with-doc

Used by

None

Conflicts

None