📄 app.html
字号:
for example store its value in a database.<p>The problem with such a straightforward use of forms is that<p><ol><li>they require the application programmer to take care of maintaining lots ofglobal variables. Each input field on the page needs an associated variable forthe round trip between server and client.<li>they do not preserve an application's internal state. Each POST requestspawns an individual process on the server, which sets the global variables totheir new values, generates the HTML page, and terminates thereafter. Theapplication state has to be passed along explicitly, e.g. using<code><hidden></code> tags.<li>they are not very interactive. There is typically only a single submitbutton. The user fills out a possibly large number of input fields, but changeswill take effect only when the submit button is pressed.</ol><p>Though we wrote a few applications in that style, we recommend the GUIframework provided by "lib/form.l". It does not need any variables for theclient/server communication, but implements a class hierarchy of GUI componentsfor the abstraction of application logic, button actions and data linkage.<p><hr><h3><a name="sessions">Sessions</a></h3><p>First of all, we need to establish a persistent environment on the server, tohandle each individual session (for each connected client).<p>Technically, this is just a child process of the server we started <ahref="#server">above</a>, which does not terminate immediately after it sent itspage to the browser. It is achieved by calling the <code>app</code> functionsomewhere in the application's startup code.<p><pre><code>########################################################################(app) # Start a session(html 0 "Simple Session" "lib.css" NIL (<post> NIL "project.l" (<field> 10 '*Text) (<submit> "Save") ) )########################################################################</code></pre><p>Nothing else changed from the previous example. However, when you connectyour browser and then look at the terminal window where you started theapplication server, you'll notice a colon, the Pico Lisp prompt<p><pre><code>$ ./p dbg.l lib/http.l lib/xhtml.l lib/form.l -'server 8080 "project.l"':</code></pre><p>Tools like the Unix <code>ps</code> utility will tell you that now two<code>picolisp</code> processes are running, the first being the parent of thesecond.<p>If you enter some text, say "abcdef", into the text field in the browserwindow, press the submit button, and inspect the Lisp <code>*Text</code>variable,<p><pre><code>: *Text-> "abcdef"</code></pre><p>you see that we now have a dedicated Pico Lisp process, "connected" to theclient.<p>You can terminate this process (like any interactive Pico Lisp) by hittingENTER on an empty line. Otherwise, it will terminate by itself if no otherbrowser requests arrive within a default timeout period of 5 minutes.<p>To start a (non-debug) production version, the server is commonly startedwithout "dbg.l", and with <code>-wait</code><p><pre><code>$ ./p lib/http.l lib/xhtml.l lib/form.l -'server 8080 "project.l"' -wait</code></pre><p>In that way, no command line prompt appears when a client connects.<p><hr><h3><a name="actionForms">Action Forms</a></h3><p>Now that we have a persistent session for each client, we can set up anactive GUI framework.<p>This is done by wrapping the call to the <code>html</code> function with<code>action</code>. Inside the body of <code>html</code> can be - in additionto all other kinds of tag functions - one or more calls to <code>form</code><p><pre><code>########################################################################(app) # Start session(action # Action handler (html 0 "Form" "lib.css" NIL # HTTP/HTML protocol (form NIL # Form (gui 'a '(+TextField) 10) # Text Field (gui '(+Button) "Print" # Button '(msg (val> (: home a))) ) ) ) )########################################################################</code></pre><p>Note that there is no longer a global variable like <code>*Text</code> tohold the contents of the input field. Instead, we gave a local, symbolic name'<code>a</code>' to a <code>+TextField</code> component<p><pre><code> (gui 'a '(+TextField) 10) # Text Field</code></pre><p>Other components can refer to it<p><pre><code> '(msg (val> (: home a)))</code></pre><p><code>(: home)</code> is always the form which contains this GUI component.So <code>(: home a)</code> evaluates to the component '<code>a</code>' in thecurrent form. As <code><a href="refM.html#msg">msg</a></code> prints itsargument to standard error, and the <code>val></code> method retrieves thecurrent contents of a component, we will see on the console the text typed intothe text field when we press the button.<p>An <code>action</code> without embedded <code>form</code>s - or a<code>form</code> without a surrounding <code>action</code> - does not make muchsense by itself. Inside <code>html</code> and <code>form</code>, however, callsto HTML functions (and any other Lisp functions, for that matter) can be freelymixed.<p>In general, a typical page may have the form<p><pre><code>(action # Action handler (html .. # HTTP/HTML protocol (<h1> ..) # HTML tags (form NIL # Form (<h3> ..) (gui ..) # GUI component(s) (gui ..) .. ) (<h2> ..) (form NIL # Another form (<h3> ..) (gui ..) # GUI component(s) .. ) (<br> ..) .. ) )</code></pre><h4><a name="guiFoo">The <code>gui</code> Function</a></h4><p>The most prominent function in a <code>form</code> body is <code>gui</code>.It is the workhorse of GUI construction.<p>Outside of a <code>form</code> body, <code>gui</code> is undefined.Otherwise, it takes an optional alias name, a list of classes, and additionalarguments as needed by the constructors of these classes. We saw this examplebefore<p><pre><code> (gui 'a '(+TextField) 10) # Text Field</code></pre>Here, '<code>a</code>' is an alias name for a component of type<code>(+TextField)</code>. The numeric argument <code>10</code> is passed to thetext field, specifying its width. See the chapter on <a href="#guiClasses">GUIClasses</a> for more examples.<p>During a GET request, <code>gui</code> is basically a frontend to<code>new</code>. It builds a component, stores it in the internal structures ofthe current form, and initializes it by sending the <code>init></code>message to the component. Finally, it sends it the <code>show></code>message, to produce HTML code and transmit it to the browser.<p>During a POST request, <code>gui</code> does not build any new components.Instead, the existing components are re-used. So <code>gui</code> does not havemuch more to do than sending the <code>show></code> message to a component.<h4><a name="ctlFlow">Control Flow</a></h4><p>HTTP has only two methods to change a browser window: GET and POST. We employthese two methods in a certain defined, specialized way:<p><ul><li>GET means, a <b>new page</b> is being constructed. It is used when a page isvisited for the first time, usually by entering an URL into the browser'saddress field, or by clicking on a link (which is often a <ahref="#menus">submenu item or tab</a>).<li>POST is always directed to the <b>same page</b>. It is triggered by a buttonpress, updates the corresponding form's data structures, and executes thatbutton's action code.</ul><p>A button's action code can do almost anything: Read and modify the contentsof input fields, communicate with the database, display alerts and dialogs, oreven fake the POST request to a GET, with the effect of showing a completelydifferent document (See <a href="#switching">Switching URLs</a>).<p>GET builds up all GUI components on the server. These components are objectswhich encapsulate state and behavior of the HTML page in the browser. Whenever abutton is pressed, the page is reloaded via a POST request. Then - before anyoutput is sent to the browser - the <code>action</code> function takes control.It performs error checks on all components, processes possible user input on theHTML page, and stores the values in correct format (text, number, date, objectetc.) in each component.<p>The state of a form is preserved over time. When the user returns to aprevious page with the browser's BACK button, that state is reactivated, and maybe POSTed again. (However, this does <i>not</i> work well with browsers which donot allow to revisit a previously posted page (e.g. MSIE), and also not whenreturning to an already disposed (closed) dialog)<p>The following silly example displays two text fields. If you enter some textinto the "Source" field, you can copy it in upper or lower case to the"Destination" field by pressing one of the buttons<p><pre><code>########################################################################(app)(action (html 0 "Case Conversion" "lib.css" NIL (form NIL (<grid> 2 "Source" (gui 'src '(+TextField) 30) "Destination" (gui 'dst '(+Lock +TextField) 30) ) (gui '(+JS +Button) "Upper Case" '(set> (: home dst) (uppc (val> (: home src))) ) ) (gui '(+JS +Button) "Lower Case" '(set> (: home dst) (lowc (val> (: home src))) ) ) ) ) )########################################################################</code></pre><p>The <code>+Lock</code> prefix class in the "Destination" field makes thatfield read-only. The only way to get some text into that field is by using oneof the buttons.<h4><a name="switching">Switching URLs</a></h4><p>Because an action code runs before <code>html</code> has a chance to outputan HTTP header, it can abort the current page and present something different tothe user. This might, of course, be another HTML page, but would not be veryinteresting as a normal link would suffice. Instead, it can cause the downloadof dynamically generated data.<p>The next example shows a text area and two buttons. Any text entered into thetext area is exported either as a text file via the first button, or a PDFdocument via the second button<p><pre><code>########################################################################(load "lib/ps.l")(app)(action (html 0 "Export" "lib.css" NIL (form NIL (gui '(+TextField) 30 8) (gui '(+Button) "Text" '(let Txt (tmp "export.txt") (out Txt (prinl (val> (: home gui 1)))) (url Txt) ) ) (gui '(+Button) "PDF" '(psOut NIL "foo" (a4) (indent 40 40) (down 60) (hline 3) (font (14 . "Times-Roman") (ps (val> (: home gui 1))) ) (hline 3) (page) ) ) ) ) )########################################################################</code></pre><p>(a text area is built when you supply two numeric arguments (columns androws) to a <code>+TextField</code> class)<p>The action code of the first button creates a temporary file (i.e. a filenamed "export.txt" in the current process's temporary space), prints the valueof the text area (this time we did not bother to give it a name, we simply referto it as the form's first gui list element) into that file, and then calls the<code>url</code> function with the file name.<p>The second button uses the PostScript library "lib/ps.l" to create atemporary file "foo.pdf". Here, the temporary file creation and the call to the<code>url</code> function is hidden in the internal mechanisms of<code>psOut</code>. The effect is that the browser receives a PDF document anddisplays it.<h4><a name="dialogs">Alerts and Dialogs</a></h4><p>Alerts and dialogs are not really what they used to be ;-)<p>They do not "pop up". In this framework, they are just a kind ofsimple-to-use, pre-fabricated form. They can be invoked by a button's actioncode, and appear always on the current page, immediately preceding the formwhich created them.<p>Let's look at an example which uses two alerts and a dialog. In thebeginning, it displays a simple form, with a locked text field, and two buttons<p><pre><code>########################################################################(app)(action (html 0 "Alerts and Dialogs" "lib.css" NIL (form NIL (gui '(+Init +Lock +TextField) "Initial Text" 20 "My Text") (gui '(+Button) "Alert" '(alert NIL "This is an alert " (okButton)) ) (gui '(+Button) "Dialog" '(dialog NIL (<br> "This is a dialog.") (<br> "You can change the text here " (gui '(+Init +TextField) (val> (: top 1 gui 1)) 20) ) (<br> "and then re-submit it to the form.") (gui '(+Button) "Re-Submit" '(alert NIL "Are you sure? " (yesButton '(prog (set> (: home top 2 gui 1) (val> (: home top 1 gui 1)) ) (dispose (: home top 1)) ) ) (noButton) ) ) (cancelButton) ) ) ) ) )########################################################################</code></pre><p>The <code>+Init</code> prefix class initializes the "My Text" field with thestring "Initial Text". As the field is locked, you cannot modify this valuedirectly.<p>The first button brings up an alert saying "This is an alert.". You candispose it by pressing "Ok".<p>The second button brings up a dialog with an editable text field, containinga copy of the value from the form's locked text field. You can modify thisvalue, and send it back to the form, if you press "Re-Submit" and answer "Yes"to the "Are you sure?" alert.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -