Introduction

Ant Design is an enterprise-class UI design language and React-based implementation. It has the following features:

  • An enterprise-class UI design language for web applications.

  • A set of high-quality React components out of the box.

  • Extensive well-documented API and examples.

Antizer is a ClojureScript library for Ant Design React components with bindings for Reagent and Rum.

Status

All the Ant Design components should be fully functional and production-ready. If you discover any missing or invalid components, please file a ticket.

Install

To use antizer, just add the following to your project.clj:

[antizer "0.3.1"]

You would also need to add the ClojureScript React library that you will be using.

For Reagent:

[reagent "X.Y.Z"]

For Rum:

[rum "X.Y.Z"]

It is also necessary to include the relevant Ant Design CSS stylesheet in your HTML page. There are two ways that the CSS files can be included:

  1. Loading the CSS stylesheet from an external CDN:

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/${antd_version}/antd.min.css">

    where ${antd_version} must be the same antd library version as the one that Antizer is using.

  2. Alternatively, the CSS sheetsheet can be loaded from either of the following classpaths. This can be done via Ring library’s wrap-resource function:

    • cljsjs/antd/development/antd.inc.css

    • cljsjs/antd/production/antd.min.inc.css

    An example of how this can be done is illustrated by https://github.com/dfuenzalida/antizer-demo.

You can also follow the instructions for customization with LESS here.

Resources

Quick Example

Here’s the classic example to help you get started:

For Reagent:

(require '[antizer.reagent :as ant])
(require '[reagent.core :as r])

(defn click-me []
  [ant/button {:on-click #(ant/message-info "Hello Reagent!")} "Click me"])

(defn init! []
  (r/render [click-me] (.-body js/document)))

For Rum:

(require '[antizer.rum :as ant])
(require '[rum.core :as rum])

(defn click-me []
  (ant/button {:on-click #(ant/message-info "Hello Rum!")} "Click me"))

(defn init! []
  (rum/mount (click-me) (.-body js/document)))
Please remember to include the CSS stylesheet using one of the methods specified here, otherwise the components would not be styled correctly.
It is not recommended to render to the <body> element. This is only done for illustrative purposes.

Programming Guide

Naming convention

In order to be consistent with ClojureScript naming conventions, all functions and properties are specified in lisp case (aka kebab-case), eg: on-click, wrapper-col

The Ant design components, functions and properties have been translated to kebab-case, via the following rules:

  1. Replace all instances of . with -.

  2. Except for the first letter, insert a dash before any uppercase letters, unless there already is a dash before this letter.

  3. Convert the whole string to lowercase.

Table 1. Example of name mappings
Ant Design component or function name Antizer name

Calendar

calendar

Checkbox.Group

checkbox-group

DatePicker.RangePicker

date-picker-range-picker

InputNumber

input-number

Modal.confirm

modal-confirm

notification.info

notification-info

The full list of the Ant Design component and function names can be found here.

Types

As can be seen from the module list here, there are three main types within the Antizer library that maps to the equivalent in the Ant Design library.

  • Functions

  • Properties

  • Components

Functions

These are just normal ClojureScript functions. All the functions reside in their UI respective library namespaces:

Example for reagent:

(require '[antizer.reagent :as ant])

(ant/message-info "Hello World!")

Example for rum:

(require '[antizer.rum :as ant])

(ant/message-info "Hello World!")

The full list of the Ant Design functions can be found here.

Components

These are the backbone of the entire library. Please refer to the Components section of the respective ClojureScript React libraries.

Properties

There is currently only one property type currently: locales. Please refer to the Localization section of the respective ClojureScript React libraries.

Return Values

All return values from the callback functions of the Ant Design components and the form functions will be in the form of JavaScript objects. Please use (js->clj) to convert them before any ClojureScript operations are to be performed on them.

Reagent

Components

The Antizer components can be treated like any Reagent components. To use them, wrap them in a Hiccup style list:

(require '[antizer.reagent :as ant])

(defn render-menu []
  [ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}
             :on-click #(println "You have clicked" %1)}
    [ant/menu-item {:key "item1"} "Menu Item 1"]
    [ant/menu-item {:key "item2"} "Menu Item 2"]])

All the properties that are passed in can be in kebab-case.

Any properties that begins with data has to be in camel case instead of kebab case, as they will be treated as html attributes. Thus, DO NOT use data-index and data-source as they would not work, use dataIndex and dataSource instead.

All return values from the callback functions of the Ant Design components and the form functions will be in the form of JavaScript objects. Please use (js->clj) to convert them before any ClojureScript operations are to be performed on them.

In some cases, certain components (eg: menu-sub-menu and tooltip) can accept React.Elements as their properties. In such cases, please use (reagent.core/as-element) to turn Reagent’s hiccup forms into React elements.

(require '[antizer.reagent :as ant])
(require '[reagent.core :as r])

(defn render-menu []
  [ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}}
    ;; adds a sub-menu containing a "home" icon and "SubMenu 1" text
    [ant/menu-sub-menu {:title
                         (r/as-element
                           [:span [ant/icon {:type "home"}] "SubMenu 1"])}]])

The full list of the Ant Design component names can be found here.

Forms

Form creation

Antizer supports the use of Form.create() which helps to collect and validate the form data automatically. If you prefer to do this manually or prefer using another validation library, you can skip the section below and use the the Form API found here.

The following is an example of how to use Antizer’s implementation of Form.create():

(require '[antizer.reagent :as ant])
(require '[reagent.core :as r])

(defn actual-form []
  (fn [props]
    (let [my-form (ant/get-form)]
      [ant/form
        [ant/form-item {:label "Name"}
          (ant/decorate-field my-form "name"
            [ant/input])]
        [ant/form-item {:label "Password"}
          ;; validates that the password field is not empty
          (ant/decorate-field my-form "password" {:rules [{:required true}]}
            [ant/input])]])))

(defn init-form []
  (ant/create-form (actual-form)))

(defn init! []
  (r/render [init-form] (.-body js/document)))

The initial step involves calling (create-form) with the form component to be rendered.

Within the form component the form object should be obtained via (get-form), which obtains the form object from the React component’s property. This should be done only after the component has been constructed.

In the case of Reagent, this can be done right at the start of the render stage, by wrapping the Reagent Hiccup elements with a function. Next, call (antizer.reagent.get-form) to obtain the form object that will be used by the field decorator and form functions.

It is recommended to pass in the React props to the form render function so that the form object is accessible. This is needed in some cases like nested forms.

Without the wrapper function, (antizer.reagent.get-form) would try to obtain the form value before the component has been constructed, and thus a nil value will be returned.
Field decoration

(decorate-field) is used to add automatic data capture and validation features to each form field. This function accepts the following parameters:

(decorate-field form identifier [options] form-field)

It corresponds to the getFieldDecorator function. For an explaination of the parameters, please refer to the API documentation, the Ant Design Form page and the examples provided.

Form functions

After the form has been obtained via (get-form), the form functions can be used.

An example of the usage of these functions is shown here:

(require '[antizer.reagent :as ant])
(require '[reagent.core :as r])

(defn actual-form []
  (fn [props]
    (let [my-form (ant/get-form)]
      [ant/form
        [ant/form-item {:label "Name"}
          (ant/decorate-field my-form "name" {:rules [{:required true :min 10}]}
            [ant/input])]
        [ant/form-item
          [ant/button {:on-click #(ant/validate-fields my-form)} "Submit"]
          [ant/button {:on-click #(ant/reset-fields my-form)} "Reset"]]])))

(defn init-form []
  (ant/create-form (actual-form)))

(defn init! []
  (r/render [init-form] (.-body js/document)))

The form functions (eg: reset-fields, validate-fields) correspond to the form functions which can be found here.

All return values from the callback functions of the Ant Design components and the form functions will be in the form of JavaScript objects. Please use (js->clj) to convert them before any ClojureScript operations are to be performed on them.

Localization / i18n

Locale Provider

Antizer supports the use of locale provider to provide localization / i18n support.

This is how it works:

(require '[antizer.reagent :as ant])

(defn render-app []
  [ant/locale-provider {:locale (ant/locales "en_US")}
    [content]])

It is recommended to place the locale provider at the top level of the application. The whole list of supported locales can be found here.

To add a new language, please refer to this.

Moment.js

Some Ant Design components (eg: calendar, date-picker, date-picker-range-picker) uses the Moment.js library to display the dates in the correct language.

To set the locale for Moment.js:

(require '[cljsjs.moment.locale.es])

(defn render-app []
  [ant/locale-provider {:locale (ant/locales "es_ES")}
    [ant/calendar {:default-value (js/moment)}]])

Note that each locale to be used has to be included using require seperately.

Rum

Components

The Antizer components can be treated like any other Rum components. This is how to make use of them:

(require '[antizer.rum :as ant])

(defn render-menu []
  (ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}
             :on-click #(println "You have clicked" %1)}
    (ant/menu-item {:key "item1"} "Menu Item 1")
    (ant/menu-item {:key "item2"} "Menu Item 2")))

All the properties that are passed in can be in kebab-case.

Any properties that begins with data has to be in camel case instead of kebab case, as they will be treated as html attributes. Thus, DO NOT use data-index and data-source as they would not work, use dataIndex and dataSource instead.

All return values from the callback functions of the Ant Design components and the form functions will be in the form of JavaScript objects. Please use (js->clj) to convert them before any ClojureScript operations are to be performed on them.

In some cases, certain components (eg: menu-sub-menu and tooltip) can accept React.Elements as their properties. In this case, simply send the hiccup form in:

(require '[antizer.rum :as ant])

(defn render-menu []
  (ant/menu {:mode "inline" :theme "dark" :style {:height "100%" :width "20%"}}
    ;; adds a sub-menu containing a "home" icon and "SubMenu 1" text
    (ant/menu-sub-menu {:title [:span (ant/icon {:type "home"}) "SubMenu 1"]})))

The full list of the Ant Design component names can be found here.

Forms

Form creation

Antizer supports the use of Form.create() which helps to collect and validate the form data automatically. If you prefer to do this manually or prefer using another validation library, you can skip the section below and use the the Form API found here.

The following is an example of how to use Antizer’s implementation of Form.create():

(require '[antizer.rum :as ant])
(require '[rum.core :as rum])

(rum/defcs actual-form
  [state]
  (let [my-form (ant/get-form state)]
    (ant/form
      (ant/form-item {:label "Name"}
        (ant/decorate-field my-form "name"
          (ant/input)))
      (ant/form-item {:label "Password"}
        (ant/decorate-field my-form "password" {:rules [{:required true}]}
          (ant/input))))))

(defn init-form []
  (ant/create-form actual-form))

(defn init! []
  (rum/mount (init-form) (.-body js/document)))

The initial step involves calling (create-form) with the form component to be rendered.

Within the form component the form object should be obtained via (get-form), which obtains the form object from the React component’s property. This should be done only after the component has been constructed.

In the case of Rum, use rum.core/defcs to define a component that receives the state parameter. Next, call (antizer.rum/get-form) with the state parameter to obtain the form object that will be used by the field decorator and form functions.

Field decoration

(decorate-field) is used to add automatic data capture and validation features to each form field. This function accepts the following parameters:

(decorate-field form identifier [options] form-field)

It corresponds to the getFieldDecorator function. For an explaination of the parameters, please refer to the API documentation, the Ant Design Form page and the examples provided.

Form functions

After the form has been obtained via (get-form), the form functions can be used.

An example of the usage of these functions is shown here:

(require '[antizer.rum :as ant])
(require '[rum.core :as rum])

(rum/defcs actual-form
  [state]
  (let [my-form (ant/get-form state)]
    (ant/form
      (ant/form-item {:label "Name"}
        (ant/decorate-field my-form "name" {:rules [{:required true :min 10}]}
          (ant/input)))
      (ant/form-item
        (ant/button {:on-click #(ant/validate-fields my-form)} "Submit")
        (ant/button {:on-click #(ant/reset-fields my-form)} "Reset")))))

(defn init-form []
  (ant/create-form actual-form))

(defn init! []
  (rum/mount (init-form) (.-body js/document)))

The form functions (eg: reset-fields, validate-fields) correspond to the form functions which can be found here.

All return values from the callback functions of the Ant Design components and the form functions will be in the form of JavaScript objects. Please use (js->clj) to convert them before any ClojureScript operations are to be performed on them.

Localization / i18n

Locale Provider

Antizer supports the use of locale provider to provide localization / i18n support.

This is how it works:

(require '[antizer.rum :as ant])

(defn render-app []
  (ant/locale-provider {:locale (ant/locales "en_US")}
    (content)))

It is recommended to place the locale provider at the top level of the application. The whole list of supported locales can be found here.

To add a new language, please refer to this.

Moment.js

Some Ant Design components (eg: calendar, date-picker, date-picker-range-picker) uses the Moment.js library to display the dates in the correct language.

To set the locale for Moment.js:

(require '[cljsjs.moment.locale.es])
(require '[antizer.rum :as ant])

(defn render-app []
  (ant/locale-provider {:locale (ant/locales "es_ES")}
    (ant/calendar {:default-value (js/moment)})))

Note that each locale to be used has to be included using require seperately.

Examples

To compile the examples:

lein with-profile +examples cljsbuild once

To compile the examples and enable hot reloading with figwheel:

lein with-profile +examples-dev figwheel

After compilation, open up the respective HTML page in the examples/resources folder in your browser.

Troubleshooting

  • The table or auto complete component is not displaying the data.

Any properties that begins with data has to be in camel case instead of kebab case, as they will be treated as html attributes. Thus, DO NOT use data-index and data-source as they would not work, use dataIndex and dataSource instead.

Acknowledgement

Thanks to Ant Design, cljsjs/antd, Reagent, Rum and of course ClojureScript, without which this project would not be possible.

License

Copyright © 2017 Michael Lim

Licensed under Eclipse Public License (see LICENSE).