Added wsf_html documentation in the doc/workbook.

Also improved the structure of `wsf_html` library.
Added a few widgets.
This commit is contained in:
2017-02-14 19:37:03 +01:00
parent b93cb17f7c
commit 5dc9d82df7
43 changed files with 401 additions and 33 deletions

View File

@@ -20,7 +20,7 @@ Nav: [Workbook](../workbook.md) :: [Basic Concepts](../basics/basics.md) :: [Han
An HTML Form can handle `GET` and `POST` requests.
When we use a form with method `GET`, the data is attached at the end of the url for example:
>http://wwww.example.com?key1=value1&...keyn=valuen
>http://wwww.example.com/?key1=value1&...keyn=valuen
If we use the method `POST`, the data is sent to the server in a different line.

View File

@@ -7,6 +7,7 @@
* [Handling Requests: Header Fields](#header_fields)
* [Generating Responses](#generating_responses)
* [Handling Cookies](#handling_cookies)
* [Web User Interface](#wui)
* [EWF Deployment](#deployment)
<a name="core"></a>
@@ -39,6 +40,11 @@ Before reading (or walking throught) the workbook, to get a quick overview of EW
## Generating Response
[Generating Responses](./generating_response/generating_response.md)
<a name="wui"></a>
## Web User Interface
[Web User Interface](./wui/readme.md)
<a name="handling_cookies"></a>
## Handling Cookies

235
doc/workbook/wui/readme.md Normal file
View File

@@ -0,0 +1,235 @@
# Web User Interface (WUI)
When it comes to web user interface, HTML, CSS and Javascript are part of the solution.
To generate the HTML and CSS, there are many solution, among them:
* use template (such as can use templates like the [Eiffel Smarty lib](https://github.com/EiffelSoftware/EiffelStudio/tree/master/Src/contrib/library/text/template/smarty)
* generate the html from the Eiffel code directly
* use high level components that generate the html themselves.
Within the EiffelWeb framework, the `wsf_html` library provides classes to build a structured representation of html and then generate expected HTML.
## Overview
The [`wsf_html` library](https://github.com/EiffelWebFramework/EWF/tree/master/library/server/wsf_html) provides:
* `CSS_*` classes to generate CSS style.
* `WSF_WIDGET_*` classes representing HTML widget (such as `<table..`, raw text, pager, ..)
* and `WSF_FORM_*` classes which are also widgets, but specific to web forms (i.e `<form ..`, `<input ...`, ...).
In addition, the `WSF_FORM_..` is capable to analyze parameters from request `WSF_REQUEST` to populate its entries, but support value validation, and finally value submission. See the section "web form handling".
## Generating web form html
A simple solution to return html content is to use the `WSF_HTML_PAGE_RESPONSE` for now.
In a `WSF_EXECUTION` descendant, see how to implement the `execute` procedure.
```eiffel
execute
local
mesg: WSF_HTML_PAGE_RESPONSE
do
create mesg.make
mesg.set_title ("This is an HTML page")
mesg.set_body ("<h1>HTML Response</h1>")
response.send (mesg)
end
```
In this case, the html is hardcoded, and written manually.
Now let's see how to use the WSF widgets on a more advanced usecase, a simple sign-in web form with 2 input fields `username`, `password` and `submit` button.
```eiffel
execute
do
-- Basic url dispatching...
if request.is_post_request_method then
execute_web_form_submission
else
execute_web_form_display
end
end
execute_web_form_display
local
mesg: WSF_HTML_PAGE_RESPONSE
f: WSF_FORM
l_html: STRING
do
-- Build the web form
f := new_login_form
-- Html generation
--| the first argument of `to_html` is the theme for advanced usage you can provide a custom theme
--| that can redefine how to generate link for instance.
l_html := f.to_html (create {WSF_REQUEST_THEME}.make_with_request (request))
-- Send the response
create mesg.make
mesg.set_title ("This is a Web form")
mesg.set_body (l_html)
response.send (mesg)
end
execute_web_form_submission
do
... this will be explain in next section !
end
new_login_form: WSF_FORM
local
f: WSF_FORM
f_set: WSF_FORM_FIELD_SET
f_username: WSF_FORM_TEXT_INPUT
f_password: WSF_FORM_PASSWORD_INPUT
f_submit: WSF_FORM_SUBMIT_INPUT
do
-- Build the web form
create f.make ("/form", "form-login") -- The form id is optional.
f.set_method_post -- it could also use the default GET method.
-- Put specific html code
f.extend_html_text ("<h1>Web form example</h1>")
-- username input field
create f_username.make ("username")
f_username.set_label ("User name")
f.extend (f_username)
-- password input field
create f_password.make ("password")
f_password.set_label ("Password")
f.extend (f_password)
-- submit button
create f_submit.make_with_text ("op", "Login")
f.extend (f_submit)
Result := f
end
```
## Handling web form data
When a form is submitted, the code can access the request data thanks to the `request: WSF_REQUEST` attribute.
See [Handling requests section](../handling_request/form.md)
The `wsf_html` library, thanks to the `WSF_FORM`, provides a more powerful solution.
Indeed `WSF_HTML.process (request, .., ..)` analyze the request, fill the form fields, and process various validations, and submissions automatically.
see
```eiffel
process (req: WSF_REQUEST; a_before_callback, a_after_callback: detachable PROCEDURE [WSF_FORM_DATA])
-- Process Current form with request `req`,
-- and build `last_data: WSF_FORM_DATA` object containing the field values.
-- agent `a_before_callback` is called before the validation
-- agent `a_after_callback` is called after the validation
```
In our previous sample code, `execute_web_form_submission` could be:
```eiffel
execute_web_form_submission
local
mesg: WSF_HTML_PAGE_RESPONSE
l_html: STRING
f: WSF_FORM
do
create mesg.make
mesg.set_title ("Web form submission")
create l_html.make_empty
-- Build again the same form.
f := new_login_form
-- Process this form with `request` data.
f.process (request, Void, Void)
if attached f.last_data as form_data and then not form_data.has_error then
-- `form_data` contains the submitted fields with their value.
-- Depending on the form data, display a response.
l_html.append ("Username: ")
if attached form_data.string_item ("username") as l_username then
-- The username may contain unicode, or characters like '<'
-- thus, it needs to be html encoded
l_html.append (mesg.html_encoded_string (l_username))
else
l_html.append ("missing value !!")
end
l_html.append ("<br/>")
if attached form_data.string_item ("password") as l_password then
l_html.append ("Password: ")
-- The password may contain unicode, or characters like '<'
-- thus, it needs to be html encoded
l_html.append (mesg.html_encoded_string (l_password))
else
l_html.append ("missing value !!")
end
l_html.append ("<br/>")
else
l_html.append ("Error while processing the web form!")
-- Display again the form (it will contain the submitted values, if any).
f.append_to_html (create {WSF_REQUEST_THEME}.make_with_request (request), l_html)
end
mesg.set_body (l_html)
response.send (mesg)
end
```
In this case, the code could report an error if the username is empty, or with unwanted character ... this could be done in the `execute_web_form_submission` code, but it is also possible to set validation on each form field.
If those validations reports error, then the `form_data.has_error` will be True.
To set validation,
For instance, in previous sample, accepts only alpha-numeric characters:
```eiffel
f_username.set_description ("Only alpha-numeric character are accepted.")
f_username.set_validation_action (agent (fd: WSF_FORM_DATA)
do
if attached fd.string_item ("username") as u then
if across u as ic some not ic.item.is_alpha_numeric end then
fd.report_invalid_field ("username", "Missing username value!")
elseif u.is_whitespace then
fd.report_invalid_field ("username", "Empty username value!")
end
else
fd.report_invalid_field ("username", "Missing username value!")
end
end)
```
Notes:
* If the validation is not satisfied, the form data will report `has_error` as True, and the associated form will be updated with submitted values, and with `class="error"` set to the related html code.
The errors are also available via the `form_data.errors` feature.
* Since the validation feature argument is the `WSF_FORM_DATA` itself, it is also possible to validate several fields at once.
If there is no error, the form submission process will trigger the `WSF_FORM.submit_actions`. This could be used when the form is built by different components, and each component will handle the submission of its associated fields.
## Catalog of widgets
The `wsf_html` library includes a collection of widgets and form items:
### `WSF_WIDGET_...`
* `.._DIV`: `<div..>..</div>` html element.
* `.._IMAGE`: `<img .../>` html element.
* `.._RAW_TEXT`: html escaped text.
* `.._TEXT`: html text.
And more advanced widgets such as:
* `.._PAGER`: to generate pager widget like ` << 1 2 3 .. >> `
* `.._TABLE`: a set of classes to generate `<table ../>` html element.
### `WSF_FORM_...`
Widget usually included in web forms, such as
* `WSF_FORM_*_INPUT`: text, password, file, submit, ...
* `WSF_FORM_FIELD_SET`, `.._TEXTAREA` ...
## What about CSS style
The `CSS_STYLE`, `CSS_SELECTOR` and `CSS_TEXT` can be used to generate CSS style sheat, but they can also be used to set css style to `WSF_WIDGET`.
## Potential future evolutions
For now, the `wsf_html` provides a simple solution to build web form. It is always possible to add new `WSF_WIDGET` to support more html elements.
Advanced usage could be built on top of the `wsf_html` to include for instance javascript actions, but this is out of the scope of `wsf_html` .

View File

@@ -0,0 +1,26 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
WSF_FORM_DIV
inherit
WSF_WIDGET_DIV
undefine
extend
end
WSF_FORM_COMPOSITE
create
make,
make_with_item,
make_with_items,
make_with_text,
make_with_text_and_css_id,
make_with_item_and_css_id
end

View File

@@ -8,16 +8,24 @@ class
WSF_FORM
inherit
WSF_WIDGET
WSF_FORM_COMPOSITE
WSF_WITH_HTML_ATTRIBUTE
WSF_WITH_CSS_ID
WSF_WITH_CSS_CLASS
WSF_WITH_CSS_STYLE
create
make
feature {NONE} -- Initialization
make (a_action: READABLE_STRING_8; a_id: READABLE_STRING_8)
make (a_action: READABLE_STRING_8; a_id: detachable READABLE_STRING_8)
do
action := a_action
id := a_id
@@ -33,8 +41,8 @@ feature -- Access
action: READABLE_STRING_8
-- URL for the web form
id: READABLE_STRING_8
-- Id of the form
id: detachable READABLE_STRING_8
-- Optional id of the form
is_get_method: BOOLEAN
do
@@ -56,9 +64,10 @@ feature -- Access
feature -- Basic operation
process (req: WSF_REQUEST; a_before_callback, a_after_callback: detachable PROCEDURE [WSF_FORM_DATA])
-- Process Current form with request `req'
-- agent `a_before_callback' is called before the validation
-- agent `a_after_callback' is called after the validation
-- Process Current form with request `req`,
-- and build `last_data: WSF_FORM_DATA` object containing the field values.
-- agent `a_before_callback` is called before the validation
-- agent `a_after_callback` is called after the validation
local
fd: WSF_FORM_DATA
do
@@ -120,22 +129,18 @@ feature -- Conversion
local
s: STRING_8
do
a_html.append ("<form action=%""+ action +"%" id=%""+ id +"%" method=%""+ method +"%"")
a_html.append ("<form action=%""+ action +"%"")
a_html.append (" method=%""+ method +"%"")
if attached encoding_type as enctype then
a_html.append (" enctype=%""+ enctype +"%"")
end
if not html_classes.is_empty then
create s.make_empty
across
html_classes as cl
loop
if not s.is_empty then
s.extend (' ')
end
s.append (cl.item)
end
a_html.append (" class=%"" + s + "%" ")
if attached id as l_id then
a_html.append (" id=%""+ l_id +"%"")
end
append_css_id_to (a_html)
append_css_class_to (a_html, html_classes)
append_css_style_to (a_html)
append_html_attributes_to (a_html)
a_html.append (">%N")
across
items as c
@@ -145,10 +150,4 @@ feature -- Conversion
a_html.append ("</form>%N")
end
to_html (a_theme: WSF_THEME): STRING_8
do
create Result.make_empty
append_to_html (a_theme, Result)
end
end

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {WSF_WIDGET_TABLE}."
author: ""
description: "Summary description for {WSF_WIDGET_AGENT_TABLE}."
date: "$Date$"
revision: "$Revision$"

View File

@@ -5,15 +5,21 @@ note
revision : "$Revision$"
class
WSF_FORM_DIV
WSF_WIDGET_DIV
inherit
WSF_FORM_ITEM
WSF_WIDGET
WSF_FORM_COMPOSITE
WSF_WIDGET_COMPOSITE
WSF_WITH_CSS_ID
WSF_WITH_CSS_CLASS
WSF_WITH_CSS_STYLE
WSF_WITH_HTML_ATTRIBUTE
create
make,
make_with_item,
@@ -71,14 +77,14 @@ feature -- Conversion
append_css_class_to (a_html, Void)
append_css_id_to (a_html)
append_css_style_to (a_html)
a_html.append (">%N")
append_html_attributes_to (a_html)
a_html.append (">")
across
items as c
loop
c.item.append_to_html (a_theme, a_html)
end
a_html.append ("%N</div>%N")
a_html.append ("</div>%N")
end
end

View File

@@ -0,0 +1,97 @@
note
description: "Summary description for {WSF_WIDGET_IMAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_WIDGET_IMAGE
inherit
WSF_WIDGET
WSF_WITH_CSS_ID
WSF_WITH_CSS_CLASS
WSF_WITH_CSS_STYLE
WSF_WITH_HTML_ATTRIBUTE
create
make
feature {NONE} -- Initialization
make (a_src: READABLE_STRING_8)
do
src := a_src
end
feature -- Access
src: READABLE_STRING_8
-- `src` html attribute.
alt: detachable READABLE_STRING_8
-- Alternate text for Current image.
width: detachable READABLE_STRING_8
-- Optional width value.
height: detachable READABLE_STRING_8
-- Optional height value.
feature -- Change
set_src (v: like src)
do
src := v
end
set_alt (v: like alt)
do
alt := v
end
set_width (v: like width)
do
width := v
end
set_height (v: like height)
do
height := v
end
feature -- Conversion
append_to_html (a_theme: WSF_THEME; a_html: STRING_8)
do
a_html.append ("<img src=%"")
a_html.append (src)
a_html.append ("%"")
if attached alt as l_alt then
a_html.append (" alt=%"")
a_html.append (l_alt)
a_html.append ("%"")
end
if attached width as w then
a_html.append (" width=%"")
a_html.append (w)
a_html.append ("%"")
end
if attached height as h then
a_html.append (" height=%"")
a_html.append (h)
a_html.append ("%"")
end
append_css_id_to (a_html)
append_css_style_to (a_html)
append_css_class_to (a_html, css_classes)
append_html_attributes_to (a_html)
a_html.append ("/>")
end
end