Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The forme_set plugin remembers columns from another form on the same page #109

Closed
janko opened this issue Jan 15, 2023 · 5 comments
Closed

Comments

@janko
Copy link
Contributor

janko commented Jan 15, 2023

I have two forms for an Expense records, one for updating the amount, the other for updating the note describing the expense. The first form has only the field for the amount, while the second has only the field for the note. However, when I render both forms on the same page, and submit the second one, it tries to nullify the amount field.

Here is a self-contained example demonstrating this issue:

require "roda"
require "sequel"
require "capybara"

DB = Sequel.sqlite
DB.create_table :expenses do
  primary_key :id
  Float :amount, null: false
  String :note, null: false
end

class Expense < Sequel::Model
  plugin :forme_set
end

Expense.create(amount: 123, note: "Description")

class App < Roda
  plugin :sessions, secret: "a" * 64
  plugin :route_csrf
  plugin :forme_set, secret: "a" * 64
  plugin :render

  route do |r|
    @expense = Expense.first!

    r.root do
      render inline: <<~ERB
        <% form @expense, method: :post, action: "/update" do |f| %>
          <%= f.input :amount %>
          <%= f.button "Update Amount" %>
        <% end %>
        <% form @expense, method: :post, action: "/update" do |f| %>
          <%= f.input :note %>
          <%= f.button "Update Note" %>
        <% end %>
      ERB
    end

    r.post "update" do
      forme_set(@expense).save

      r.redirect "/"
    end
  end
end

session = Capybara::Session.new(:rack_test, App)
session.visit "/"

session.fill_in "Amount", with: "456"
session.click_on "Update Amount" # performs just fine

session.fill_in "Note", with: "Updated"
session.click_on "Update Note" # ~> SQLite3::ConstraintException: NOT NULL constraint failed: expenses.amount

When I inspect the HTML, the _forme_set_data field in the second form has "columns":["amount", "note"], instead of just ["note"]. I expected forms to be fully self-contained, where rendering one form won't affect the other. Can this be considered a bug?

@janko janko changed the title The forme_set plugin remembers columns between two different forms on the same page The forme_set plugin remembers columns from another form on the same page Jan 15, 2023
@janko
Copy link
Contributor Author

janko commented Jan 15, 2023

Seems like this is because Forme inputs are being stored on the Sequel model instance, so they persisted from the previous form:

def forme_inputs
return (@forme_inputs || {}) if frozen?
@forme_inputs ||= {}
end

@jeremyevans
Copy link
Owner

This behavior is currently expected, though apparently it isn't listed in the Caveats section of the documentation. In this case, you should avoid using forme_set to handle responses for the route.

However, it may be possible to change things for the Roda forme_set plugin so that the hidden input only includes inputs added to the model object during the form. This will require some care to be backwards compatible, so that only the hidden input is affected, and the model object still has inputs from all forms after processing.

@janko
Copy link
Contributor Author

janko commented Jan 16, 2023

If changes could be made for multiple forms to work, in a way that's backwards compatible, that would be great 👌🏻

@jeremyevans
Copy link
Owner

Agreed. I'll try to work on this this week.

@janko
Copy link
Contributor Author

janko commented Jan 17, 2023

Perfect, thanks for the quick fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants