One can argue however that we did not have separated the view part from the "controller" part. Now this is fine if you are used to Common Lisp (so probably fine for all of you ;-), but hardly for a larger audience. For most Common Lisp is an alien. And especially for web "Designer", that are those you add funky layout (or not so funky ones) to you pages, it's no-where land. So how can we break out of it?
Tal to the rescue.
Now this part of UCW is even less documented then anything else. I just invite you to try the following simple thing. Display a form to the user, from a tal template....
Or stay tuned and follow this pages ;-)
You have to check the examples or doc strings quite careful. You can find something here or try you luck with
(apropos "template" :ucw)and there you'll find something. Now you just have to see that you include 'TEMPLATE-COMPONENT' into the parent list of you class.
The UCW examples use this pattern (slighly modified for the example here")
(defclass tal-test (simple-window-component template-component)
((val-1 :accessor val-1 :initform (make-instance 'string-field
:input-size 20
:validators (list (make-instance 'not-empty-validator))))
(val-2 :accessor val-2 :initform (make-instance 'string-field
:input-size 20
:validators (list (make-instance 'not-empty-validator)))))
(:metaclass standard-component-class)
(:default-initargs :template-name "tal/tal-test.tal"
:title "UCW Tutoial TALitations"))
I added the SIMPLE-WINDOW-COMPONENT because I liked to get the proper heading for a valid xhtml page, which are added by including this part.
As you can see I used the same :INITFORM as before.
What is new is the :TEMPLATE-NAME argument. This tells which component is responsible for rendering this class.
Here's now our first TAL Template
< div xmlns:tal="http://common-lisp.net/project/bese/tal/core"
xmlns:ucw="http://common-lisp.net/project/ucw/core"
tal:in-package="ucw.tutorial">
< h1>To TAL or not
< form method="POST"
ucw:action="(tal-test-viewer $component)">
< ucw:render-component ucw:component="(val-1 $component)"/>
< ucw:render-component ucw:component="(val-2 $component)"/>
< input type="submit"/>
< /form>
< /div>
ATTENTION: I added a blank between < and opening or closing tag otherwise the rendering failed.
The first two entries define the XML name space (xmlns). If you follow those URL's you'll see the definition of which tags and attributes are legal in a UCW TAL file, and in what context. It's not quite documentation, but useful since essentially these xmlns files are source code and they tell the XML parser how to read the TAL file.
The next part is easy. I just check that I'm working in the proper package to access the embedded lisp stuff without the need to qualify things.
The embedded lisp can be found as a string behind special tags.
There must be quite few others. I hope I'll find them at the proper time. A few parts have names starting with '$', $COMPONENT stands for the currently active component.
This TAL page renders the class TAL-TEST, so we have to write an function to act on this part. So here we go:
(defaction tal-test-viewer ((form tal-test)) (call 'tal-test-viewer :data form))
This is "business-as-usual". I wrote the following class to render the result:
(defcomponent tal-test-viewer (simple-window-component) ((data :initarg :data :accessor data)))(defmethod render ((win tal-test-viewer)) (<:p "val-1 = " (<:as-is (value (val-1 (data win))))) (<:p "val-2 = " (<:as-is (value (val-2 (data win))))))
Now it could have been possible to use another template for this task, as you would expect in a surrounding where View and Controller are more clearly separated. Agreed it's nested a bit deep, but improving that is not really hard.
(defentry-point "index.ucw" (:application *ucw-yaml-test-application*) () (call 'tal-test))
(defclass foo-test (simple-window-component) ((v1 :accessor v1 :initform "v1 Value") (v2 :accessor v2 :initform "v2 Value" )) (:metaclass standard-component-class))
(defmethod render ((foo-obj foo-test)) (<:p "v1 = " (<:as-is (v1 foo-obj))) (<:p "v2 = " (<:as-is (v2 foo-obj))))
This will obviously get rendered without any "decoration", no header no footer. So how to get around this?
With the following templates:
header.tal: < h1>I'm the marvellous header footer.tal: < h1>And I'm the gorgeous footerATTENTION: Problems with rendering, added blank between < and tag. And the following render method:
defmethod render ((foo-obj foo-test))(render-template *context* "tal/header.tal" nil) ;; extra arguments for the environment, that would mean ;; if foo-test is added here that one should be able to ;; access the fields v1 and v2
(<:p "v1 = " (<:as-is (v1 foo-obj))) (<:p "v2 = " (<:as-is (v2 foo-obj))) (render-template ucw:*context* "tal/footer.tal" nil))
You get the proper output. You get the header you get the body and you get the footer.
Please take the comment with some grains of salt. It's suspicion at the moment. I'm not sure it it's really correct. Ok, here we go Marco has send me the following:
from Marco Baringerbasically a tal env is a list of 'things' mapping keys to values. when we look for a value associated with a key (using the $foo syntax) we check each 'thing' in the tal-env, in order, and return the value of the first 'thing' which has a key named, in this case, foo.
if the thing is an object then we look for a slot with that name and return the slot-value. if thing is a hash-table then we see if the hash-table has a key, if so we return the value. if thing is an alist we see if the car of one of the cons cells in the alist is #'eql to the key and return the cdr if it is. otherwise we return nil.
Here we used the mentioned RENDER-TEMPLATE function. You can not test it stand alone in your programs easily because *CONTEXT* is bound dynamically to the proper *CONTEXT* in a request. So be careful.
Maybe while solving the exercise will lead us to some of them ;-)
This page is linked from: ucw Tutorial ucwTutorialAddingAValidatorToTheExample ucwTutorialTalification
CLiki pages can be edited by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively