Removed Features

During refactoring, I removed some other interesting concepts from the framework core. They could be reborn as separate libraries as well as the navigation widget.

Views

Views provided convenient ways to define how data objects are to be rendered.

Originally, Weblocks supported three types of views:

Form views were especially useful because they let you build forms in a declarative manner, with error checking and reporting included.

Views mainly consist of a rendering framework unique to the view and a series of view fields. The table view, for example, knows how to render an HTML table.

View fields usually mapped to the slots of your data class but they were flexible enough to be used in any other way. Associated with each view field regardless of the view type were things like a human-readable label and information on how to render the field (presentation).

Form views included additional parameters like how to translate user input to a proper model value (parser) and constraints on the data (satisfies).

Templates

Templates were something different from views. They are another layer which allows customizing the views' look. Most of the Weblocks code supported templates and this gave us the ability to change widget HTML without changing widget logic.

Templates brought theming functionality into the original Weblocks and were intended to improve widget reuse.

I think it is better to split your render method into smaller ones and allow users to override these methods. But anyway, here is a short example of how templates worked in the old version of Weblocks.

Note! The following subsections contain only examples and such code is not supported in the latest version of Weblocks. However, you might want to implement something like that on your own or use djula, mustache or other template engine.

Template function

Template functions received some key parameters and returned HTML strings. You were able to use any HTML generation library or template library for templates.

You were encouraged to

Here is a template example.

(defun my-wt (&key (content "I'm template") &allow-other-keys)
    (with-html-to-string 
        (:p (str content))))

Template definition

A template definition is a deftemplate call.

(deftemplate :my-wt 'my-wt)

Here we just connected the :my-wt template name with the template function 'my-wt.

And here comes the most important template part. We can connect many template functions to a template name and only one of them - the effective template - will be called. Effectiveness is determined by priority which is received from context matchers. A context matcher just receives context and returns a priority number.

(defun my-other-wt(&key (content "I'm template") &allow-other-keys)
  (with-html-to-string 
    (:div :class "other-template" (str content))))

(deftemplate :my-wt 'my-other-wt 
  :context-matches (lambda(&rest context)
    100))

my-other-wt has higher priority than my-wt so it will be called. And this is how template overriding is done.

There is also an :application-class parameter which gives the template 10 more priority points.

(deftemplate :page-wt 'my-customized-page-wt 
             :application-class 'my-webapp)

Here the 'my-customized-page-wt function will have higher priority than the one defined in Weblocks.

Note. This context matching logic was too complex and in some places it intersected with CLOS and generic functions which is why I decided to exclude templates from the Weblocks core.

As an example of old-school templating, you may see Weblocks Twitter Bootstrap theme.

Footnotes

[1] An acyclic graph with exactly one parent per node.

[2] Each path component of a URI is a token; for example in "/foo/bar/quux" there are three tokens foo, bar and quux.

[3] I.e. symbols naming a global function and function objects themselves.

Forms

Warning! This section is outdated and belongs to the old Weblocks documentation. All widgets described below were removed from the core framework. Probably only a link to reblocks-ui is needed here.

Below is a description of how forms worked in the original Weblocks. It is given here to show how one might handle data model changes. Probably someone will want to resurrect this method in a Weblocks extension library.

Forms enable users to communicate with a web application.

Usually the server-side action boils down to selecting, modifying, creating or deleting data sets (this is sometimes abbreviated as CRUD: Create/Read/Update/Delete).

Building web forms is usually a cumbersome process. Elaborate but complicated solutions have been devised such as php-quickform, but so far none of them have been found to match the ease of use and flexibility of Weblocks' declarative view DSL.

Introduction

The form mechanism consists of two parts: the DATAFORM widget and the FORM-VIEW view type.

Forms are usually built by defining form views using the DEFVIEW macro and instantiating a DATAFORM object with this view.

Simple example

Let's define a view for creating and editing bug reports.

Let the model be defined thus:

(defclass bug-report ()
  ((id :type integer :initform (create-unique-id))
   (status :type (member :new :open :resolved) :initform :new)
   (summary :type string)
   (body :type string)))

This view should apply to users who are not developers: they may enter a summary and body, but that's it.

(defview bug-report-form-view
    (:type form :inherit-from '(:scaffold bug-report)
     :caption "Enter a bug report")
  (id :hidep t)
  (status :hidep t))

The SUMMARY and BODY fields will default to strings; every form field is presented as a text input and parsed as a string by default.

Let's use this view to derive the view for developers:

(defview bug-report-form-view/developer
    (:type form :inherit-from 'bug-report-form-view )
  (status :hidep nil))

The status field will automatically be displayed as a dropdown control since the scaffold inspector has decided this based on the slot's type.

Scaffolding rules can be defined for custom types.

As part of the validation process, Weblocks will also check whether user input matches the slot's type regardless of whether scaffolding is used or not.

But let's assume that custom labels are wanted for the dropdown:

(defview bug-report-form-view/developer
    (:type form :inherit-from 'bug-report-form-view )
  (status :hidep nil
          :present-as (dropdown :choices '(("This report is totally new, don't trust it!" . :new)
                                           ("Yeah okay, we're working on it." . :open)
                                           ("We've solved that problem already..." . :resolved)))))

Quickforms

Quickforms are specialized Dataforms.

They provide a way to build forms based entirely on a view; they are handy for operations where the user is not working on an actual model instance.

Let's dive right into it:

(make-quickform 
  (defview nil 
    (:caption "A Quickform" :type form :persistp nil)
    (some-text  :present-as input))
  :on-success (lambda (form data)
    (with-html ()
      "Submitted data - "
      (str (slot-value data 'some-text)))))

This will display a form with a single field. After form submission, text with the submitted value will be seen. The DATA object here is a class created dynamically from view fields.

:persistp nil

in the view definition is necessary, as the dynamic class should not persist.

There are options in MAKE-QUICKFORM for validation, control flow and other things. See the MAKE-QUICKFORM documentation.

Continuations-based tools

Warning! Continuations support was removed in the Reblocks fork.

This feature will be reborn as a separate library with a custom widget class.