Skip to content

Instantly share code, notes, and snippets.

@stevepolitodesign
Last active October 31, 2021 16:03
Show Gist options
  • Save stevepolitodesign/96eaaad6647353968d9bca3912e8b613 to your computer and use it in GitHub Desktop.
Save stevepolitodesign/96eaaad6647353968d9bca3912e8b613 to your computer and use it in GitHub Desktop.
Poor Man's Turbo with Remote Forms and Stimulus JS
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="form"
export default class extends Controller {
  static targets = [ "error" ]
  static values = { target: String,  action: String }

  connect() {
    this.target = this.hasTargetValue && document.querySelector(this.targetValue);
    this.action = this.hasActionValue && this.actionValue;
    this.actions = ["beforebegin", "afterbegin", "beforeend", "afterend"];
    console.log(this);
  }

  handleRequest(event){
    const { status, response } = event.detail[0];

    if(status >= 200 && status <= 299) {
      this.handleSuccess(response);
    }

    if(status >= 400) {
      this.handleError(response);
    }
  }

  handleError(response){
    this.errorTarget.innerHTML = response;
  }

  handleSuccess(response) {
    this.clearErrors();  
    this.clearForm();

    if(this.actionIsPermitted(this.action)) {
      this.target.insertAdjacentHTML(this.action, response);
    } else {
      this.target.innerHTML = response;
    }
  }

  clearErrors(){
    this.hasErrorTarget && (this.errorTarget.innerHTML = "");
  }

  clearForm(){
    this.element.reset();
  } 

  actionIsPermitted(action) {
    if(this.actions.indexOf(action) == -1) {
      return false;
    } else {
      return true;
    }
  }
}
class TasksController < ApplicationController
  def create
    @task = Task.new(task_params)
    if @task.save
      render @task, layout: false
    else
      render(
        partial: "layouts/form_errors",
        locals: { object: @task },
        status: :unprocessable_entity,
        layout: false
      )
    end
  end
end  
<%= render partial: "form" %>
<ul id="tasks">
  <%= render @tasks %>
</ul>
<%= form_with(
  model: @task,
  local: false,
  data: { 
    controller: "form",
    form_target_value: "#tasks",
    form_action_value: "afterbegin",
    action: "ajax:complete->form#handleRequest"
  }
) do |form| %>
  <div data-form-target="error">
    <%= render partial: "layouts/form_errors", locals: { object: form.object } %>
  </div>
  <%= form.text_field :title %>
  <%= form.submit %>
<% end %>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment