If you think in your heart, "Replace the product image"! At that time, the action is over! ~ part4 ~

Premise

--Use ruby on rails 6.0.0. --User functions are assumed to be introduced by devise. --All view files are in haml format. ――By the way, I'm using MacBook Air (Retina, 13-inch, 2020).

Introduction

This is the synopsis of the last time (part3) . I implemented such a thing and such a thing on the new registration screen using jQuery.

The current state is that when you select an image, a new form appears, and when you press delete, it disappears. At first glance, it looks like it's done, but since these are all actions by js, they disappear on the reloaded edit screen.

So, in this part, it will be work such as making a new form appear on the edit screen.

By the way, the introduction and procedure are described in detail in <a href="https://qiita.com/silvercrow222/items/55aeb5d13ec2bde305db""> part1 , so please take a look if you are interested.

let's try it

There are three main things to do.

--Display a new form on the edit screen. --You can also delete the already registered information with the delete button. --Do not cover the unique index of the image form.

Something like this. I'll do it right away, but I need to add a little to the controller before I can play with the view file, so let's start from there.

app/controllers/products_controller.rb


#~abridgement~
  private
  def product_params
    params.require(:product).permit(:name, images_attributes: [:src, :_destroy, :id]).merge(user_id: current_user.id)
  end
#~abridgement~
end

I added a little description to the strong parameter. This _destroy is a key that deletes the information of the associated child model. It's an unfamiliar shape, but it's a decent key, so don't worry.

Now let's write the view file.

haml:app/views/products/_form.html.haml


= form_with model: @product, local: true do |f|
  = f.text_field :name, placeholder: 'name'
  #image-box
    // ~abridgement~
    - if @product.persisted?
      .group{ data: { index: @product.images.count } }
        = file_field_tag :src, name: "product[images_attributes][#{@product.images.count}][src]", class: 'file'
        .remove
Delete
  = f.submit 'SEND'

I added the @ product.persisted? Part. It looks difficult, but all I'm doing is converting the form part I added in jQuery last time to haml format. I'm trying to display a new form.

This is persisted ?, but it will determine if the instance you used is saved. The point is whether it is new or already registered information. Remember, it's very convenient.

I was able to display it, but the current delete button cannot delete the information already registered in the database. So you need to be able to delete it using the _destroy key you just added.

haml:app/views/products/_form.html.haml


= form_with model: @product, local: true do |f|
  = f.text_field :name, placeholder: 'name'
  #image-box
    = f.fields_for :images do |i|
      // ~abridgement~
      - if @product.persisted?
        = i.check_box :_destroy, data: { index: i.index }, class: 'hidden'
  - if @product.persisted?
    // ~abridgement~
  = f.submit 'SEND'

Apart from the previous sentence, I also added a sentence with persisted? Inside the image-box. If you check the checkbox with the _destroy key, the corresponding record will be deleted from the database. I won't elaborate on why this happens (because I don't fully understand it ...), but I think it's a good idea to remember how to write it like this.

Now, the mechanism itself is completed. However, it is not good that there is a check box separate from the delete button, so we will link these.

app/assets/javascripts/product.js


$(function() {
  // ~abridgement~
  $('.hidden').hide();
  $('#image-box').on('click', '.remove', function() {
    //Get the unique index assigned to the form.
    const targetIndex = $(this).parent().data('index')
    //Get the check box corresponding to the obtained index.
    const hiddenCheck = $(`input[data-index="${targetIndex}"].hidden`);
    //If there is a check box, check it.
    if (hiddenCheck) hiddenCheck.prop('checked', true);
    ~abridgement~
  });
});

Added processing to the click event of image-box. I also wrote a line-by-line commentary. It's easy to do, so if you know the basics of jQuery, it's okay.

You can also check by pressing the delete button, so let's hide the check box. This time I used js .hide (), but you can set display: none in css.

Then, finally, the index cover is prevented and the implementation is completed.

app/assets/javascripts/product.js


$(function() {
  // ~abridgement~
  let fileIndex = [1,2,3,4,5]
  lastIndex = $('.group:last').data('index');
  fileIndex.splice(0, lastIndex);
  // ~abridgement~
});

Let's add a description to the definition part of fileIndex. The idea is to get the last index currently in use and replace the fileindex value with that value.

By the way, regarding .splice, this is an excellent one that allows you to count as many elements as you like from the specified elements and then add elements as well.

This time, all the values after the number specified by the first argument are removed and the value specified by the second argument is inserted. Since there are many things that can be done, there are some complicated parts in writing, so please take a closer look.

By the way, the implementation of the editing function itself is complete!

Finally

It's finally done. It was long.

I think it's good to say that we have achieved the "replacement of images one by one" in the specifications. All you have to do is add a preview of the image to make it easier to understand.

You can hear the voice of heaven by previewing the image in the article before previewing the image in the app, but I don't care. I'm sorry it's hard to read.

See you in the next part! !!

I want to implement the product information editing function ~ part5 ~

Recommended Posts

If you think in your heart, "Replace the product image"! At that time, the action is over! ~ part4 ~
If you just want to run your containers in the cloud, Azure Container Instances is easy
Like function The part that is stuck in making it asynchronous
[Rails] About the error that the image is not displayed in the production environment