I am trying to submit a form via remote: true in rails 3 and for some reason when I look at the response in the browser I only see the raw response instead of the JavaScript being interpreted.
Form:
<%= form_for #entry, url: contest_entries_path, remote: true, html: {id: "contest_form"} do |f| %>
Controller:
class ContestEntriesController < ApplicationController
respond_to :html, :js
def index
#entry = ContestEntry.new
#entry.build_school
respond_with #entry
end
def create
#entry = ContestEntry.new(params[:contest_entry])
respond_with #entry
end
end
Create.js.erb:
<% unless #entry.errors.any? %>
<% if #entry.parent? %>
$('body').find('#parents_message').show();
<% else %>
$('body').find('#falculty_message').show();
<% end %>
<% end %>
The response in the browser is the raw JavaScript response
Related
I have a rails 7.0 application, in my application.html.erb
<body>
<%= yield %>
<div class="signin-container">
<div class="signin-container-inner">
<%- flash.each do |name, msg| -%>
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
<%- end -%>
</div>
</div>
</body>
When I visit signin page and add some wrong email or password the error message is not showing.
Since Ruby on Rails 7 uses :turbo_stream, we need to make some modifications to get what you need.
First, we let's add a new parent controller for Devise:
# frozen_string_literal: true
# app/controllers/turbo_devise_controller.rb
class TurboDeviseController < ApplicationController
class Responder < ActionController::Responder
def to_turbo_stream
controller.render(options.merge(formats: :html))
rescue ActionView::MissingTemplate => e
if get?
raise e
elsif has_errors? && default_action
render rendering_options.merge(formats: :html, status: :unprocessable_entity)
else
redirect_to navigation_location
end
end
end
self.responder = Responder
respond_to :html, :turbo_stream
end
Second, we also need to tell Devise to use our new controller and also add a class to handle our errors:
# frozen_string_literal: true
# app/config/initializers/devise.rb
# ! Create custom failure for turbo
class TurboFailureApp < Devise::FailureApp
def respond
if request_format == :turbo_stream
redirect
else
super
end
end
def skip_format?
%w(html turbo_stream */*).include? request_format.to_s
end
end
Devise.setup do |config|
...
config.parent_controller = 'TurboDeviseController'
config.navigational_formats = ['*/*', :html, :turbo_stream]
config.warden do |manager|
manager.failure_app = TurboFailureApp
end
...
end
That's it.
More information about it: GoRails - How to use Devise with Hotwire & Turbo.js
Here is a workaround this issue that occurs when using Rails 7, Hotwire, Turbo, and Devise together. By passing data:{turbo: false} along with each Devise form, it can prevent Turbo from conflicting with the Devise authentication process. This should allow Devise to function properly while still using Hotwire and Turbo in your Rails application.
Here is an example of how you might use data:{turbo: false} on a Devise form:
<%= form_for(resource, as: resource_name, url: session_path(resource_name), data: {turbo: false}) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, autofocus: true %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit "Log in" %>
<% end %>
In this example, the data: {turbo: false} is being passed as an option to the form_for helper method. This tells Turbo to not apply any of its features to this form, which should prevent any conflicts with Devise's authentication process.
I can't figure out why I have this error from my knowledge everything is set up as it should to work.
this is the error i get:
NoMethodError at /short/new
undefined method `shorts_path' for #<#:0x007fbc441426e0>
my model is short not shortS
when I run rake routes there is no shorts_path so I'm not sure where this helper is coming from. I don't understand why the form_for is giving me this error when #short is defined in the def new section of my controller.
Can someone please explain this to me?
Thank you in advance
This is what my controller looks like
class ShortController < ApplicationController
def show
#short = Short.find(params[:id].to_i(36))
respond_to do |format|
#redirect directly to the url stored as long in the database
format.html { redirect_to #short.long}
end
end
def new
#short = Short.new
respond_to do |format|
format.html # new.html.erb
end
end
def create
#short = Short.new(params[:short])
respond_to do |format|
if #short.save
format.html { render action: "show" }
else
format.html { render action: "new" }
end
end
end
end
routes
Trainingproject::Application.routes.draw do
root :to => 'short#welcome'
resources :short, :only => [:new, :create, :show]
end
form partial which is rendered in the view for new
<%= form_for(#short) do |f| %>
<% if #short.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#short.errors.count,"error") %> prohibited this url from being saved:</h2>
<ul>
<% #short.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-field">
<%= f.label "Enter your URL" %><br />
<%= f.text_field :long %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You're getting this error because form_for(#short), where #short is a new Short record, will attempt to use the path helper called shorts_path to route to where the form needs to go. You're missing this route definition in your config/routes.rb file:
resources :shorts
Please read the Getting Started and Routing guides, which should explain the things that you're missing.
I have watched the railscasts on Pagination with AJAX. I am trying to duplicate his method using a nested model. I seem to be confused because i don't have a view for the child model or a fully integrated controller.
Assume the Product has_many Comments. How can I do the same pagination using will_paginate on comments within a product view?
UPDATE:
Here is most of my attempt (my attempt actually uses kaminari but it's basically the same as will_pagenate):
view/products/comments/_index.html.erb:
<div id="comments">
<% comments.each do |comment| %>
<%= f.fields_for :comments, comments do |comment_builder| %>
<%= render 'products/comments/field', f: comment_builder %>
<% end %>
<% end %>
<%= paginate comments %>
</div>
view/products/comments/_field.html.erb:
<fieldset>
<%= f.hidden_field :_id, :value => f.object._id %>
<%= f.hidden_field :_destroy %>
<%= f.object.text %>
<%= link_to "remove", '#', class: "remove_field" %>
</fieldset>
view/products/comments/_index.js.erb:
$("#comments").html("j render("products/comments/index")) %>");
assets/javascripts/product.js
$('.pagination a').live
click: ->
$(".pagination").html("Page is loading...")
$.getScript(this.href)
false
controllers/comments_controller.rb
class CommentsController < ApplicationController
before_filter :get_product
def index
#comments = #product.comments.desc(:created_at).page(params[:page]).per(10)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #comments }
end
end
private
def get_product
#product = Product.find(params[:product_id]) if params[:product_id]
redirect_to root_path unless defined?(#product)
end
end
views/products/show.html.erb (an excerpt)
<%= render "products/comments/index", f: f, comments: #comments%>
controllers/products_controller.rb
def show
#product = Product.find(params[:id])
#comments = #product.comments.desc(:created_at).page(params[:page]).per(1)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #product }
end
end
view/products/comments/_index.js.erb never gets called. Also is there a more rails-like way of doing this?
I have a form in my project for a model Artworks where I am using fields_for for a resource Photo where Artworks has_many :photos.
The Photo model uses paperclip to attach images.
All my forms are AJAX based, meaning the page should never be reloaded, and I shouldn't be using redirect_to anywhere in my controllers (and I'm not).
When I include the fields_for in my form with the paperclip file_field, if I create or update the photo when I submit the form, I get the following error:
Template is missing
Missing template artworks/show, application/show with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]}. Searched in: * "/home/david/rails/piml_artwork/app/views" * "/home/david/.rvm/gems/ruby-1.9.3-p194/gems/devise-2.1.2/app/views"
The destroy image checkbox functions as expected. Updating the rest of the details on the form and submitting it functions as expected, just returning an AJAX response and filling out the page.
My artworks controller is as follows:
class ArtworksController < ApplicationController
before_filter :load
authorize_resource
respond_to :html, :json, :js
def load
end
def show
#artwork = Artwork.find(params[:id])
end
def index
#artworks = Artwork.all
end
def new
#artwork = Artwork.new
#artwork.photos.build unless #artwork.photos.count > 0
end
def create
#artwork = Artwork.new(params[:artwork])
if #artwork.save
flash[:notice] = "Artwork saved"
#artworks = Artwork.all
respond_with #artwork
end
end
def edit
#artwork = Artwork.find(params[:id])
#artwork.photos.build unless #artwork.photos.count > 0
end
def update
#artwork = Artwork.find(params[:id])
if #artwork.update_attributes(params[:artwork])
flash[:notice] = "Artwork updated"
#artworks = Artwork.all
respond_with #artwork
end
end
def destroy
#artwork = Artwork.find(params[:id])
#artwork.destroy
flash[:notice] = "Artwork destroyed"
#artworks = Artwork.all
end
end
The form causing the strife is this:
<%= form_for( #artwork, remote: true, mutlipart: true ) do |f| %>
<%= render 'layouts/error_messages', object: f.object %>
<div id="artwork-title">TITLE: <%= f.text_field :title %></div>
<div id="artwork-title-underline"></div>
<div id="artwork-show-picture"><%= image_tag #artwork.photos.first.photo.url(:medium) unless #artwork.photos.first.new_record? %>
<div id="artwork-picture-upload">
<%= f.fields_for :photos, remote: true, multipart: true do |p| %>
<% if p.object.new_record? %>
UPLOAD PHOTO
<%= p.file_field :photo, value: 'Upload Image' %>
<% else %>
REPLACE PHOTO
<%= p.file_field :photo, value: 'Replace Image' %>
<br />
<br />
<%= p.check_box :_destroy %>
<%= p.label :_destroy, 'Delete Image' %>
<% end %>
<% end %>
</div>
</div>
<div id="artwork-public">
ARTIST<br />
<%= f.select :artist_id, Artist.select_list, include_blank: "SELECT ARTIST" %><br />
<br />
DATE OF WORK <br />
<%= f.text_field :date_of_work %><br />
<br />
<div id="artwork-modify-buttons">
<%= f.submit %>
</div>
<% end %>
My JS file for update is:
<% if !#artwork.errors.any? %>
$("#facebox .content").html("<%= escape_javascript(render(partial: "/artworks/show")) %>");
$("#artworks-col").html(" <%= escape_javascript(render(partial: "/admin/artworks_list" )) %>")
<% else %>
$("#facebox .content").html("<%= escape_javascript(render(partial: "/artworks/form")) %>");
<% end %>
As it turns out, the issue I was having is that by design, you cannot upload files through AJAX.
After a few days of toiling with this, I found a gem that fixes it all incredibly easily; Remotipart
The entirety of what I had to do to fix it was:
Add remotipart to my gem file gem 'remotipart'
Add //= require jquery.remotipart to my app/assets/javascripts/application.js file
bundle install
That fixed it. Images are now uploading with what 'looks like' AJAX, but is actually using an iframe to do the work.
I have a project that contains projects that have todos that have tasks. When I try to create a new task, I get this error when I submit:
No route matches [POST] "/projects/1/todos/19/tasks/new"
Here is my form:
<%= form_for [#todo, #todo.tasks.new], :url => new_project_todo_task_path(#project, #todo) do |f| %>
<div class="field">
<%= f.label :description, "Description" %><br />
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %> or <%= link_to "Cancel", "#", :id => "cancel_new_task_link" %>
</div>
<% end %>
Here is my controller:
class TasksController < ApplicationController
before_filter :authenticated?
before_filter :get_project_and_todo
respond_to :html, :xml, :json, :js
def new
#task = #todo.tasks.new
end
def create
#task = #todo.tasks.new(params[:task])
if #task.save
respond_with #todo, :location => project_todo_path(#project, #todo)
else
render "new"
end
end
private
def get_project_and_todo
#project = Project.find(params[:project_id])
#todo = #project.todos.find(params[:todo_id])
end
end
Here are my routes:
resources :projects do
resources :todos do
resources :tasks
end
end
Thanks
Your URL should not be new_project_todo_task_path(#project, #todo). You don't need to specify the URL here as Rails will imply it from the parameters passed in to form_for.
If the final object is a new object and not persisted in the database then it will make a POST request to, in this case, /projects/:project_id/todos. You're declaring in your example that you want to make a POST request to /projects/:project_id/todos/new, for which there is no POST route and that is why it's failing.