Stimulus Discourse

Controller action running twice

I’m new to the world of Stimulus, I’ve been working on adding a few actions to my Rails application. I have a comments form, when submitted I’d like the comment body from the form to be appended to the rest of the comments. Everything seems to be working because this happens, but then the controller action runs again giving the appearance that the comment was added two times.

This is my controller:

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = ["body", "add"]

  addBody() {
    let content = this.bodyTarget.value;
    this.addTarget.insertAdjacentHTML('beforebegin', `<div>${content}</div>`);
  }
}

The form:

  <div data-controller="posts">
     <div data-target="posts.add">
     </div>
     <!-- this is from a rails partial but it's all inside the data-controller -->
     <%= form_with scope: :post, url: posts_path, method: 'post', data: { action: "post#addBody" } do |form| %>
     <%= form.text_field :content, class: "form-control", data: { target: "posts.body"} %>
     <%= form.submit class: "btn btn-primary" %>
  </div>

For general reference my Rails controller:

 def create
    @post = current_user.post.build(post_params)

    respond_to do |format|
      if @post.save
        format.json {head :ok}
      else
        raise ActiveRecord::Rollback
      end
    end

  end

I’ve tried several things, explicitly setting remote: true on the form in the view, also trying data: { action: "ajax:success->post#addBody" } these things don’t seem to make an impact. Adding a debugger statement to the controller action I can clearly see that the action is being called two times.

Turns out the issue was in application.js:

const application = Application.start()
const context = require.context("../controllers", true, /\.js$/)
application.load(definitionsFromContext(context))

import "controllers"

import "controllers", not sure why it was there but that was causing the issue. I’m not really sure why but removing it led all controller actions to only fire once!

The reason is that most folks import "controllers" in their app/javascript/packs/application.js but then have a app/javascript/controllers/index.js that looks like:

import { Application } from 'stimulus'
import { definitionsFromContext } from 'stimulus/webpack-helpers'

const application = Application.start()
const context = require.context('controllers', true, /_controller\.js$/)

application.load(definitionsFromContext(context))

Somehow, some index.js code seems to have made its way to your application.js. Ironically, the only Stimulus-related code you want in your application.js is import "controllers" after all.

Ah, thanks. That makes sense.