首页 > 解决方案 > XHR 表单提交到渲染部分被触发,但视图未更新

问题描述

我想我错过了一些明显的东西。

应该发生什么:

当用户将图像添加到 Story#new 表单时:

  1. 用户点击部分file_field中的Story#new _form
  2. Javascriptfile_field使用 XMLHttpRequest 将表单数据(包括包含图像)发布到自定义控制器操作
  3. 该自定义操作使用 ActiveStorage 将该图像附加到关联的报告
  4. 该自定义操作调用.js.erb重新呈现部分视图的视图
  5. 重新渲染的部分显示了附加的图像

实际发生了什么

除了#5之外的一切。

设置:

有问题的型号:

class Story < ApplicationRecord
  belongs_to :report, inverse_of: :story
  has_one_attached :image, dependent: :purge
  
  def picture
    image.attached? ? Rails.application.routes.url_helpers.rails_blob_path(image, only_path: true) : 
                      ActionController::Base.helpers.asset_path('story_no_image.png')
  end
end

class Report < ApplicationRecord
  has_one    :story, inverse_of: :report, dependent: :destroy
  has_one_attached :image, dependent: :purge

  def picture
    image.attached? ? Rails.application.routes.url_helpers.rails_blob_path(image, only_path: true) : 
                      ActionController::Base.helpers.asset_path('story_no_image.png')
  end
end

故事是根据现有报告创建的。因此,为了“急切地预览”用户想要与故事相关联的图像,让我们暂时将其保存到相关报告中。

脚步:

1.用户点击部分file_field中的Story#new _form

# app/views/stories/_form.haml
# @story is new, but it's associated @report already exists,
# so let's temporarily associate the image with @report until @story gets created.
.container
  .col-12
    %h1 New Story
    %h4= "For Report ##{link_to(@report.id, @report)} #{@report.details}".html_safe
  .col-12
    = simple_form_for @story, wrapper: :horizontal_form, html: { id: 'story_form' }  do |f|
      = f.input :report_id #, as: :hidden
      .field
        = f.input :title
      .field.wysiwyg
        = f.input :text, input_html: { class: "tinymce", style: "height: 300px;" }
      .field
        = f.label @story.image.present? ? 'Replace Picture:' : 'Choose Picture:'
        = f.file_field :image, id: "image_upload", accept: 'image/*'
      .actions
        = f.submit 'Save', class: 'btn yellow'

.row.mt-5
  .col-12
    %h5 Image Preview:
  .col-6#image_preview_wrapper
    = render partial: 'image_preview', locals: { record: @story }
  .col-6.text-center
    =link_to 'Rotate Left', '#', class: 'rotate-btn', data: { rotation: 'left' }
    =link_to 'Rotate Right', '#', class: 'rotate-btn', data: { rotation: 'right' }
    =link_to 'Delete Image', '#', id: 'purge_img_btn'
= tinymce
# app/views/stories/_image_preview.haml
- byebug if record.class.name == 'Report' # using this debugger to make sure item #4 is actually happening
=image_tag(record.picture, id: "image_preview", class: 'yay!')

2. Javascript POST 表单数据

$(document).on 'turbolinks:load', ->
  # only for new records:
  if controllerMatches(['stories']) and actionMatches(['new', 'create'])

    $('#image_upload').on 'change', ->
      return unless !!$('#image_upload').val().length
      reportId = $('#story_report_id').val()
      eagerUrl = '/reports/' + reportId + '/upload_eager'
      form = document.getElementById('story_form')
      formData = new FormData(form)
      xhr = new XMLHttpRequest()
      xhr.open 'post', eagerUrl, true
      xhr.send(formData)

自定义路由:

# routes.rb
resources :reports do
  post 'upload_eager', on: :member
  post 'rotate_eager', on: :member
end

3. 该自定义操作使用 ActiveStorage 将该图像附加到关联的报告

# ReportsController

def upload_eager
  # for Story#new, upload an 'eager' image by attaching the image
  # to the report (which exists) instead of the story (which doesn't)
  authorize @report = Report.find(params[:id])

  return unless eager_image_params[:image].present?

  # this method is my customization of @report.image.attach(image_io)
  # so I can rename and resize the image before uploading.
  @report.upload_image!(eager_image_params[:image]) unless @report.image.attached?

  respond_to do |format|
    format.js
  end
end

4. 该自定义操作调用.js.erb重新呈现部分的视图:

# upload_eager.js.erb
$('#image_preview_wrapper').html("<%= j render partial: 'stories/image_preview', locals: { record: @report } %>");
$('#image_preview').removeClass('boo');
$('#image_preview').addClass('YAY');

5. 重新渲染的部分显示附加的图像。

DOM 没有改变(注意我在 js 部分改变了标签的类)

但是浏览器视图没有更新。

我唯一的成功是:

如果我upload_eager从 Network 事件中获取 JQuery 的结果,将其放入 DevTools 控制台并按 Enter 键,图像标签将被替换并且图像正确显示(又名 - JQuery 触发)。

这告诉我,来自部分的 JQuery 实际上并没有触发,我可以确认这一点,因为<img>标签的类没有改变。

让我难过的是为什么终端日志显示部分已呈现:

Started POST "/reports/1615/upload_eager" for ::1 at 2019-11-21 23:09:16 -0500
Processing by ReportsController#upload_eager as */*
  Parameters: {"utf8"=>"✓&quot;, "authenticity_token"=>"H2SVzwB8b02wNde/YePpYnVGBYa3nd6i4THa21MLOQbNQ/CgE/lIjAqpqaCUnsCKzEEZCDuEGuLfylWwwreo9g==", "story"=>{"report_id"=>"1615", "title"=>"", "prominent"=>"0", "text"=>"", "image"=>#<ActionDispatch::Http::UploadedFile:0x00007fc2237425b0 @tempfile=#<Tempfile:/var/folders/th/fsmws2ns6t54j_yy1wbk7qhc0000gn/T/RackMultipart20191121-80561-1j4sec1.JPG>, @original_filename="test-story-family-sam3.JPG", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"story[image]\"; filename=\"test-story-family-sam3.JPG\"\r\nContent-Type: image/jpeg\r\n">}, "year"=>"2019", "month"=>"8", "id"=>"1615"}
  Report Load (0.3ms)  SELECT "reports".* FROM "reports" WHERE "reports"."id" = $1 LIMIT $2  [["id", 1615], ["LIMIT", 1]]
  ↳ app/controllers/reports_controller.rb:82:in `upload_eager'
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/controllers/reports_controller.rb:82:in `upload_eager'
Unpermitted parameters: :report_id, :title, :prominent, :text
  ActiveStorage::Attachment Load (0.4ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4  [["record_id", 1615], ["record_type", "Report"], ["name", "image"], ["LIMIT", 1]]
  ↳ app/controllers/reports_controller.rb:86:in `upload_eager'
Unpermitted parameters: :report_id, :title, :prominent, :text
   (0.2ms)  BEGIN
  ↳ app/models/report.rb:91:in `upload_image!'
  Technology Load (0.4ms)  SELECT "technologies".* FROM "technologies" WHERE "technologies"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  ↳ app/models/report.rb:91:in `upload_image!'
  User Load (0.4ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 2], ["LIMIT", 1]]
  ↳ app/models/report.rb:91:in `upload_image!'
  Contract Load (0.4ms)  SELECT "contracts".* FROM "contracts" WHERE "contracts"."id" = $1 LIMIT $2  [["id", 4], ["LIMIT", 1]]
  ↳ app/models/report.rb:91:in `upload_image!'
  Village Load (0.3ms)  SELECT "villages".* FROM "villages" WHERE "villages"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/models/report.rb:91:in `upload_image!'
  ActiveStorage::Blob Load (0.5ms)  SELECT "active_storage_blobs".* FROM "active_storage_blobs" INNER JOIN "active_storage_attachments" ON "active_storage_blobs"."id" = "active_storage_attachments"."blob_id" WHERE "active_storage_attachments"."record_id" = $1 AND "active_storage_attachments"."record_type" = $2 AND "active_storage_attachments"."name" = $3 LIMIT $4  [["record_id", 1615], ["record_type", "Report"], ["name", "image"], ["LIMIT", 1]]
  ↳ app/models/report.rb:91:in `upload_image!'
  ActiveStorage::Blob Create (0.4ms)  INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["key", "moe206b5grl9laccxf4707kfb5ma"], ["filename", "000000_eager_1615.JPG"], ["content_type", "image/jpeg"], ["metadata", "{\"identified\":true}"], ["byte_size", 108855], ["checksum", "9BB+x9JRqJfEKb+RKcUEFA=="], ["created_at", "2019-11-22 04:09:17.443238"]]
  ↳ app/models/report.rb:91:in `upload_image!'
  ActiveStorage::Attachment Create (0.4ms)  INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["name", "image"], ["record_type", "Report"], ["record_id", 1615], ["blob_id", 33], ["created_at", "2019-11-22 04:09:17.447089"]]
  ↳ app/models/report.rb:91:in `upload_image!'
   (0.4ms)  SELECT "plans"."id" FROM "plans" WHERE "plans"."contract_id" = $1 AND "plans"."technology_id" = $2 AND "plans"."planable_id" = $3 AND "plans"."planable_type" = $4 LIMIT $5  [["contract_id", 4], ["technology_id", 1], ["planable_id", 3], ["planable_type", "Village"], ["LIMIT", 1]]
  ↳ app/models/report.rb:274:in `find_plan'
  Report Update (0.4ms)  UPDATE "reports" SET "updated_at" = $1 WHERE "reports"."id" = $2  [["updated_at", "2019-11-22 04:09:17.450861"], ["id", 1615]]
  ↳ app/models/report.rb:91:in `upload_image!'
   (0.6ms)  COMMIT
  ↳ app/models/report.rb:91:in `upload_image!'
  Disk Storage (5.8ms) Uploaded file to key: moe206b5grl9laccxf4707kfb5ma (checksum: 9BB+x9JRqJfEKb+RKcUEFA==)
  Rendering reports/upload_eager.js.erb
  Rendered stories/_image_preview.haml (Duration: 1.0ms | Allocations: 310)
  Rendered reports/upload_eager.js.erb (Duration: 3.7ms | Allocations: 840)
Completed 200 OK in 1227ms (Views: 6.9ms | ActiveRecord: 5.5ms | Allocations: 20005)

标签: ruby-on-railsxmlhttprequestrails-activestorage

解决方案


推荐阅读