Example 3 – Jurassic Park

Example 3

What is this?

This app demonstrates the use of a custom form component in a basic log in application. The app contains three views: a login form, a registration form, and a user profile form that displays the information entered during registration. These forms are built using a custom form component, which is defined at the end of the program. The form component allows for the concise definition of forms by defining common behavior like form submission and resetting.

You can play with this example in your browser here.

Application Set Up

The app contains the current page, as well as the current user. Initially, though, there is no user, so we just need to specify the current page.

bind @browser   [#div #app class: "app-wrapper" page: "login" children:      [#div sort: 0 text: "Jurassic Park System Security Interface"]] 

Pages

Log In Form

The log in form contains two input boxes, one for the username and another for a password. We must explicitly sort the fields (using a sort attribute) to display them in a specific order.

search @browser   app = [#app page: "login"]    bind @browser   app.children +=     [#div sort: 1 children:       [#form name: "Login" sections:          [#section fields:            [#field sort: 2 type: "password" field: "password"]           [#field sort: 1 type: "input" field: "username"]]]       [#button #signup text: "Sign Up" ]] 

A successful login is one where the username and password entered in the login form match some user/password combination stored in the system. For simplicity, passwords are stored as plain text, so we just need to search for a #user with a matching username and password. If one is found, we set it as the user attribute in the #app record.

search @browser @session   [#form name: "Login" submission: [username password]]   user = [#user username password]   app = [#app]      commit @browser   app.page := "profile"   app.user := user 

If the user enters a login that does not match a user, then display a message indicating that the login failed.

search @browser @session     form = [#form name: "Login" submission: [username password]]   form-message = [#form-message form]      not([#user username password])  commit @browser   form-message.text := "** Login Failed **" 

Clicking the sign up button changes the page to the sign up page

search @browser @event   [#click element: [#signup]]    app = [#app]    commit @browser   app.page := "signup" 

Sign Up Form

The user registration page requests the name, department, a username and password.

search @browser   app = [#app page: "signup"]    bind @browser   app.children +=    [#div children:     [#form name: "Sign Up" options: [reset: true] sections:        [#section name: "User Info" fields:          [#field sort: 1 type: "input" field: "full-name"]         [#field sort: 2 type: "input" field: "department"]]       [#section name: "Account Info" fields:          [#field sort: 1 type: "input" field: "username"]         [#field sort: 2 type: "password" field: "password"]         [#field sort: 3 type: "password" field: "confirm-password"]]]     [#button #login text: "Log In" ]] 

We need to create a #user from the submission of the registration form. This will only work if every field has an entry, and the two password fields match.

search @browser   [#form name: "Sign Up" submission: [username password confirm-password full-name department]]   // The password and the confirmation must match   password = confirm-password   app = [#app]  commit @browser   app.page := "login"    commit   [#user username password name: full-name department] 

Clicking the login button changes the page back to the login screen

search @browser @event   [#click element: [#login]]    app = [#app]    commit @browser   app.page := "login" 

Profile Page

The profile page displays information relating to the current user profile. It is accessed after a successful submission of the login form, which creates a user attribute in the #app.

search @browser @session   app = [#app page: "profile" user]    bind @browser   app.children +=    [#div class: "profile" children:     [#div text: "Welcome !"]     [#div text: "Name: "]     [#div text: "Department: "]     [#button #logout text: "Log Out"]] 

Clicking logout returns to the login page, and removes the user from #app.

search @browser @event   [#click element: [#logout]]    app = [#app]    commit @browser   app.user := none   app.page := "login" 

An easter egg

We can specify custom behavior by special casing search conditions and adding new side effects. In this block, we hijack the login process when the username is “dnedry”. Instead of displaying the typical “login failed” message, we give the user a surprise.

search @browser @session   app = [#app]      [#form name: "Login" submission: [username password]]   username = "dnedry"   not([#user username password])    commit @browser   app.children := none   app.class -= "app-wrapper"   app.class += "uh-uh-uh" 

Clicking anywhere returns to the login screen

search @browser @session @event   [#click]   app = [#app class: "uh-uh-uh"]    commit @browser   app.class += "app-wrapper"   app.class -= "uh-uh-uh"   app.children += [#div sort: 0 text: "Jurassic Park System Security Interface"]   app.page := "login" 

A Custom Form Element

Forms have a title and one or more sections. Each section has an optional name, and contains one or more fields. Each field additionally has the input type of that field (input, radio button, drop down list, etc.).

A form starts as a #form record.

search @browser   form = [#form]    bind @browser   form += #div   form.sort := 0   form.class := "form"   form.submission := [] 

Display the form name

search @browser   form = [#form]    bind @browser   form.children += [#div children:     [#h1 class: "form-name" sort: 0 text: form.name]     [#div #form-message form sort: 1 class: "form-message"]]  

Display each section. To properly display sections, we need to add them to the children of the form.

search @browser   form = [#form sections]      bind @browser   form.children += [#div form section: sections class: "form-section" sort: 1]   sections.form := form 

If the section has a name, display it

search @browser   section-display = [#div section]    bind @browser   section-display.children += [#h2 class: "section-name" text: section.name, sort: 0] 

Display the fields in each section. As we did with sections, to display fields we need to move them over to the children of the section display.

search @browser   section-display = [#div section]   field = section.fields    bind @browser   section-display.children +=      [#div field sort: field.sort form: section.form sort: 1 children:        [tag: field.type, placeholder: field.field, class: field.type]] 

Display a submit button at the end of the form

search @browser   form = [#form]    bind @browser   form.children += [#button #submit form sort: 100 text: "Submit"] 

Forms can have an optional reset button, which clears the fields in the form

search @browser   form = [#form options: [reset: true]]    bind @browser   form.children += [#button #reset form sort: 101 text: "Reset"] 

Clicking the reset button clears each field in the form

search @event @browser   [#click element: [#reset]]   field-container = [#div field form]      commit @browser   field-container.children.value := none 

Save Input to Records

Form values are saved as a #submission when the submit button is clicked. This submission has a lifetime equal to that of the #click, so a submission must be committed to a record by the user. This allows the user to implement custom handling logic.

One thing this form component does not handle is form validation. In a future example, we will demonstrate how certain fields can be required, while others are optional. This form will submit any fields that are filled, while omitting any that are not. If a form is handled expecting fields that aren’t submitted, then the submission will simply be ignored.

search @browser @event @session   click = [#click element: [#submit form]]   form = [#form submission]   field-container = [#div field form]   value = field-container.children.value   key = field.field   [#time timestamp: time]    bind @browser   // When used in a bind or commit. lookup[] creates a record with the give attribute and value. We use it here to create a record with the attribute as the field name.    //For example, a login form with "username" and "password" fields could be accessed as [#form name: "Login" submission: [username password]]   lookup[record: submission, attribute: key, value]   field-container.children.value := none 

Custom Input Types

Render password fields

search @browser   password = [#password]    bind @browser   password += #input   password.type := "password"   password.class := "password" 

Render custom button styles

search @browser   button = [#button]    bind @browser   button.class += "button" 

Appendix

Test Data

commit   [#user username: "jhammond" name: "John Hammond" department: "Executive" password: "password"]   [#user username: "dnedry" name: "Dennis Nedry" department: "Engineering" password: "Mr. Goodbytes"]   [#user username: "hwu" name: "Henry Wu" department: "Genetics" password: "slartibartfast"] 

Styles

{there is currently a bug that causes the first CSS block in an Eve program to be disregarded, so for a good time, leave this here} 
.application-container {  background-color: #000;   color: green;  font-family: monospace; }  @media (min-width: 1800px) {   .app-wrapper {     background-image: url(http://i.imgur.com/BBPkd29.gif);     background-repeat: no-repeat;     width: 800;     height: 658px;     background-size: 100%;     padding: 180px;     padding-top: 130px;   } }  .profile {  border: 1px solid green;  padding: 10px;  font-size:18px;  border-radius: 5px; }  .profile div {  padding: 10px;  }    .form-section {  border: 1px solid green;   border-radius: 5px;  padding: 10px;  margin: 10px; }  .form {   border: 1px solid green;    border-radius: 5px;   padding: 10px;   margin: 10px;   color: green; }  .input {  background-color: #000;  border-radius: 5px;  border: 1px solid green;  padding: 5px;  margin: 5px;  font-family: monospace;  color: green;  outline: none; }  .password {  background-color: #000;  border-radius: 5px;  border: 1px solid green;  padding: 5px;  margin: 5px;  font-family: monospace;  outline: none;  color: green; }  .button {   background-color: #000;   color: green;   border-radius: 5px;   border: 1px solid green;   padding: 5px;   margin: 5px;   cursor: pointer; }  .form-message {  color: green;  }  .form-name {   margin: 0px; }  .section-name {    margin: 0px; }  .uh-uh-uh {  width: 320px;  height: 520px;  background-image: url(http://i.imgur.com/yz53s4N.gif);   background-color: #FFF; } 

You may also like...