Search code examples
ruby-on-railsrubyajaxforms

How to add a user input to re-calculate code in controller and then show in the same view?


Looking to be pointed in the right direction. I have a simple form in my view that accepts a number. I am wanting to send the input of this to be used in a calculation in my controller and then update for the user to see in real time.

index.html.erb Simple input form, followed by calculation output:

<%= form_with do |form| %>
<%= form.text_field :number %>
<%= form.submit "Submit" %>
<% end %> 

<%= @output %>

Calculation in controller:

@output = params[:number].to_i * 2

The code is just a stupid example, but the idea is: User input in the view, which I could then place in a calculation in my controller and then send back to view. I don't need a database as I don't need to store any information.

After researching, a lot of people are suggesting Ajax for this in rails? Is this the way to go or is there something in Ruby that I can use for the same effect. I've also seen Tableless Models being used. What would the best solution be here for me? I'm guessing recalculating the code in controller is the key bit.


Solution

  • Yes, you can do this with AJAX. In Rails, you can use a Stimulus controller to do that.

    Below is a simple demonstration:

    1. I have a controller called pages and it looks like this.
    class PagesController < ApplicationController
      def index
      end
    
      def receive
        render json: { body: params }
      end
    end
    

    Then pages#index view should be like this.

    <div class="input-form" data-controller="ajax">
      <%= form_with url: receive_path, data: { ajax_target: 'form', action: 'submit->ajax#send' } do |form| %>
        <%= form.text_field :number %>
        <%= form.submit "Submit" %>
      <% end %>
      <p data-ajax-target="output"></p>
    </div>
    

    It has your form and p tag with data-ajax-target="output" is the element where you display form input data.

    Then you generate a new stimulus controller caller ajax and put something like this.

    export default class extends Controller {
      static targets = ['form', 'output']
      connect() {
        console.log(this.formTarget);
        console.log(this.outputTarget);
      }
    
      send(event) {
        event.preventDefault();
    
        fetch(this.formTarget.action, {
          method: 'POST',
          headers: { "Accept": "application/json" },
          body: new FormData(this.formTarget)
        })
          .then(response => response.json())
          .then(data => {
            this.outputTarget.innerHTML = data.body.number * 2;
          });
      }
    }
    

    And you need to make sure that you define a post route.

    Rails.application.routes.draw do
      root 'pages#index'
      post 'receive', to: 'pages#receive'
    end
    

    Hope that make sense.