0

I have a huge multiply nested forms which take a while to be filled. I am looking for a way to cache their form data if a user mistakenly refreshes the page or goes back. It becomes a major pain when they refresh the page and have to refill the entire form again. Is there a way by which I can retain these data before saving?

<%= simple_form_for(round, url: decide_action) do |f| %>
    <div class="block-content block-content-sm">
      <div class="progress" data-wizard="progress" style="height: 8px;">
        <div class="progress-bar progress-bar-striped progress-bar-animated bg-primary" role="progressbar" style="width: 34.3333%;" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100"></div>
      </div>
    </div>
    <div class="block-content block-content-full tab-content" style="min-height: 300px;">
      <div class="tab-pane active show" id="wizard-progress-step1" role="tabpanel">
        <% if @company.equity_pool %>
          <div class="row no-gutters">
            <%= link_to get_revise_form_company_equity_pool_path(@company, @company.equity_pool), remote: true, class: "btn btn-hero-sm btn-square btn-hero-primary", data: {toggle:  "modal", target: '#modal' } do %>
              Revise Form
            <% end %>
          </div>
        <% end %>
        <div class="row no-gutters">
          <div class="col mr-3">
            <%= f.input :name, input_html: {data: {email: "#{current_user.email}"}} %>
          </div>
          <div class="col">
            <%= f.input :pre_money_valuation, as: :string, input_html: { value: 0, data: {autonumeric: {mDec: 2}, fdsc_shares: @company.total_fdsc, investment: ''} }, label: 'Pre money valuation' %>
          </div>
        </div>

        <div class="row no-gutters">
          <div class="col mr-3">
            <%= f.input :issue_price, as: :string, input_html: {min: 0, max: 1000, data: { autonumeric: {mDec: 5}}} %>
          </div>
          <div class="col">
            <%= f.input :conversion_ratio, as: :string, input_html: {min: 0, max: 10000, data: { autonumeric: {mDec: 3 }}} %>
          </div>
          <div class="col">
            <%= f.input :conversion_price, as: :string, input_html: { data: {autonumeric: {mDec: 5}} } %>
          </div>
        </div>
        <div class="row no-gutters">
          <div class="col-12">
            <div class="form-group">
              <div class="custom-control custom-switch custom-control-primary custom-control-lg mb-2">
                <%= f.check_box :anti_dilution, class: 'custom-control-input', id: 'example-sw-custom-primary-lg2' %>
                <label class="custom-control-label" for="example-sw-custom-primary-lg2">Anti Dilution</label>
              </div>
            </div>
          </div>
          <div class="col" id="anti-dilution-fields">
            <%= f.input :dilution_type, collection: Round.dilution_types.keys, include_blank: false, input_html: { id: "dilution-type", style: 'height: 36px;' } %>
          </div>
        </div>

      </div>
      <div class="tab-pane" id="wizard-progress-step2" role="tabpanel", data-company="<%=@company.id %>">
        <%= f.simple_fields_for :certificates, validate: true do |t| %>
          <%= render 'certificate_fields', f:t %>
        <% end %>
        <div class="links">
          <%= link_to_add_association 'Add Investors', f, :certificates, class: 'btn btn-hero-primary btn-block mt-3', id: "addFounder" %>
        </div>
      </div>
      <% if @company.pending_cns.count > 0 %>
        <div class="tab-pane" id="wizard-progress-step3" role="tabpanel">
          <div class="alert alert-primary d-flex align-items-center mb-3" role="alert">
            <div class="flex-00-auto">
              <i class="fa fa-fw fa-check"></i>
            </div>
            <div class="flex-fill ml-3">
              <p class="mb-0">Please tick all the convertible notes you want to convert in this round!</p>
            </div>
          </div>
          <table class="js-table-checkable table table-hover table-vcenter js-table-checkable-enabled mb-5">
            <thead class="thead-light">
              <tr>
                <th class="text-center" style="width: 70px;">
                  <div class="custom-control custom-checkbox custom-control-primary d-inline-block">
                    <input class="custom-control-input" id="check-all" name="check-all" type="checkbox">
                    <label class="custom-control-label" for="check-all"></label>
                  </div>
                </th>
                <th class="text-center" style="width: 100px;">#</th>
                <th class="text-center">Convertible Note Holder</th>
                <th class="text-center">Status</th>
                <th class="text-center">Principal</th>
                <th class="text-center">Interest Rate</th>
                <th class="text-center">Interest Start</th>
                <th class="text-center">Interest Accrued</th>
                <th class="text-center">Total Amount</th>
              </tr>
            </thead>
            <tbody>
              <% @company.pending_cns.each_with_index do |convertible_note, i| %>
                <tr>
                  <td class="text-center">
                    <div class="custom-control custom-checkbox custom-control-primary d-inline-block">
                      <input class="custom-control-input" id="row_<%=i%>" name="conv_notes[<%= i%>]" value="<%= convertible_note.id %>" type="checkbox">
                      <label class="custom-control-label" for="row_<%=i%>"></label>
                    </div>
                  </td>
                  <th class="text-center" scope="row"><%= convertible_note.security %></th>
                    <td class="font-w600 text-center">
                      <a class="text-info push" href="#" data-toggle="modal" data-id= "<%= convertible_note.id %>" data-target="#modal-first"><%= convertible_note.name %></a>
                    </td>
                    <td class="text-center">
                      Outstanding
                    </td>
                    <td class="text-center">
                      <%= number_to_currency(convertible_note.principal) || "No quantity" %>
                    </td>
                    <td class="text-center">
                      <%= convertible_note.convertible_term.interest_rate %>
                    </td>
                    <td class="text-center">
                      <%= convertible_note.issue_date || "No issue date" %>
                    </td>
                    <td class="text-center">
                      <% interest = convertible_note.interest_calculator(convertible_note.principal, convertible_note.issue_date, convertible_note.convertible_term.interest_rate, convertible_note.convertible_term.interest_period, Date.today) %>
                      <%= number_to_currency(interest) || "No Interest" %>
                    </td>
                    <td class="text-center">
                      <%= number_to_currency(convertible_note.principal + interest)%>
                    </td>
                </tr>
              <% end %>
            </tbody>
        </table>
        </div>
      <% end %>
      <% if @company.pending_warrants.count > 0 %>
        <div class="tab-pane" id="wizard-progress-step4" role="tabpanel">
          <table class="js-table-checkable table table-hover table-vcenter js-table-checkable-enabled mb-5">
            <thead class="thead-light">
              <tr>
                <th class="text-center" style="width: 70px;">
                  <div class="custom-control custom-checkbox custom-control-primary d-inline-block">
                    <input class="custom-control-input" id="check-all" name="check-all" type="checkbox">
                    <label class="custom-control-label" for="check-all"></label>
                  </div>
                </th>
                <th class="text-center" style="width: 50px;">#</th>
                <th class="text-center">Warrant Holder</th>
                <th class="text-center">Quantity</th>
                <th class="text-center">Exercise Price</th>
                <th class="text-center">Issue date</th>
                <th class="text-center">Expiration date</th>
              </tr>
            </thead>
            <tbody>
              <% @company.pending_warrants.each_with_index do |warrant, i| %>
                <tr>
                  <td class="text-center">
                    <div class="custom-control custom-checkbox custom-control-primary d-inline-block">
                      <input class="custom-control-input" id="row_<%=i%>" name="warrants[<%= i%>]" value="<%= warrant.id %>" type="checkbox">
                      <label class="custom-control-label" for="row_<%=i%>"></label>
                    </div>
                  </td>
                  <th class="text-center" scope="row"><%= warrant.security %></th>
                  <td class="font-w600 text-center">
                    <a class="text-info push" data-toggle="modal" data-target="#modal-<%= warrant.id %>"><%= warrant.name %></a>
                  </td>
                  <td class="text-center">
                    <%= warrant.quantity || "No quantity" %>
                  </td>
                  <td class="text-center">
                    <%= warrant.warrant_block.exercise_price %>
                  </td>
                  <td class="text-center">
                    <%= warrant.issue_date || "Not entered" %>
                  </td>
                  <td class="text-center">
                    <%= warrant.expiration_date || "Not entered" %>
                  </td>
                </tr>
              <% end %>
            </tbody>
          </table>
        </div>
      <% end %>
      <div class="block-content block-content-sm block-content-full bg-body-light rounded-bottom " style="margin: -20px;margin-top: 75px;width: calc(100% + 40px);">
        <div class="row">
          <div class="col-6">
            <button type="button" class="btn btn-secondary disabled" data-wizard="prev">
              <i class="fa fa-angle-left mr-1"></i> Previous
            </button>
          </div>
          <div class="col-6 text-right">
            <button type="button" class="btn btn-secondary" data-wizard="next">
              Next <i class="fa fa-angle-right ml-1"></i>
            </button>
            <%= f.submit :submit, class: "btn btn-primary d-none", data: {wizard: "finish"}, value: "Create Round"%>
          </div>
        </div>
      </div>
    </div>
  <% end %>
sawa
  • 165,429
  • 45
  • 277
  • 381
meerkat
  • 361
  • 1
  • 3
  • 11
  • One thing I would recommend is when someone tries to navigate from the page with unsaved changes, display an alert warning them, and allow them to confirm that they want to navigate away, or cancel to keep editing. Check this post for more info. https://stackoverflow.com/q/11844256/10068463 – Nate Jan 18 '19 at 05:56
  • Other than that, you could look into backing the data up into LocalStorage (browser storage) periodically while editing (like a draft) and asking the user if they’d like to pick up where they left off the next time the page loads, if they didn’t submit the form last time. – Nate Jan 18 '19 at 05:58
  • @nate How do I back up my data into LocalStorage? – meerkat Jan 18 '19 at 06:37

2 Answers2

1

I think you better change the user interface, and let the user submit little by little on small forms each time. Prepare different pages, each with a little amount of form. Depending on how far the user got, they should be redirected to a page with the appropriate form to continue.

sawa
  • 165,429
  • 45
  • 277
  • 381
  • 1
    This would definitely feel much less overwhelming for the user, especially if validation errors happen. What you’re describing would basically be a “wizard” form. – Nate Jan 18 '19 at 14:28
  • My form is a wizard form – meerkat Jan 18 '19 at 17:41
0

As suggested in the comments by @Nate, it is a good idea to store the current state of the form in localStorage. For example, on every form field focus, you could run the code from the snippet https://gist.github.com/shawnbot/89855705204494c6d8bf.

Alternatively, you can use a custom jQuery library called jquery.remember-state: http://shaneriley.com/jquery/remember_state/

Both approaches will solve you problem.

MatFiz
  • 973
  • 1
  • 8
  • 25
  • this does solves my problem but if there are multiple nested forms like in my case if I have more than one certificates then it retains only the first nested form data because the controller builds only one nested form. – meerkat Jan 18 '19 at 17:42
  • @meerkat it is all up to you to cache nested forms in local storage. Just find a javascript selector that will choose the filelds from both main and nested forms an cache this. You can even call sth like `$('form').serialize()` (if you're using jQuery). Alternatively, you can iterate over `document.forms` and save values from each from in local storage with key being `formID.fieldID`. Many possibilities :) – MatFiz Jan 20 '19 at 20:50