-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Form filling in and updating #37
Comments
I can build a SignUp-Login form on W3 Schools and get the HTML, with no behavior. HTML FormsFirst name:Last name: Password: If you click the "Submit" button, the form data will be sent to a page called "/action_page.php". What I need to understand first is how to convert that HTML to a Component. Once I understand that then I need to get the data committed to an ActiveRecord instance. I don't currently grok how components map to AR fields, and I don't know if the form is a single FORM component, or if each field is a separate component. I am confused. |
Lets break it down:
I'll add a comment for each these below. |
Mapping an HTML form and tags to a componentHere is a typical HTML login form:
<form action="action_page.php" style="border:1px solid #ccc">
<div class="container">
<h1>Sign Up</h1>
<p>Please fill in this form to create an account.</p>
<hr>
<label for="email"><b>Email</b></label>
<input type="text" placeholder="Enter Email" name="email" required>
<label for="psw"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="psw" required>
<label for="psw-repeat"><b>Repeat Password</b></label>
<input type="password" placeholder="Repeat Password" name="psw-repeat" required>
<label>
<input type="checkbox" checked="checked" name="remember" style="margin-bottom:15px"> Remember me
</label>
<p>By creating an account you agree to our <a href="#" style="color:dodgerblue">Terms & Privacy</a>.</p>
<div class="clearfix">
<button type="button" class="cancelbtn">Cancel</button>
<button type="submit" class="signupbtn">Sign Up</button>
</div>
</div>
</form> To begin our conversion we need some place to put our code. In Hyperstack you break the UI up into components. You could of course build the entire UI as one giant component, but that would be hard to maintain, so we want to break up our app into smaller chunks of code that represent a smaller, testable portion of the UI. The above form makes a perfect sized component and we will call it class LoginForm < HyperComponent
render do
# our form goes here
end
end Each component is defined as a Ruby class that inherits from the applications base component class called Converting our form to Ruby syntax is straight forward. HTML tags become method calls, and are written ALLCAPS. Attributes to the HTML tags are sent as a hash data. Any nested tags are placed in the methods block. For example: <div class="clearfix">
<button type="button" class="cancelbtn">Cancel</button>
<button type="submit" class="signupbtn">Sign Up</button>
</div> becomes DIV(class: :clearfix) do
BUTTON(type: :button, class: :cancelbtn) { 'Cancel' }
BUTTON(type: :submit, class: :signupbtn) { 'Sign Up' }
end
Styles are translated to a hash, and when text is mixed with tags, the text needs to be wrapped in span tags. So <p>
By creating an account you agree to our
<a href="#" style="color:dodgerblue">Terms & Privacy</a>.
</p> becomes P do
SPAN { 'By creating an account you agree to our ' }
A(href: '#', style: {color: :dogerblue}) { 'Terms & Privacy' }
SPAN { '.' }
end Okay with this in mind we can convert the form to a complete Hyperstack component: class LoginForm < HyperComponent
render do
FORM(action: "action_page.php", style: {border: 1 solid: '#ccc'}) do
DIV class: :container
H1 { 'Sign Up' }
P { 'Please fill in this form to create an account.' }
HR() # if there are no params and no block you must include empty ()
LABEL(for: :email) { B { 'Email' } }
# the following is the same as saying INPUT(required: true, ...)
INPUT(:required, type: :text, placeholder: "Enter Email", name: email)
LABEL(for: :psw) { B { 'Password' } }
INPUT(:required, type: :password, placeholder: "Enter Password", name: :psw)
LABEL(for: :psw_repeat) { B { 'Repeat Password' } }
INPUT(:required, type: :password, placeholder: "Repeat Password", name: :psw_repeat)
LABEL do
# note that the style property margin-bottom becomes marginBottom
INPUT(type: :checkbox, checked: :checked, name: :remember, style: {marginBottom: 15})
SPAN { 'Remember me' }
end
P do
SPAN { 'By creating an account you agree to our ' }
A(href: "#", style: {color: :dodgerblue}) { 'Terms & Privacy' }
SPAN { '.' }
end
DIV(class: :clearfix) do
BUTTON(type: :button, class: :cancelbtn) { 'Cancel' }
BUTTON(type: :submit, class: :signupbtn) { 'Sign Up' }
end
end
end
end |
Thanks a bunch, Mitch. I will study and apply it.
…On Sat, 3 Aug 2019 at 10:12, Mitch VanDuyn ***@***.***> wrote:
Mapping an HTML form and tags to a component
Here is a typical HTML login form:
stolen shamelessly from
https://www.w3schools.com/howto/howto_css_signup_form.asp
<form action="action_page.php" style="border:1px solid #ccc">
<div class="container">
<h1>Sign Up</h1>
<p>Please fill in this form to create an account.</p>
<hr>
<label for="email"><b>Email</b></label>
<input type="text" placeholder="Enter Email" name="email" required>
<label for="psw"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="psw" required>
<label for="psw-repeat"><b>Repeat Password</b></label>
<input type="password" placeholder="Repeat Password" name="psw-repeat" required>
<label>
<input type="checkbox" checked="checked" name="remember" style="margin-bottom:15px"> Remember me
</label>
<p>By creating an account you agree to our <a href="#" style="color:dodgerblue">Terms & Privacy</a>.</p>
<div class="clearfix">
<button type="button" class="cancelbtn">Cancel</button>
<button type="submit" class="signupbtn">Sign Up</button>
</div>
</div>
</form>
To begin our conversion we need some place to put our code. In Hyperstack
you break the UI up into components. You could of course build the entire
UI as one giant component, but that would be hard to maintain, so we want
to break up our app into smaller chunks of code that represent a smaller,
testable portion of the UI. The above form makes a perfect sized component
and we will call it LoginForm and it will look like this:
class LoginForm < HyperComponent
render do
# our form goes here
endend
Each component is defined as a Ruby class that inherits from the
applications base component class called HyperComponent. A component
class (like any class) defines how *instances* of the class will behave.
To actually display a component you need to create an instance by
*mounting* it someplace on the UI. More on that later.
Converting our form to Ruby syntax is straight forward. HTML tags become
method calls, and are written ALLCAPS. Attributes to the HTML tags are sent
as a hash data. Any nested tags are placed in the methods block. For
example:
<div class="clearfix">
<button type="button" class="cancelbtn">Cancel</button>
<button type="submit" class="signupbtn">Sign Up</button>
</div>
becomes
DIV(class: :clearfix) do
BUTTON(type: :button, class: :cancelbtn) { 'Cancel' }
BUTTON(type: :submit, class: :signupbtn) { 'Sign Up' }end
Note that Ruby allows us to define blocks either as do...end or { ... }.
Ruby style convention suggests that { ... } be used for single line
blocks, and do...end be used for multiple line blocks, but otherwise they
are equivalent.
Also note that in Hyperstack strings and symbols are also equivalent, so
that writing class: :clearfix is the same as class: 'clearfix'
Styles are translated to a hash, and when text is mixed with tags, the
text needs to be wrapped in span tags. So
<p>
By creating an account you agree to our
<a href="#" style="color:dodgerblue">Terms & Privacy</a>.
</p>
becomes
P do
SPAN { 'By creating an account you agree to our ' }
A(href: '#', style: {color: :dogerblue}) { 'Terms & Privacy' }
SPAN { '.' }end
Okay with this in mind we can convert the form to a complete Hyperstack
component:
class LoginForm < HyperComponent
render do
FORM(action: "action_page.php", style: {border: 1 solid: '#ccc'}) do
DIV class: :container
H1 { 'Sign Up' }
P { 'Please fill in this form to create an account.' }
HR() # if there are no params and no block you must include empty ()
LABEL(for: :email) { B { 'Email' } }
# the following is the same as saying INPUT(required: true, ...)
INPUT(:required, type: :text, placeholder: "Enter Email", name: email)
LABEL(for: :psw) { B { 'Password' } }
INPUT(:required, type: :password, placeholder: "Enter Password", name: :psw)
LABEL(for: :psw_repeat) { B { 'Repeat Password' } }
INPUT(:required, type: :password, placeholder: "Repeat Password", name: :psw_repeat)
LABEL do
# note that the style property margin-bottom becomes marginBottom
INPUT(type: :checkbox, checked: :checked, name: :remember, style: {marginBottom: 15})
SPAN { 'Remember me' }
end
P do
SPAN { 'By creating an account you agree to our ' }
A(href: "#", style: {color: :dodgerblue}) { 'Terms & Privacy' }
SPAN { '.' }
end
DIV(class: :clearfix) do
BUTTON(type: :button, class: :cancelbtn) { 'Cancel' }
BUTTON(type: :submit, class: :signupbtn) { 'Sign Up' }
end
end
endend
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#37?email_source=notifications&email_token=AAAJXSRRGBLTCO4DM723HWDQCW37FA5CNFSM4IJAB4Z2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD3PSJMI#issuecomment-517940401>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAJXSWPUW7L6MGGJD2ENUTQCW37FANCNFSM4IJAB4ZQ>
.
--
Regards,
Ken
Seek wisdom through disbelief
|
Mounting our componentThe As this will represent our top level app we will call it simply class App < HyperComponent
render do
if @show_signup
DIV(class: :modal) do
SPAN(class: close, title: "Close Modal") { '×' }
.on(:click) { toggle :show_signup }
LoginForm()
end
else
H2 { 'Modal Signup Form' }
BUTTON { 'Sign Up' }
.on(:click) { toggle :show_signup }
end
end
end If you look at https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_signup_form_modal you will see that our implementation of the "wrapper" is different. In the w3 schools example they directly use the onclick event to modify the style of the modal div making it show and hide. While this is possible in Hyperstack we would much prefer to use states to control how things look. So we have a single state variable Depending on the state we either show the "Sign Up" button or show the The final step here is to "mount" this top level App component in a test framework. The easiest way to do this is to create a new Hyperstack rails app following these instructions: https://github.com/hyperstack-org/hyperstack/blob/edge/install/readme.md Once you have built a new blank app, replace the contents of Finally copy the following style sheet (from the W3 example) to the body {font-family: Arial, Helvetica, sans-serif;}
* {box-sizing: border-box;}
/* Full-width input fields */
input[type=text], input[type=password] {
width: 100%;
padding: 15px;
margin: 5px 0 22px 0;
display: inline-block;
border: none;
background: #f1f1f1;
}
/* Add a background color when the inputs get focus */
input[type=text]:focus, input[type=password]:focus {
background-color: #ddd;
outline: none;
}
/* Set a style for all buttons */
button {
background-color: #4CAF50;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
opacity: 0.9;
}
button:hover {
opacity:1;
}
/* Extra styles for the cancel button */
.cancelbtn {
padding: 14px 20px;
background-color: #f44336;
}
/* Float cancel and signup buttons and add an equal width */
.cancelbtn, .signupbtn {
float: left;
width: 50%;
}
/* Add padding to container elements */
.container {
padding: 16px;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: #474e5d;
padding-top: 50px;
}
/* Modal Content/Box */
.modal-content {
background-color: #fefefe;
margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
}
/* Style the horizontal ruler */
hr {
border: 1px solid #f1f1f1;
margin-bottom: 25px;
}
/* The Close Button (x) */
.close {
position: absolute;
right: 35px;
top: 15px;
font-size: 40px;
font-weight: bold;
color: #f1f1f1;
}
.close:hover,
.close:focus {
color: #f44336;
cursor: pointer;
}
/* Clear floats */
.clearfix::after {
content: "";
clear: both;
display: table;
}
/* Change styles for cancel button and signup button on extra small screens */
@media screen and (max-width: 300px) {
.cancelbtn, .signupbtn {
width: 100%;
}
} Your UI of your signup form is now complete. |
I'm interested in: I'm trying to build a page where multiple users can update ActiveRecord objects trough form inputs in real time. So no "save" button. Each change to the form is saved and synced. When multiple users mutate the same ActiveRecord object at the same time it should keep the data synced across users, overwriting local data as needed. I have also been thinking about how to implement some sort of locking feature. The idea is that if one user has his cursor in a textarea, this texterea gets disabled (grayed out) on other clients. Preferably with some sort of indication which user has a lock on it. |
DRAFT DRAFT DRAFT - not complete What is the equivalent to submitting a form but in an SPA? - part 1 - a login formThis is where things get a bit tricky. The above code "works" - sort of. When you click to signup nothing happens, because the action on form submit is to post to Before going further let's talk about what a form submit does: It takes all the input fields inside the form and packages them up as a set of parameters that are part of the post request. So when the form is submitted all the input fields arrive at the server neatly packaged. If you are using rails, then Rails takes care of unpacking the fields and assigning them to the controllers The Rails controller then processes the request, and typically would redirect to a new page where the new user is logged in. So to get this work we could simply add a signup end point to our rails app by adding a controller, and a route to that controller, and then change the name This is not a bad way to do it, especially if you have an existing Rails app that you are converting to Hyperstack. However Hyperstack provides an alternative mechanism that avoids a lot of the excess baggage called Operations. We can define a signup operation like this: class SignupOp < Hyperstack::ControllerOp
param :email
inbound :password
# here you can validate the params
step { User.create(email: params.email, password: params.password).id }
end |
My first take on this: define the terms 'Operation' and 'step'. In what
context?
These are very generic terms and are commonly overloaded. That can be a
problem.
…On Tue, 6 Aug 2019 at 08:26, Mitch VanDuyn ***@***.***> wrote:
What is the equivalent to submitting a form but in an SPA?
This is where things get a bit tricky. The above code "works" - sort of.
When you click to signup nothing happens, because the action on form submit
is to post to action_page.php which does not exist in our app.
Before going further let's talk about what a form submit does: It takes
all the input fields inside the form and packages them up as a set of
parameters that are part of the post request. So when the form is
*submitted* all the input fields arrive at the server neatly packaged. If
you are using rails, then Rails takes care of unpacking the fields and
assigning them to the controllers param object.
The Rails controller then processes the request, and typically would
*redirect* to a new page where the new user is logged in.
So to get this work we could simply add a signup *end point* to our rails
app by adding a controller, and a route to that controller, and then change
the name action_page.php to whatever our url was.
This is *not* a bad way to do it, especially if you have an existing
Rails app that you are converting to Hyperstack. However Hyperstack
provides an alternative mechanism that avoids a lot of the excess baggage
called Operations. We can define a signup operation like this:
class SignupOp < Hyperstack::ControllerOp
param :email
inbound :password
# here you can validate the params
step { User.create(email: params.email, password: params.password).id } end
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#37?email_source=notifications&email_token=AAAJXSQ2ERRW6IU5HDULQKLQDGJ3VA5CNFSM4IJAB4Z2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD3VQR6I#issuecomment-518719737>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAJXSXLBI3B2YOMJBFLFULQDGJ3VANCNFSM4IJAB4ZQ>
.
--
Regards,
Ken
Seek wisdom through disbelief
|
No description provided.
The text was updated successfully, but these errors were encountered: