javascriptruby-on-railsfetcherb

Is there a way to render a js.erb after a fetch call?


In my Rails app, I want to render a js.erb partial after a fetch call and for some reasons I do not understand it does not work. If you can help me, it would be great.

In a view, an event trigger this function that basically do a fetch request:

function updateState(id, state){
      const csrfToken = document.querySelector('meta[name="csrf-token"]').attributes
          .content.value;
      fetch(window.location.origin + "/update_state", {
          method: "POST",
          headers: {
            'Accept': "JS",
            "Content-Type": "application/json",
            "X-CSRF-Token": csrfToken
          },
          body: JSON.stringify({'id': id, 'state': state}),
          credentials: "same-origin"
        })
}

Then in my controller:

  def update_state
    @model = Model.find(params[:id])
    authorize @model
    @model.update(state: params[:state])

    respond_to do |format|
      format.html { redirect_to xxx }
      format.js
    end
  end

In my js.erb files:

console.log('hello');

However, in that case, I get an error from the server: ActionController::UnknownFormat at the line 'respond_to do |format| I have the feeling that the server do not understand the header of the fetch: 'Accept': "JS"

When I look at the logs of the server:

Started POST "/update_state" for 127.0.0.1 at 2019-04-04 11:22:49 +0200
Processing by ModelsController#update_state as JS

But I think that Rails does not recognize it. How do I do?

I tried also this in the controller:

  def update_state
    @model = Model.find(params[:id])
    authorize @model
    @model.update(state: params[:state])
    render 'update_state.js', layout: false
  end

Which does not fire errors. I received in the client side the js.erb. However, it is not executed (console.log does not execute).

Do you have any idea? Thank a lot for your help.


Solution

  • I manage the other way around. I never succeded with js.erb.

    So I use a fetch call with a response in text:

    function updateState(id, state){
      const csrfToken = document.querySelector('meta[name="csrf-token"]').attributes
          .content.value;
      fetch(window.location.origin + "/update_state", {
          method: "POST",
          headers: {
            'Accept': "JS",
            "Content-Type": "application/json",
            "X-CSRF-Token": csrfToken
          },
          body: JSON.stringify({'id': id, 'state': state}),
          credentials: "same-origin"
        }).then(response => response.text())
          .then(data => {
           div.inserAdjacentHTML('XXX', data);
        });
    }
    

    Then in the controller/action:

      def update_state
        @model = Model.find(params[:id])
        authorize @model
        @model.update(state: params[:state])
        render 'update_state.js', layout: false, content_type: "text/plain"
      end
    

    That way, it works. Hope it helps.