rails / request.js

MIT License
389 stars 28 forks source link

Shorthand verbs don't work with webpack 5/jsbundling #49

Closed rcackerman closed 1 year ago

rcackerman commented 2 years ago

I'm upgrade a Rails 6 application from using webpacker to jsbundling with webpack 5. I'm running into a very strange error where get, patch, post, etc result in a TypeError, while the FetchRequest class does not. The exact error is that <webpack module>.post is not a constructor.

I managed to reproduce the error in a simple test application using Rails 6 and then following the migration instructions from jsbundling. There's a simple form:

<%= form_for @item, method: :patch, html: {data: {controller: "ajax-button"}} do |form| %>
  <div class="form-check form-switch form-control-lg ms-3">
    <%= form.check_box :active, class: "form-check-input", role: "switch", data: {action: "change->ajax-button#submit"} %>
  </div> Active
<% end %>

This doesn't work:

import { Controller } from "@hotwired/stimulus"
import { get, post, put, patch, destroy } from '@rails/request.js'

// Connects to data-controller="ajax-button"
export default class extends Controller {
  async submit(event) {
    event.preventDefault()
    event.stopPropagation()

    let payload = new FormData(this.element)
    const request = new post(this.element.action, { body: payload, responseKind: "json" })
    const response = await request.perform();
    if(response.ok) {
      return response
    }
  }
}

but this does:

import { Controller } from "@hotwired/stimulus"
import { FetchRequest } from "@rails/request.js"

// Connects to data-controller="ajax-button"
export default class extends Controller {
  async submit(event) {
    event.preventDefault()
    event.stopPropagation()

    let payload = new FormData(this.element)
    const request = new FetchRequest('patch', this.element.action, { body: payload, responseKind: "json" })
    const response = await request.perform();
    if(response.ok) {
      return response
    }
  }
}

My package.json looks like this:

{
  "name": "test-jsbundling",
  "private": true,
  "dependencies": {
    "@hotwired/stimulus": "^3.1.0",
    "@hotwired/turbo-rails": "^7.1.3",
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/request.js": "^0.0.6",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "5.4.3",
    "turbolinks": "^5.2.0",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "webpack-dev-server": "^3"
  },
  "scripts": {
    "build": "webpack --config webpack.config.js"
  }
}

And my webpack config looks like:

const path    = require("path")
const webpack = require("webpack")

module.exports = {
  mode: "development",
  optimization: {
    moduleIds: 'hashed',
  },
  devtool: "source-map",
  entry: {
    application: "./app/javascript/application.js"
  },
  output: {
    filename: "[name].js",
    sourceMapFilename: "[file].map",
    path: path.resolve(__dirname, "app/assets/builds"),
  },
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    })
  ]
}
ur5us commented 1 year ago

@rcackerman Take another close look at Shorthand methods. Now compare that to your “This doesn’t work” code snippet, specifically the following bit:

const request = new post(this.element.action, { body: payload, responseKind: "json" })
const response = await request.perform();

The correct invocation looks as follows:

const response = await post(this.element.action, { body: payload, responseKind: "json" });

In the short version form you skip constructing a Request object, so you don’t need new …/request.perform() as the short versions do that for you.

marcelolx commented 1 year ago

@rcackerman Did you solve your problem based on @ur5us answer? I'll close the issue, but if you still are facing the problem we can reopen it, thank you!