Skip to content


Subversion checkout URL

You can clone with
Download ZIP
ClojureScript Mini Model View Presenter framework
JavaScript Clojure
branch: master


latest commit fb9be9199b
dima authored



Mini-MVP (Model View Presenter) library serves as a clean isolation layer among data models (Model), business logic (Presenter) and UI representation (View).
Any model that supports ILookup protocol can be used as a composite model, which comprises other composite and atomic model.
Model that doesn't support ILookup protocol is atomic model as far as Mini-MVP is concerned.

Mini-MVP strives for keeping all relationships among M-V-P components and composite MVP triads well-defined and concise.

Let's say you have some data model that you'd like to use.
(def my-model (mvp/create { :type Text :hint "Enter your age" :value "1234" :error "Value too big to be true" }))

Let's bind this model onto a view that renders the text and the error from the model. I'm using jQuery here, the demo also uses Closure.

;; Defines a subscriber that updates a control's value if the model changes.
(mvp/add-reader text-model [:text] #(.val (jq "#text2") (:new-value %)))

;; In a similar way, we also render the error message, which is generated by a validator
(mvp/add-reader my-model [:error] #(.html (jq "#error-message") (:new-value %)))

;; let's assume we have a generic age validation function
(defn- age-validator
    "Answers a text error message if the specified age is out of valid range.
     Yields an empty string in case if the specified age appears to be correct."
    (if (> age 120) "Value too big to be true" ""))

;; let's use the age validation function in our model
   my-model  ;; we subscribe to changes in my-model
   [:value]  ;; at path ROOT -> :value
   ;; and when a value under ROOT->:value changes, we update the value at ROOT->:error
   #(mvp/assoc-value my-model [:error] (age-validator (:new-value %))))

;; finally, update the model when the age is changed
(.change (jq "#input-control") #(mvp/assoc-value my-model [:value] (. (jq "#input-control") (val))))

Now, if you enter any age less than 120, the error message is cleared. Enter more than 120 and it'll show up again.


create <some clojure data model> <optional initial version> <optional function to suggest the next version given the current>
       Creates a new MVP model.

create-ref <existing MVP mode> <path in the model>
       Creates a model that redirects to a subpath in the parent model.

get-version <MVP model>
       Answers the current version of the model. Each change causes version change.

get-value <MVP model> [path] <optional default value>
       Similar to clojure.core/get-in, gets the value in the model under the specified path.
       Empty path [] fetches the root of the model.

update-value <MVP model> [path] <function to apply> <additional arguments to the function ...>
       Similar to clojure.core/update-in, updates the model's value under the specified path. The specified function is invoked to get the new value.
       Producing equal value is considered a no-op.

assoc-value <MVP model> [path] <new-value>
       Similar to clojure.core/assoc-in, associates a new value with the sub-model at the specified path.
       Assigning equal value is considered a no-op.

add-writer <MVP model> [path] <subscriber function>
       Adds a 1st phase subscriber function that is invoked when the value under the specified path changes.
       The function is not invoked if the change(s) didn't cause actual value change.
       I.e. given a model { :first "CA" :second "NY" } which is being updated to { :first "WA" :second "NY" }, 
       the subscriber for [:second] isn't invoked. The subscribers for [] and [:first] are to be called.

       A subscriber is a function that receives a single parameter { :new-value <new-value> :old-value <old-value> :new-version <new-model-version> }.

add-reader <MVP model> [path] <subscriber function>
       Exactly the same as add-writer, but these folks are invoked after all writers. 
       The idea is to avoid unnecessary UI rendering/flickering while validators/statistics update the model.

       A subscriber is a function that receives a single parameter { :new-value <new-value> :old-value <old-value> :new-version <new-model-version> }.

clear <MVP model> [path]
       Eliminates all subscribers under the specified path.
       (clear my-model []) ;; eliminates all existing subscribers

delay-events <MVP model> <function>
       All subscriber calls are delayed until after the function completion.
       Only subscribers which guarded value has actually changed are called.
       I.e. given a subscriber for path=[1] in a model ["a" "b" "c"], three subsequent changes within (delay-events ...)
       ["a" "b" "c"] -> ["b" "c" "d"] -> ["e" "f" "g"] -> ["h" "b" "e"] won't trigger the subscriber (which is subscribed for "b").

version <MVP model>
       Returns the current version of the model. By default, a new unique identifier is generated after each actual change.
       You can define the initial version and a function to generate new versions:
       (mvp/create { data...} 12 inc)  ;; will start with version=12, which is incremented upon each change


Same as Clojure/ClojureScript.

Something went wrong with that request. Please try again.