Removed Features
During refactoring, I've removed some other interesting conceptions from the core of the framework. They could be reborn as a separate libraries as well as navigation widget.
Views
Views provided convenient ways to define how data object are to be rendered.
Originally, Weblocks supported three types of views:
data views;
table views;
form views.
Form views were especially useful because they let you build forms in a declarative manner, 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 a HTML
table.
View fields usually map 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 are 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
Template were something different from view. It is another layer which allows to customize views look. Most of weblocks code supported templates and this gave us ability to change widget html without changing widget logic.
Templates brought theming functionality into original Weblocks, and intended to improve widgets reusing.
I think it is better to split you render method into a smaller ones and allow user to override these methods. But anyway, here is a short example of how did templated work in old version of the Weblocks.
Note! Following subsections contains only examples and such code is not supported in the latest version of the Weblocks. However you might want to implement something like that on your own or use djula, mustache or other template engine.
Template function
Template function received some key parameters and returned html string. You was able to use any html generation library or template library for templates.
You was encouraged to
use naming convention and call template
*-wt
(with "-wt" suffix ). "wt" means either "web template" or "Weblocks template". Since template should be overriden often, name convention will made easier to find what we need.use
&allow-other-keys
in every template.use no complex logic and no computations in templates. Just simple iteration over lists and if statements. The best thing would be to use no more logic then in mustache templates.
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
Template definition is a deftemplate
call.
(deftemplate :my-wt 'my-wt)
Here we just connected :my-wt
template name with template function 'my-wt
.
And here comes most important templates part. We can connect many template functions to template name and only one of them - effective template - will be called. Effectiveness determined by priority which is received from context matchers. Context matcher just receives context and returns 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 more priority than my-wt
so it will be called.
And this is how template overriding is done.
There is also :application-class
parameter which gives template 10
more priority points.
(deftemplate :page-wt 'my-customized-page-wt
:application-class 'my-webapp)
Here 'my-customized-page-wt
function will have more priority than
one defined in Weblocks.
Note. This context matching logic was too complex and at some places it was intersected with CLOS
and generic functions
that is why I've decided to exclude templates from the core of the Weblocks.
As an example for the oldschool templating you may see Weblocks Twitter Bootstrap theme.
Footnotes
[1] An acyclic graph with exactly one parent per node.
[2] Đ•ach path component of an 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 documention. All widgets, described below, were removed from the core framework. Probably we need only to link to the reblocks-ui here.
Below you'll find a description how did forms work in the original Weblocks. It is given to here to show who one might handle data-models changes. Probably someone will want to resurrect this method in a Weblocks extension library.
Forms enable the user 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
php-quickform,
but so far we haven't found any of them 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 that 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 upon the slot's type.
You can define scaffolding rules for your own types.
As part of the validation process Weblocks will also check whether a user input matches the slot's type regardless of whether you use scaffolding or not.
But let's assume that we want custom labels 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 you don't have the user 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 form with single field. After form submit we'll see text with value submitted.
DATA
object here is a class created dynamically from view fields.
:persistp nil
in view definition is necessary, we don't want dynamic class to persist.
There are options in MAKE-QUICKFORM for validation, control flow and other things. See MAKE-QUICKFORM documentation.
Continuations-based tools
Warning! Continuations support support was removed in the Reblocks fork.
This feature will reborn as a separate library with a custom widget class.