Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails form rendering wrong URL after validation errors (not keeping passed parameter)

On a project show page, I pass a very simple parameter on my 'create new task' that stores which project its from:

@project.id), :class => "btn btn-info col-md-12" %>

so that when i create a new task for it, it stores it in the URL on my new task form like this:

http://localhost:3000/task/new?project_id=5

My New form is as follows:

<div class="container sign-in-register">
    <div class="authform">

           <%= form_for @task, :html => {:multipart => true} do |f| %>

          <h3>Add a task for this project...</h3><br/>

            <%= f.label :name %>
            <%= f.text_field :name, class: 'form-control' %>

            <%= hidden_field_tag 'project_id', @project_id %>

            <%= f.fields_for :taskrelationships do |ff| %>
              <%= ff.hidden_field :taskproject_id, value: @project_id %>
              <%= ff.label :task_url %>
              <%= ff.text_field :task_url, class: 'form-control' %>
            <% end %>

            <br clear="all">

            <%= f.submit "Save Task", class: "btn btn btn-info" %>
          <% end %>

    </div>
</div>

As you can see i'm using nested attributes in the form (I am creating both a task and a TaskRelationship. Now, when I try to save without filling out all the required fields a validation is thrown but for some reason it redirects me to:

http://localhost:3000/tasks

instead of the original:

http://localhost:3000/tasks/new?project_id=5

I have read many posts and none seem to answer this particular case. The stackO post below was close but when I try it with task instead of user it still cannot find the task_ID

Render error on new page with customize url like http://localhost:3000/education_informations/new?user_id=10

How can I have rails simply render the same exact url as I started with - it seems like this should be easy so must be missing something small.

My controller actions:

def new
    @project_id = params[:project_id]
    @task = Task.new
    @task.taskrelationships.build
  end

  def create
    @project = Project.find(params[:project_id])

    @task = Task.new(task_params)
    if @task.save
      flash[:success] = "This task has been added."
      @task.taskrelationships.create!(@taskrelationships_params)
      redirect_to tasks_project_path(@project)
    else
      @task.taskrelationships.build(@taskrelationships_params)
      flash[:alert] = @task.errors.full_messages.to_sentence
      render :new
    end
  end

 private

def task_params
      @taskrelationships_params = params.require(:task).permit(taskrelationships_attributes: [
        :task_url,
        :taskproject_id
        ])[:taskrelationships_attributes]["0"]

      params[:task].delete(:taskrelationships_attributes)
      params.require(:task).permit(
        :name,
        :user_id,
        taskrelationships_attributes: [
          :task_url,
          :taskproject_id
        ]
      ).merge(owner: current_user)
    end

UPDATE W / ROUTES

resources :projects do
    resources :reviews, except: [:destroy]
    member do
      get :tasks
    end
  end

  resources :tasks
  resources :taskrelationships, only: [:create, :destroy] do
    post :vote, on: :member, controller: "task_relationships"
  end

thanks for any assistance...

like image 716
BB500 Avatar asked Oct 30 '16 20:10

BB500


1 Answers

Ok firstly an explanation as to what is going on here:

When you invoke http://localhost:3000/task/new?project_id=5 you are actually being routed to the new action on the task controller (with a project_id param).

Your new action then sets the variables and rails will render the new.html.erb which contains your new task form.

When you submit the form it actually is doing a http POST to /tasks, which routes to the create action of your tasks controller. That url and http method is a result of what is generated from the form_for helper:

<%= form_for @task, :html => {:multipart => true} do |f| %>

This is why the url changes from /tasks/new?project_id=5 to /tasks

Now the create action if it fails the validation simply renders the new form - it is not redirecting anywhere - the url remains unchanged from what it was when it entered this action - meaning, it remains as /tasks.

You do not actually need to navigate to /tasks/new?project_id=5 to render the new form but what you do need to do is set @project_id in the controller so the view has access to that variable (just as it does in the new action):

  def create
    @project = Project.find(params[:project_id])

    @task = Task.new(task_params)
    if @task.save
      flash[:success] = "This task has been added."
      @task.taskrelationships.create!(@taskrelationships_params)
      redirect_to tasks_project_path(@project)
    else
      @task.taskrelationships.build(@taskrelationships_params)
      @project_id = @project.id
      flash[:alert] = @task.errors.full_messages.to_sentence
      render :new
    end
  end

So, to clarify the change in url is not a redirection it's just that the form is posting to a different url than /tasks/new, and this is actually just a cosmetic issue.

Now if it is a concern to you, you can change the routing to something like the following:

  resources :tasks, except: [:create, :new]

  post     'new_task' => 'tasks#create'
  get      'new_task' => 'tasks#new'

This is mapping the POST and GET http methods to /new_task so the url appears the same for the new and create action invocations. Note you do need to change the url in the form_for helper to use this new route:

<%= form_for @task, url: 'new_task', multipart: true do |f| %>
like image 146
David Avatar answered Sep 27 '22 23:09

David