diff -r 76ab3c71aff2 -r c67bcee93248 doc/book/devweb/edition/dissection.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/devweb/edition/dissection.rst Thu Jan 08 22:11:06 2015 +0100 @@ -0,0 +1,316 @@ + +.. _form_dissection: + +Dissection of an entity form +---------------------------- + +This is done (again) with a vanilla instance of the `tracker`_ +cube. We will populate the database with a bunch of entities and see +what kind of job the automatic entity form does. + +.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker + +Populating the database +~~~~~~~~~~~~~~~~~~~~~~~ + +We should start by setting up a bit of context: a project with two +unpublished versions, and a ticket linked to the project and the first +version. + +.. sourcecode:: python + + >>> p = rql('INSERT Project P: P name "cubicweb"') + >>> for num in ('0.1.0', '0.2.0'): + ... rql('INSERT Version V: V num "%s", V version_of P WHERE P eid %%(p)s' % num, {'p': p[0][0]}) + ... + + + >>> t = rql('INSERT Ticket T: T title "let us write more doc", T done_in V, ' + 'T concerns P WHERE V num "0.1.0"', P eid %(p)s', {'p': p[0][0]}) + >>> commit() + +Now let's see what the edition form builds for us. + +.. sourcecode:: python + + >>> cnx.use_web_compatible_requests('http://fakeurl.com') + >>> req = cnx.request() + >>> form = req.vreg['forms'].select('edition', req, rset=rql('Ticket T')) + >>> html = form.render() + +.. note:: + + In order to play interactively with web side application objects, we have to + cheat a bit to have request object that will looks like HTTP request object, by + calling :meth:`use_web_compatible_requests()` on the connection. + +This creates an automatic entity form. The ``.render()`` call yields +an html (unicode) string. The html output is shown below (with +internal fieldset omitted). + +Looking at the html output +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The form enveloppe +'''''''''''''''''' + +.. sourcecode:: html + +
main informations
+
+
+
validating...
+
+ + + + + + + + ... +
+ +
+
+ +The main fieldset encloses a set of hidden fields containing various +metadata, that will be used by the `edit controller` to process it +back correctly. + +The `freezeFormButtons(...)` javascript callback defined on the +``onlick`` event of the form element prevents accidental multiple +clicks in a row. + +The ``action`` of the form is mapped to the ``validateform`` controller +(situated in :mod:`cubicweb.web.views.basecontrollers`). + +A full explanation of the validation loop is given in +:ref:`validation_process`. + +.. _attributes_section: + +The attributes section +'''''''''''''''''''''' + +We can have a look at some of the inner nodes of the form. Some fields +are omitted as they are redundant for our purposes. + +.. sourcecode:: html + +
+ + + + + + ... (description field omitted) ... + + + + + ... (type field omitted) ... + + + + + + + + +
+ +
+ +
importance
+
+ +
+ +
version in which this ticket will be / has been done
+
+
+ + +Note that the whole form layout has been computed by the form +renderer. It is the renderer which produces the table +structure. Otherwise, the fields html structure is emitted by their +associated widget. + +While it is called the `attributes` section of the form, it actually +contains attributes and *mandatory relations*. For each field, we +observe: + +* a dedicated row with a specific class, such as ``title_subject_row`` + (responsability of the form renderer) + +* an html widget (input, select, ...) with: + + * an id built from the ``rtype-role:eid`` pattern + + * a name built from the same pattern + + * possible values or preselected options + +The relations section +''''''''''''''''''''' + +.. sourcecode:: html + +
+ This ticket : + + + + +
+ + + + + + +
  
+ +
+
+
+ +The optional relations are grouped into a drop-down combo +box. Selection of an item triggers a javascript function which will: + +* show already related entities in the div of id `relatedentities` + using a two-colown layout, with an action to allow deletion of + individual relations (there are none in this example) + +* provide a relation selector in the div of id `relationSelector_EID` + to allow the user to set up relations and trigger dynamic action on + the last div + +* fill the div of id `unrelatedDivs_EID` with a dynamically computed + selection widget allowing direct selection of an unrelated (but + relatable) entity or a switch towards the `search mode` of + |cubicweb| which allows full browsing and selection of an entity + using a dedicated action situated in the left column boxes. + + +The buttons zone +'''''''''''''''' + +Finally comes the buttons zone. + +.. sourcecode:: html + + + + + + + + +
+ + + + +
+ +The most notable artifacts here are the ``postForm(...)`` calls +defined on click events on these buttons. This function basically +submits the form. + +.. _validation_process: + +The form validation process +--------------------------- + +Validation loop +~~~~~~~~~~~~~~~ + +On form submission, the form.action is invoked. Basically, the +``validateform`` controller is called and its output lands in the +specified ``target``, an invisible `` + +The ``window.parent`` part ensures the javascript function is called +on the right context (that is: the form element). We will describe its +parameters: + +* first comes the form id (`entityForm`) + +* then two optional callbacks for the success and failure case + +* an array containing: + + * a boolean which indicates status (success or failure), and then, on error: + + * an array structured as ``[eid, {'rtype-role': 'error msg'}, ...]`` + + * on success: + + * a url (string) representing the next thing to jump to + +Given the array structure described above, it is quite simple to +manipulate the DOM to show the errors at appropriate places. + +Explanation +~~~~~~~~~~~ + +This mecanism may seem a bit overcomplicated but we have to deal with +two realities: + +* in the (strict) XHTML world, there are no iframes (hence the dynamic + inclusion, tolerated by Firefox) + +* no (or not all) browser(s) support file input field handling through + ajax.