This time, we will use jQuery to set the submit button so that it cannot be pressed until the form is filled out and selected.
Share information and write as a memorandum of your own.
To prevent erroneous transmission and improve usability.
MacOS 10.15.7 ruby 2.6.5 Ruby on Rails 6.0.0
--JQuery has been installed. --Implemented a function to post multiple images.
The User table is "Poster", the Post table is "Post", the Image table is "Posted image", the Prefecture table is "Prefecture data", and the Category table is "Post category".
The Prefecture table and Category table utilize seed data.
Posts_controller.rb is in charge of the new post function.
posts_controller.rb
class PostsController < ApplicationController
def new
@post = Post.new
@post.build_spot
@post.images.build()
end
def create
@post = Post.new(post_params)
if @post.save
redirect_to root_path, notice: "Post has been completed"
else
flash.now[:alert] = "Please fill in the required fields"
@post.images.build()
render :new
end
end
...Some descriptions below are omitted
.
.
.
private
def post_params
params.require(:post).permit(:title, :content, :prefecture_id, :category_id, images_attributes: [:id, :image, :_destroy]).merge(user_id: current_user.id)
end
First, create the form in html.
erb:new.html.erb
<%= form_with(model: @post, local: true, multipart: true) do |form| %>
<ul class='formSpace'>
<li class="prefecture">
<label class="labelName" for="Prefecture">Prefecture:</label>
<%= form.collection_select :prefecture_id, Prefecture.all, :id, :name, {include_blank: 'Please select'}, {class: "prefecture__input", id: 'input01'} %>
</li>
<li class="category">
<label class="labelname" for="category">Category:</label>
<%= form.collection_select :category_id, Category.all, :id, :name, {include_blank: 'Please select'}, {class: "category__input", id: 'input02'} %>
</li>
<li class="title">
<label class="labelName" for="titleSpace">Title:</label>
<%= form.text_field :title, class: 'title__input', id: "input03", placeholder: "Please enter a title" %>
</lil
<li class='newImage'>
<label class="labelName" for="imageSpace">Photo:</label>
<div class="prevContent">
</div>
<div class="labelContent">
<label class="labelBox" for="post_images_attributes_0_image">
<div class="labelBox__text-visible">
Click to upload file (up to 5)
</div>
</label>
</div>
<div class="hiddenContent">
<%= form.fields_for :images do |i| %>
<%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_0_image", name: "post[images_attributes][0][image]", type: "file" %>
<%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_1_image", name: "post[images_attributes][1][image]", type: "file" %>
<%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_2_image", name: "post[images_attributes][2][image]", type: "file" %>
<%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_3_image", name: "post[images_attributes][3][image]", type: "file" %>
<%= i.file_field :image, class: "hiddenField", id: "post_images_attributes_4_image", name: "post[images_attributes][4][image]", type: "file" %>
<% end %>
</div>
</li>
<li class='content'>
<label class="labelName" for="contentSpace">Content:</label>
<%= form.text_area :content, class: 'content__input', id: "input05", placeholder: "Please enter a comment" %>
</li>
</ul>
<div class='send'>
<%# <%= form.submit "sending", class: 'send__btn', id: 'sending', value: "Post" %>
<input type='submit' id='sending' class='send__btn' value='Post'>
</div>
<% end %>
new.scss
.formSpace {
height: auto;
}
.labelName {
color: #000000;
}
//Prefectures================================================================
.prefecture {
height: auto;
width: auto;
margin-top: 1vh;
font-size: 1.5vh;
line-height: 1.5;
color: #fff;
&__input {
width: auto;
border: 1px solid #ccc;
background-color: #fff;
border-radius: 5px;
text-align: center;
color: #000000;
}
}
//Category ==============================================
.category {
height: auto;
width: auto;
margin-top: 1vh;
font-size: 1.5vh;
line-height: 1.5;
color: #fff;
&__input {
width: auto;
border: 1px solid #ccc;
background-color: #fff;
border-radius: 5px;
color: #000000;
}
}
//Title===================================================================
.title {
height: auto;
width: auto;
margin-top: 1vh;
font-size: 1.5vh;
line-height: 1.5;
color: #fff;
&__input {
width: 30vw;
border-radius: 5px;
border: 1px solid #ccc;
background-color: #fff;
color: #000000;
margin-left: 25px;
}
}
//Image======================================================================
.newImage {
display: block;
margin: 16px auto 0;
display: flex;
flex-wrap: wrap;
cursor: pointer;
}
.imageLabelName {
color: #fff;
margin-right: 25px;
}
.prevContent {
display: flex;
}
.previewBox {
height: 162px;
width: 112px;
margin: 0 15px 10px 0;
}
.upperBox {
height: 112px;
width: 100%;
img {
width: 112px;
height: 112px;
}
}
.lowerBox {
display: flex;
text-align: center;
}
.deleteBox {
color: #1e90ff;
width: 100%;
height: 50px;
line-height: 50px;
background: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.imageDeleteBtn {
background-color: #f5f5f5;
line-height: 4vh;
height: 4vh;
width: 60px;
}
.imageDeleteBtn:hover {
color: rgba($color: #1e90ff, $alpha: 0.7);
}
//Post click area CSS
.labelContent {
margin-bottom: 10px;
width: 620px;
.labelBox {
display: block;
border: 1px dashed #ccc;
position: relative;
background: #f5f5f5;
width: 100%;
height: 162px;
cursor: pointer;
&__text-visible {
position: absolute;
top: 50%;
left: 16px;
right: 16px;
text-align: center;
font-size: 14px;
line-height: 1.5;
font-weight: bold;
-webkit-transform: translate(0, -50%);
transform: translate(0, -50%);
pointer-events: none;
white-space: pre-wrap;
word-wrap: break-word;
}
}
}
//file_field css
.hiddenContent {
.hiddenField {
display: none;
}
.hidden-checkbox {
display: none;
}
}
//comment====================================================================
.content {
display: flex;
height: auto;
width: auto;
margin-top: 5px;
line-height: 1.5;
font-size: 1.5vh;
&__input {
height: 15vh;
width: 40vw;
border-radius: 5px;
color: #000000;
border: 1px solid #ccc;
background-color: #fff;
margin-left: 0px;
padding: 1vh;
}
}
//SEND button=========================================================================
.send {
display: flex;
justify-content: center;
&__btn {
height: 5vh;
width: 25vw;
margin: 50px 0;
border-radius: 20px;
background-color: #87cefa;
border: none;
box-shadow: 0 0 8px gray;
color: #ffffff;
line-height: 1.5;
font-size: 2vh;
font-weight: bold;
-webkit-transition: all 0.3s ease;
-moz-transition: all 0.3s ease;
-o-transition: all 0.3s ease;
transition: all 0.3s ease;
}
:hover {
background-color: #00bfff;
}
.send__btn[disabled] {
background-color: #ddd;
cursor: not-allowed;
}
}
If you do so far, it will look like the following.
This time, in order to manage the state whether the form is entered or selected in JavaScript
<%= form.collection_select :prefecture_id, Prefecture.all, :id, :name, {include_blank: 'Please select'}, {class: "prefecture__input", id: 'input01'} %>
The id is specified in the form of id:'input01'
.
In this way, id is specified for each form, but this time it is impossible to reuse the id with the same name.
The ids are assigned in order in the form of id ='input02'
, id ='input03'
...
(It is possible to unify the class name and reuse it, but this time it will be omitted.)
Regarding SCSS, it is described that the color of the button is changed according to the enabled / disabled state of the button (class: send__btn). Button state management is managed using a value called distabled, which will be described later. If it is invalid, a value called distabled will be given to the element, so scss
.send__btn[disabled] {
background-color: #ddd;
cursor: not-allowed;
}
If the button is disabled, the color is grayed out and the cursor is disabled.
After that, we will judge whether the form is entered / selected in the js file and describe the process to enable / disable the submit button.
This time, I will describe it in a file called submit.js.
submit.js
//Prevent the submit button from being pressed until the form is filled in and selected=============================================
$(function() {
//Disable the submit button first
$('#sending').prop("disabled", true);
//When operating the input field where "input" is set in id
$("[id^= input],#post_images_attributes_0_image").change(function () {
//In order to define whether the input field is empty, the state of the contents of the form is managed using a variable called send.
let send = true;
//id=input~Check the input fields specified as&Check the image (image with index number 0)
$("[id^= input],#post_images_attributes_0_image").each(function(index) {
//Check the contents (values) of the form in order, and if the value of the form is empty, send=false
if ($("[id^= input],#post_images_attributes_0_image").eq(index).val() === "") {
send = false;
}
});
//If all the forms are filled(send =If true)
if (send) {
//Enable submit button
$('#sending').prop("disabled", false);
}
//If even one form is empty(send =If false)
else {
//Disable submit button
$('#sending').prop("disabled", true);
}
});
});
Send button first
<input type='submit' id='sending' class='send__btn' value='Post'>
On the other hand, the button is disabled as prop (disabled, false)
.
The prop method has the __ role of setting a value to the specified attribute.
distabled is an attribute __ that can invalidate the specified HTML element.
By using it in combination with the prop method
prop (‘disabled’, true) ”・ ・ ・ Disable element
prop (‘disabled’, false) ”・ ・ ・ Enable element
You can use it like this.
reference: prop method ・ ・ ・ http://js.studio-kingdom.com/jquery/attributes/prop distabled ・ ・ ・ https://persol-tech-s.co.jp/hatalabo/it_engineer/463.html#disabled
next,
$("[id^= input],#post_images_attributes_0_image").change(function ()
It is described as.
The description is "The event fires when the values of[id ^ = input]
and # post_images_attributes_0_image
change.
What I would like you to pay attention to
[id^= input]
It is the part of. This adopts the specification method using the attribute of jQuery. There are roughly four ways to specify.
--Starting match
For "prefix match", just add "^" like "attribute ^ = attribute name", and you can get all the elements that match the character string at the beginning of the attribute name.
In this case, by setting [id ^ = input]
, the element with id = "input01", id = "input02, ... id =" input05, that is, the element named input in the id name You can get all of them.
For the specification method using jQuery attributes, I referred to this article. If you want to know more than the prefix designation, please have a look.
continue,
let send = true;
Is described in order to manage the state of the form using a variable called send in order to judge whether the input field is empty. If true, it means that the form is completely filled.
$("[id^= input],#post_images_attributes_0_image").each(function(index) {
//Check the contents (values) of the form in order, and if the value of the form is empty, send=false
if ($("[id^= input],#post_images_attributes_0_image").eq(index).val() === "") {
send = false;
}
});
For, use the each method to extract the elements whose id is named input according to the number of elements. When retrieving, it is necessary to define a callback function in the argument of each method, so specify "function (index)". By doing this, you can get the index number and assign the index number to each extracted element.
In this case, as an image
0 : id="input01"Elements of
1 : id="input02"Elements of
2 : id="input03"Elements of
3 : id="input04"Elements of
4 : id="input05"Elements of
I think it will look like this.
In addition, #post_images_attributes_0_image
is also added to the target object. Honestly,
When posting multiple images, I can not set and specify a good id, so I added it here.
(I would appreciate it if you could tell me if there is another way to do it!)
After fetching with the index number with each method
if ($("[id^= input],#post_images_attributes_0_image").eq(index).val() === "") {
send = false;
}
Move on to the processing of.
Here, we are verifying whether the contents of the form for each extracted element are empty.
The eq method
is used to verify each one.
The eq method filters the currently matching elements by index number.
(eq method reference site)
The element named input in id is an image in which the index number is entered in the argument of the eq method in order because the index numbers 0 to 4 are assigned in each method. (Example: eq (0), eq (1) ... eq (4))
Use the val method to get the value of the form (val method reference site)
~~ ===" "
means "~~ is empty".
It verifies the extracted elements one by one and returns send = false
if there is even one element whose form value is empty.
Last
//If all the forms are filled(send =If true)
if (send) {
//Enable submit button
$('#sending').prop("disabled", false);
}
//If even one form is empty(send =If false)
else {
//Disable submit button
$('#sending').prop("disabled", true);
}
For the part of
In the case of if (send)
(meaning if send == true), that is, when the form is completely filled, $ ('# sending'). Prop ("disabled ", false);
The send button is enabled and ready to be pressed.
In the case of else
(send == false), that is, if even one form is empty, the submit button is disabled in the form of$ ('# sending'). Pro ("disabled", true);
To make it impossible to press.
That's it.
Since I am a beginner, there are still many parts that I do not understand, and honestly, I think that there are many predictions of improvement regarding this implementation, so I would appreciate it if you could tell me if there is a better implementation method. Also, if you read this article, I would be very happy if you could also get LGTM. Thank you for your cooperation.