diff --git a/workbook.html b/workbook.html index ebbfc3e2..6c32cca1 100644 --- a/workbook.html +++ b/workbook.html @@ -43,4 +43,9 @@ base_url: ../
  • handling cookies
  • +
  • wui/ + +
  • diff --git a/workbook/handling_request/form.md b/workbook/handling_request/form.md index 4d762c71..13e88eb7 100644 --- a/workbook/handling_request/form.md +++ b/workbook/handling_request/form.md @@ -25,7 +25,7 @@ Nav: [Workbook](../../workbook) :: [Basic Concepts](../../basics/basics) :: [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. diff --git a/workbook/workbook.md b/workbook/workbook.md index 7466df34..b8ca48fe 100644 --- a/workbook/workbook.md +++ b/workbook/workbook.md @@ -12,6 +12,7 @@ base_url: ../../ * [Handling Requests: Header Fields](#header_fields) * [Generating Responses](#generating_responses) * [Handling Cookies](#handling_cookies) +* [Web User Interface](#wui) * [EWF Deployment](#deployment) @@ -44,6 +45,11 @@ Before reading (or walking throught) the workbook, to get a quick overview of EW ## Generating Response [Generating Responses](../generating_response/generating_response) + + +## Web User Interface +[Web User Interface](../wui/readme) + ## Handling Cookies diff --git a/workbook/wui/readme.md b/workbook/wui/readme.md new file mode 100644 index 00000000..52279321 --- /dev/null +++ b/workbook/wui/readme.md @@ -0,0 +1,240 @@ +--- +layout: default +title: readme +base_url: ../../../ +--- +# 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 `HTML Response") + 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 ("

    Web form example

    ") + + -- 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) + +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 ("
    ") + 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 ("
    ") + 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`: `..` html element. +* `.._IMAGE`: `` 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 `` 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` . + + +