package vue-jsoo

  1. Overview
  2. Docs
Binding of Vue_js

Install

Dune Dependency

Authors

Maintainers

Sources

archive?sha=2a338bcccd3cdd4c5b3c488f84b954baa976840e
md5=d8095fc66a530f25abb2c0b994e2e720
sha512=0068240dff39fb5e08a61bb36540b6fffa5d015abb7b4f5eb6a00425f8d860d7f73493d63779b6cc269680192cdab4c3dc662c5631ae1e195f6f58e92815f4e2

Description

Published: 06 Jul 2020

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.

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)

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 = struct
  include Vue_js.Root(struct
      class type data = object end
      class type state = object end
      class type all = object end
      let id = "app"
    end)

  let data = Unsafe.obj [||]
  let state = Unsafe.obj [||]

  let foo_name, foo_store, foo_route = Foo.(init ~mutations ~state ~data ())
  let bar_name, bar_store, bar_route = Bar.(init ~mutations ~state ~data ())
  let routes = [
    Unsafe.coerce foo_route;
    Unsafe.coerce bar_route ]
  let modules = [
    foo_name, Unsafe.coerce foo_store;
    bar_name, Unsafe.coerce bar_store ]

end

let () =
  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>

Dependencies (3)

  1. lwt
  2. js_of_ocaml-ppx >= "3.1.0"
  3. dune >= "2.4"

Dev Dependencies

None

Used by

None

Conflicts (1)

  1. js_of_ocaml-tyxml < "3.1.0"
OCaml

Innovation. Community. Security.