{"id":13726072,"url":"https://github.com/LexiFi/ocaml-vdom","last_synced_at":"2025-05-07T21:31:09.064Z","repository":{"id":41983611,"uuid":"74473513","full_name":"LexiFi/ocaml-vdom","owner":"LexiFi","description":"Elm architecture and (V)DOM for OCaml","archived":false,"fork":false,"pushed_at":"2024-07-16T15:39:45.000Z","size":311,"stargazers_count":196,"open_issues_count":7,"forks_count":13,"subscribers_count":21,"default_branch":"master","last_synced_at":"2024-08-04T01:28:28.723Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LexiFi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-22T13:09:50.000Z","updated_at":"2024-07-16T15:39:48.000Z","dependencies_parsed_at":"2022-09-02T01:22:23.388Z","dependency_job_id":"35ce3b9a-e8e2-4fdf-a0ea-e009adb9cd58","html_url":"https://github.com/LexiFi/ocaml-vdom","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexiFi%2Focaml-vdom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexiFi%2Focaml-vdom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexiFi%2Focaml-vdom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LexiFi%2Focaml-vdom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LexiFi","download_url":"https://codeload.github.com/LexiFi/ocaml-vdom/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224654106,"owners_count":17347670,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T01:02:51.557Z","updated_at":"2025-05-07T21:31:09.054Z","avatar_url":"https://github.com/LexiFi.png","language":"OCaml","funding_links":[],"categories":["OCaml","Web Development"],"sub_categories":[],"readme":"vdom: Elm architecture and (V)DOM for OCaml\n=================================================\n\n[![Build Status](https://travis-ci.com/LexiFi/ocaml-vdom.svg?branch=master)](https://travis-ci.com/LexiFi/ocaml-vdom)\n\nOverview\n--------\n\nThis package contains:\n\n  - OCaml bindings to DOM and other client-side Javascript APIs\n    (using [gen_js_api](https://github.com/LexiFi/gen_js_api)).\n\n  - An implementation of the [Elm architecture](https://guide.elm-lang.org/architecture/), where the\n    UI is specified as a functional \"view\" on the current state.\n\n\n\nDependencies\n------------\n\n  - OCaml\n\n  - js_of_ocaml\n\n  - gen_js_api\n\n  - odoc\n\n\nInstallation (with OPAM)\n------------------------\n\n```\nopam install vdom\n```\n\nInstall dependencies\n------------------------\n```\nopam install vdom --deps-only\n```\n\n\nManual installation\n-------------------\n\n```bash\ngit clone https://github.com/LexiFi/ocaml-vdom.git\ncd ocaml-vdom\nmake all\n```\n\n\nDOM bindings\n------------\n\n[`Js_browser`](lib/js_browser.mli) exposes (partial) OCaml bindings of the browser's DOM and\nother common client-side Javascript APIs.\n\nIt is implemented with\n[gen_js_api](https://github.com/LexiFi/gen_js_api), making it\nrealistic to have it working with ReScript in the future.  This\nwould open the door to writing client-side web applications in OCaml\nthat could be compiled to Javascript either with js_of_ocaml or\nReScript.\n\n\nVDOM\n----\n\nThe [Elm architecture](https://guide.elm-lang.org/architecture/) is a\nfunctional way to describe UI applications.  In this architecture, the\ncurrent state of the UI is represented with a single data type and a\n\"view\" function projects this state to a concrete rendering.  In our\ncase, this rendering is done to a tree-like abstraction of the browser\nDOM, called a VDOM (Virtual DOM).  This VDOM can itself be rendered to\na concrete DOM.  Whenever the state changes, the view function produces\na new VDOM tree, which is then diffed with the previous one to update\nthe concrete DOM accordingly.  The VDOM also specifies how DOM events\nare wrapped into \"messages\" that are processed by an \"update\" function\nto modify the current state.  This function can also spawn \"commands\"\n(such as AJAX calls) whose outcome is also notified by messages.\n\n\nThe implementation of this architecture relies on two modules:\n\n  - [`Vdom`](lib/vdom.mli): definition of the VDOM tree and of \"virtual\n    applications\".  This is a \"pure\" module, which does not depend on\n    any Javascript bindings (it could be executed on the server-side,\n    e.g. for automated testing).\n\n  - [`Vdom_blit`](lib/vdom_blit.mli): rendering of virtual applications into the actual\n    DOM.  This modules implements the initial \"blit\" operation\n    (rendering a VDOM tree to the DOM) and the \"diff/synchronization\"\n    algorithm.  It also manages the state of a running application.\n    `Vdom_blit` is implemented on top of `Js_browser`.\n\n\n\nThis implementation of VDOM has some specificities:\n\n  - Each node in the VDOM tree has a \"key\" string field.  By default,\n    the key corresponds to the tag name for elements but it can be\n    overridden.  The key is used by the synchronization algorithm\n    as follows: when synchronizing the old and new children of an\n    element, the children are first grouped by key.  Two children with\n    different keys are never synchronized, and the sequence of old and\n    new children with a given key are synchronized in a pairwise way\n    (first old child with key K against first new child with key K;\n    etc...), adding or removing extra/missing children if needed.\n    Children are also reordered in the DOM, if needed, to match the\n    new ordering.\n\n  - Event handlers are not attached on DOM nodes created when a VDOM\n    tree is rendered.  Instead, we attach fixed event handlers on the\n    root container, and rely on event delegation.  The handler\n    corresponding to a given element and responsible for a given kind\n    of event is searched directly in the VDOM.  The rationale for this\n    design choice is that comparing functional values is not\n    well-defined in OCaml, so it would not be clear, when the \"old\"\n    and \"new\" VDOMs are diffed, if the event handler on the DOM node\n    should be refreshed.\n\n  - A \"bridge\" structure is created in `Vdom_blit` to represent the\n    correspondence between VDOM and DOM nodes.  This structure mimics\n    the shape of both trees and avoids having to query the concrete\n    DOM to navigate in the tree.\n\n  - No data structure is created to represent the \"diff\" between old\n    and new VDOMs.  Instead, the synchronization algorithm detects\n    VDOM changes and apply them on the fly to the corresponding DOM\n    node.\n\n  - There is some special support for the \"value\" property.  When this\n    property is explicitly bound in the VDOM (typically on an input\n    field), the value is forced on the element: whenever the DOM value\n    changes, the event is potentially dispatched to an event handler,\n    and the new VDOM property is forced on the DOM element.  In\n    particular, if the internal state is not updated by the event\n    handler, the field becomes in practice read-only.\n\n  - Some special VDOM node attributes are provided to present\n    \"superficial state changes\" that are not reflected in the proper\n    functional state (currently: giving focus to an element, or\n    ensuring an element is visible by y-scrolling its parent).  These\n    attributes produce the corresponding DOM action when they are\n    first put on an element (which is not completely well-defined,\n    since this depends on the synchronization algorithm).\n\n  - The \"view\" function is **not** applied synchronously when the\n    state (\"model\") changes.  Instead, a rendering (applying the\n    \"view\" function and updating the actual DOM accordingly) is\n    scheduled.  This means that multiple changes can be grouped\n    without triggering a redraw.  The current strategy is to delay\n    redrawing with [window.requestAnimationFrame](https://developer.mozilla.org/fr/docs/Web/API/Window/requestAnimationFrame), which is supposed to be available (natively,\n    or through a polyfill).\n\n\n\nUsage\n-----\n\nA simple one-module application would look like:\n\n```ocaml\nopen Vdom\n\n(* Definition of the vdom application *)\n\nlet view model =  ...  (* the state-\u003evdom rendering function *)\nlet init = return ... (* the initial state *)\nlet update model = function .... (* the state-updating function *)\nlet my_app = app ~init ~update ~view ()\n\n\n(* Driver *)\n\nopen Js_browser\n\nlet run () =\n  Vdom_blit.run my_app   (* run the application *)\n  |\u003e Vdom_blit.dom    (* get its root DOM container *)\n  |\u003e Element.append_child (Document.body document)   (* insert the DOM in the document *)\n\nlet () = Window.set_onload window run\n```\n\nUse the following Dune file to compile this to JavaScript:\n\n```\n(executable\n (name myprog)\n (libraries vdom)\n (modes js))\n```\n\nThe resulting JavaScript file `myprog.bc.js` can be found in the `_build` directory and can then be used from a simple HTML file such as:\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cscript src=\"myprog.bc.js\"\u003e\u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nExamples: [`Demo`](examples/demo/demo.ml), [`Counters`](examples/counters/counters.ml).\n\nThird-party examples:\n- `TodoMVC` ([source](https://github.com/slegrand45/examples_ocaml_vdom/blob/master/todomvc/todomvc.ml), [demo](https://slegrand45.github.io/examples_ocaml_vdom.site/todomvc/))\n- [`With Eliom service`](https://github.com/slegrand45/examples_ocsigen/blob/master/eliom/with-ocaml-vdom/simple/mixvdomandeliom.eliom).\n- `camel-finder` ([source](https://github.com/edwinans/camel-finder), [demo](https://edwinans.github.io/camel-finder/))\n\nAbout\n-----\n\nThis project has been created by LexiFi initially for its internal\nuse. It has been used in production since 2016 but no commitment is made on\nthe stability of its interface. So please let us know if you consider\nusing it!\n\nThis ocaml-vdom package is licensed by LexiFi under the terms of the\n[MIT license](LICENSE).\n\nContact: alain.frisch@lexifi.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLexiFi%2Focaml-vdom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLexiFi%2Focaml-vdom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLexiFi%2Focaml-vdom/lists"}