[Rails] [jQuery] Prevents the submit button from being pressed until the form is filled out and selected.

Introduction

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.

Complete image

submitButtonJs.gif

Purpose of writing the article

Share information and write as a memorandum of your own.

Purpose of introduction

To prevent erroneous transmission and improve usability.

environment

MacOS 10.15.7 ruby 2.6.5 Ruby on Rails 6.0.0

Prerequisites

--JQuery has been installed. --Implemented a function to post multiple images.

Article writer status

table

Untitled Diagram-ページ2 (1).png

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.

controller

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

Let's work now.

①-1 Create new.html.erb

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 %>

Then write scss.

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. newpageform1.png

point

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.

①-2 Describe the process in submit.js

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);
      }
  });
});

point

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.

①-3 completed

That's it.

submitButtonJs2.gif

Finally

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.

Recommended Posts

[Rails] [jQuery] Prevents the submit button from being pressed until the form is filled out and selected.
Rails API prevents booleans from being arbitrarily cast and passed validation