0

My app has a portfolio that has many positions. Each position has many movements.

Within a portfolio I am showing all of my positions.

<p>
  <strong>Name:</strong>
  <%= @portfolio.name %>
</p>

<%= render partial: @positions %>

<%= render 'positions/form' %>

<%= link_to 'Edit', edit_portfolio_path(@portfolio) %> |
<%= link_to 'Back', portfolios_path %>

My position partial is written like this:

<ul>
  <li><%= position.name %></li>
  <li><%= position.quantity %></li>
  <li><%= position.ticker %></li>
</ul>
<%= form_for [@portfolio, position, @movement] do |f| %>
  <div id = "movement-errors">
    <% if @movement.errors.any? %>
      <%= render partial: 'movements/movement_error_messages' %>
    <% end %>
  </div>
  <div class="field">
    <%= f.label :quantity %>
    <%= f.text_field :quantity %>
 </div>
 <div class="actions">
   <%= f.submit %>
 </div>
<% end %>

With my error partial like this:

<% if @movement.errors.any? %>
  <div id = "error_explanation">
    <div class = "alert alert-danger">
      The form contains <%= pluralize(@movement.errors.count, "error") %>.
    </div>
    <ul>
      <% @movement.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>

When I submit an empty quantity I want the error message to render on the same page. As it stands right now, when I submit an empty form, I'm redirected to the Movements#new page where I have a form and it has an update button.

My Movement controller is built like:

class MovementsController < ApplicationController
  before_action :load_portfolio_and_position

  def create
    @movement = Movement.new(movement_params)
    @movement.position_id = @position.id
    @movement.update_price
    @movement.date = DateTime.now
    @movement.trade ='buy'

    respond_to do |format|
      if @movement.save
        @position.update_attribute(:quantity, (@movement.quantity + @position.quantity))
        format.html { redirect_to @portfolio, notice: 'Movement was successfully created.' }
        format.js
      else
        format.html { render :new }
        format.json { render json: @movement.errors, status: :unprocessable_entity }
        format.js
      end
    end
  end

I just want to submit either valid or invalid data and have it be handled in the same page.

1 Answer 1

1

Use local variables instead when building partials. This makes them truly reusable and useful:

# app/views/shared/_errors.html.erb
<% if object.errors.any? %>
  <div id = "error_explanation">
    <div class = "alert alert-danger">
      The form contains <%= pluralize(object.errors.count, "error") %>.
    </div>
    <ul>
      <% object.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>

This means we can render it like:

<%= render 'shared/errors', object: @movement %>

I just want to submit either valid or invalid data and have it be handled in the same page.

Thats not how Rails works and for good reason. When you submit a form and the form is invalid you render a view which displays the error messages and form in so that the user can correct the issues.

Redirecting back would put any user entered data in the query string where it is less secure or rely on using the session in a way that violates REST.

format.html { render :new }

Does not call the new action in your controller. It just looks for a /movements/new.html.erb view and renders it.

You can of course have it render any other view you want. Like render template: 'foo/bar'.

Sign up to request clarification or add additional context in comments.

5 Comments

I've made the changes and yet, when I submit an empty form for movements on the portfolio form page I get:
'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
highlighting the <%= render @positions %> in the show page.
Its most likely because @portfolio is nil in your MovementsController#create action.
I have a before_action that loads both the portfolio and position objects for all actions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.