如何使用Turbolinks在Rails中监视表单提交事件?

时间:2018-07-31 18:18:03

标签: javascript ruby-on-rails turbolinks

我想在带有Turbolinks的Rails应用程序中的表单上监视Submit事件。我提出了以下建议:

document.addEventListener('turbolinks:load', function(e) {
  var arrangeForm;
  arrangeForm = document.querySelector('#arrange-dust-form');
  if (arrangeForm) {
    return arrangeForm.addEventListener('submit', function(e) {
      // do stuff on submit...
    });
  }
});

这可行,但是关于if (arrangeForm)的部分似乎很尴尬。但是,如果我不检查表单是否存在,则导航到网站的其他页面时会收到错误消息,因为它试图将侦听器设置在不存在的元素上。

在不必检查每个turbolinks:load事件中是否存在表单的情况下,是否有“正确”或“正式”的方式来执行此操作?我应该将事件附加到哪个更适用的节点上?

2 个答案:

答案 0 :(得分:0)

对于仅在一页上运行且不包含在整个应用范围内的任何特定js的Rails方式,是在您感兴趣的视图页面上使用content_for:

    <% content_for :myjava do %>
      <script type='text/javascript'>
        document.addEventListener('turbolinks:load', function(e) {
          var arrangeForm;
          arrangeForm = document.querySelector('#arrange-dust-form');
        if (arrangeForm) {
        return arrangeForm.addEventListener('submit', function(e) {
          // do stuff on submit...
        });
       }
      });
     </script>
  <% end %>

然后在您的layouts / application.html.erb文件中将其屈服于body标签的末尾,例如:

<body>
  #whatever layout view code
  <%= yield :myjava %>
</body>

答案 1 :(得分:0)

基本解决方案

Turbolinks Readme建议使用事件委托来处理事件:

  

如果可能,请避免使用turbolinks:load事件将其他事件侦听器直接添加到页面正文上的元素。相反,请考虑使用事件委托在文档或窗口上一次注册事件侦听器。

例如:

window.addEventListener('submit', function (event) {
  if (event.target.id === '#arrange-dust-form') {
    // do stuff on submit…
  }
})

这使我们的事件处理程序更加灵活,因为它不需要turbolinks:load事件即可工作。如果您的#arrange-dust-form是在turbolinks:load之后动态添加的,或者即使您完全删除了Turbolink,该代码仍然可以使用。

创建基本的事件委托系统

上面的解决方案很好,但是每次要处理事件时都要检查事件目标很费力。如果只需要声明事件的名称,事件处理程序和CSS选择器来限制处理程序的调用时间,那就更好了。 jQuery为此提供了一个不错的API:

$(window).on('submit', '#arrange-dust-form', function (event) {
  // do stuff on submit…
})

但是,如果您不使用jQuery或不希望使用jQuery,我们可以重新创建一个基本版本:

;(function () {
  var eventTypes = [
    'click', 'mouseover', 'mouseout', 'mousedown',
    'mouseup', 'mouseenter', 'mouseleave', 'mousemove',
    'keydown', 'keyup', 'submit', 'change', 'contextmenu',
    'dblclick', 'input', 'focusin', 'focusout'
  ]

  // Build structure for storing event handlers
  var handlers = eventTypes.reduce(function (obj, eventType) {
    obj[eventType] = {}
    return obj
  }, {})

  // Add listeners for each event
  eventTypes.forEach(function (eventType) {
    window.addEventListener(eventType, function (event) {
      var handlersForEvent = handlers[eventType]

      for (var selector in handlersForEvent) {
        if (event.target.matches(selector)) {
          callHandlers(handlersForEvent[selector], event)
        }
      }
    })
  })

  function callHandlers (eventHandlers, event) {
    eventHandlers.forEach(function (handler) {
      handler.call(null, event)
    })
  }

  window.App = {
    addEventListener: function (eventType, selector, handler) {
      (handlers[eventType][selector] = handlers[eventType][selector] || [])
        .push(handler)
    }
  }
})()

// Usage
App.addEventListener('submit', '#arrange-dust-form', function (event) {
  // do stuff on submit…
})

这是一个简短的摘要。首先,它创建一个对象来存储每种事件类型的事件处理程序。然后,它为每种事件类型添加一个事件侦听器。该侦听器获取与事件类型匹配的处理程序,循环选择器,如果事件目标与选择器匹配,则调用处理程序。 App.addEventListener获取给定事件类型和选择器的处理程序数组(如果不存在,则创建一个数组)并将处理程序推入该数组。在此系统中,可以为给定的事件类型/选择器添加多个处理程序。

一些警告:1. IE中不支持Element.matches,因此您可能需要polyfill或添加一个包装函数来支持这一点。 2.这没有添加删除事件侦听器的方法,因此您可能希望添加它。

希望有帮助。