Rendering Error Messages in Rails Form with Javascript - javascript

I've got a form that should render error messages but I'm not sure how to render the error messages since my form is being displayed with Javascript after clicking a button. I'm using Devise for the registration which is working just not displaying the error messages. I'm using Rails 4.
This is the code that hides the register button after it's clicked and shows the form:
<script type="text/javascript">
$(function() {
$('#prelaunchbutton').click(function() {
$('#prelaunchregistrationbutton').fadeToggle('slow');
$('.prelaunchhide').fadeToggle('slow');
return false;
});
});
</script>
Here is the Devise registration form (Shortened for brevity):
<div class="prelaunch_preregisterbutton" id="prelaunchregistrationbutton">
<%= link_to image_tag("pre-register.png", :border=>0), '#', id:'prelaunchbutton' %>
</div>
<div class="span10 offset1 prelaunchhide">
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :class => "form-inline") do |f| %>
...
<% end %>
</div>
Here is the Devise registration controller:
class RegistrationsController < Devise::RegistrationsController
before_filter :authenticate_user!
respond_to :html, :json
def new
end
def create
#user = User.new(user_params)
#user.build_store(store_params)
if #user.save
UserMailer.preregister_confirmation(#user).deliver
sign_in #user
redirect_to prelaunch_gear_path, :flash => {:success => "Welcome"}
else
render root_path
end
end
...
protected
def after_update_path_for(resource)
user_path(resource)
end
private
def user_params
params.require(:user).permit(:firstname, :lastname, :email, :password)
end
def store_params
params.require(:store).permit(:storename)
end
end

You might look into using toastr for displaying javascript notifications.
Here's an SO question I answered before that may help set it up: Rails 4 - Toaster notifications rather than flash notifications -- you would just need to make your flash notification be flash.now[:toastr] and then be sure to call the initializer for that in the JS returned by the AJAX request.

Related

Rails Ajax Jquery

I'm working on an dynamic edit of an article using rails. On each articles can be added paragraphs. I wanted to use Ajax with Jquery following the 136-jquery-ajax-revised tutorial from railscast. I created the template for the form and the new.js.erb response but I keep geting same error:
ParagraphsController#new is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: []
This is the link that request the new form from view/article/show
<%= link_to 'New Paragraph', new_article_paragraph_path(#article), remote: true, class: 'uk-button uk-button-primary' %>
view/paragraph/_form
<div class="article-form-container">
<%= form_with scope: :paragraph, url: article_paragraphs, remote: true do |f| %>
<%= f.text_field :title, class: 'uk-input', placeholder: 'Title of paragraph (optional, can be a subtitle of the article)' %>
<%= f.text_area :text, class: 'uk-textarea', placeholder: 'Content of paragraph' %>
<%= f.hidden_field :position, :value => 3 %>
<div class="submit-button">
<%= f.submit class: 'uk-button uk-button-primary' %>
</div>
<% end %>
</div>
routes
resources :articles, only: %i[show update destroy create]
resources :articles do
resources :paragraphs, only: %i[new create update destroy]
end
view/paragraphs/new.js.erb
$('div#article-control-panel').hide("fast", function () {
$('div#article-control-panel').after('<%= j render 'form' %>')
})
controllers/paragraphs_controller
class ParagraphsController < ApplicationController
def new
#paragraph = Paragraph.new
end
def create
#paragraph = Paragraph.new(paragraph_params)
#article = Article.find(params[:article_id])
if #article.user == current_user
#paragraph.article = #article
#paragraph.save
end
respond_to do |format|
format.html { redirect_to #article }
format.js
end
end
def paragraph_params
params.require(:paragraph).permit(:title, :text, :position)
end
end
Can't figure out what the problem is. The error happens in the article page, after I press the link button.
Edit
Strange thing is that if i try to change the url of orm in something like article_path it will work...
Your controller is missing a respond_to. Your new function should look like this:
def new
#paragraph = Paragraph.new
respond_to { |format| format.js }
end
This respond_to allows rails to call the relevant .js.erb file, in this instance your new.js.erb file.
Just keep in mind that when you want to perform an ajax call, you require these few elements:
A respond_to { |format| format.js } in your controller action
Your js.erb file needs to be the same name as your controller action (foo.js.erb)
A partial to render through your js.erb file, typically named the same as your js file. (_foo.js.erb)
If it is a link, you need remote: true. If it is a form and you're using rails 5, you could use form_with or simply include remote: true too.

js.erb files not rendering partials due to a failure to pass them locals from multiple controllers

I'm having trouble passing locals to a partial shared by three different views, each related to different actions in different controllers. The partial and the locals passed to it work without a problem when working with html requests, but I cannot get them to work when issuing xhr requests.
Let me show you my code to explain myself better.
So, I have this partial
# app/views/shared/_vote_form.html.erb
<div id="vote_form">
<% if post.votes.find_by(user_id: current_user.id).nil? %>
<%= render partial: "votes/vote", locals: { postv: post,
vote: post.votes.build } %>
<% else %>
<%= render partial: "votes/unvote", locals: { postu: post,
vote: post.votes.find_by(user_id: current_user.id) } %>
<% end %>
</div>
As you can see, this partial renders one of two partials depending on the outcome of an if statement:
# app/views/votes/_vote.html.erb
<%= form_for([postv, vote], remote: true) do |f| %>
<%= hidden_field_tag 'vote[vote]', 1 %>
<%= f.submit "Vote", class: "btn" %>
<% end %>
# app/views/votes/_unvote.html.erb
<%= form_for([postu, vote], html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Unvote", class: "btn btn-primary" %>
<% end %>
As I mentioned, this partials are shared by three different views associated to different actions in different controllers.
# app/views/posts/show.html.erb
<div class="post-vote-form">
<%= render partial: "shared/vote_form", locals: { post: #post } %>
</div>
Which is associated to the following action in the following controller:
class PostsController < ApplicationController
def show
#post = Post.find(params[:id])
end
end
Another view
# app/views/users/feed.html.erb
<% #feed_items.each do |f| %>
<div class="vote-button">
<%= render partial: "shared/vote_form", locals: { post: f } %>
</div>
<% end %>
Associated to the following controller#action
class UsersController < ApplicationController
def feed
#user = User.find_by(id: current_user.id)
#feed_items = #user.feed
end
end
And finally
# app/views/categories/other.html.erb
<% #gal_items.each do |f| %>
...
<%= render partial: "shared/vote_form", locals: { post: f } %>
<% end %>
Associated to controller#action
class CategoriesController < ApplicationController
def other
#other = Category.find(7)
#gal_items = #other.posts
end
end
As you can see, the forms send an xhr request to create/destroy an instance of Vote (I have routes for Vote nested in Post. That's why the form_for takes two arguments).
These requests are handled by the following actions in the VotesController
class VotesController < ApplicationController
def create
#post = Post.find_by(id: params[:post_id])
#vote = #post.votes.build(vote_params)
#vote.user_id = current_user.id
#vote.save
respond_to do |format|
format.html { redirect_to #post }
format.js
end
end
def destroy
#vote.destroy
respond_to do |format|
format.html { redirect_to #post }
format.js
end
end
end
And these two js.erb files come into play:
# app/views/votes/create.js.erb
$("#vote_form").html("<%= escape_javascript(render :partial => 'votes/unvote', locals: { postu: #post,
vote: #post.votes.find_by(user_id: current_user.id)}) %>");
And
# app/views/votes/destroy.js.erb
$("#vote_form").html("<%= escape_javascript(render :partial => 'votes/vote', locals: { postv: #post.each,
vote: #post.votes.build }) %>");
The way I am presenting these last two js.erb files work for the view # app/views/posts/show.html.erb as the values for the locals are taken directly from the VotesController actions, but I have not been able to find a way to make it work for the other two views (which are #something.each do |f|) that render these partials, as I cannot pass the appropriate values to the locals for the form_for arguments to work.
I have tried with a helper to pass values to the locals depending on the url, but without success.
It seems obvious that I cannot get these js.erb files to render the partials with appropriate values for the locals because I cannot retrieve the variables from their respective controllers.
So, bottomline, is there a way to make it work through these js.erb files, or will I have to sort this out using pure JQuery?
Has anyone faced something like this?
I am sorry that I cannot make a question that requires a more specific answer.
Hope you guys can help.
Did you try this?
class UsersController < ApplicationController
def feed
#post = Post.find(params[:id])
#user = User.find_by(id: current_user.id)
#feed_items = #user.feed
end
end
class CategoriesController < ApplicationController
def other
#post = Post.find(params[:id])
#other = Category.find(7)
#gal_items = #other.posts
end
end

Rails wicked wizard with javascript

I want to create a wizard in js.
steps :first_step,
:second_step
In my 'controller_step'
def show
case step
when :first_step
#r = R.new
when :second_step
end
render_wizard
end
def update
case step
when :first_step
#r = R.new(r_params)
when :second_step
end
render_wizard #r
end
I have problems after the update of the first step. I'm receive the following error message:
"Missing template controller_step/second_step, application/second_step
with {:locale=>[:en], :formats=>[:html], :variants=>[],
:handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. ".
How can I force loading of js templete? I would load "second_step.js.erb".
I tried to change the update method:
respond_to do |format|
format.js { render :js => ( render_wizard #r ) }
end
Of course I get the following error:
"AbstractController::DoubleRenderError in ...Controller#update Render
and/or redirect were called multiple times in this action. Please note
that you may only call render OR redirect, and at most once per
action. Also note that neither redirect nor render terminate execution
of the action, so if you want to exit an action after redirecting, you
need to do something like "redirect_to(...) and return"."
I also tried to change the code (in update):
respond_to do |format|
format.js { render :js => ( render_wizard #room_types and return ) }
end
I'm get the same error ( ... application/second_step with {:locale=>[:en], :formats=>[:html] .... )
P.S.
In view of the first step:
<%= form_for(#r, url: wizard_path, method: :put, remote: true) do |f| %>
....
<%= f.submit "Submit", class: "btn btn-default" %>
<% end %>
How do I fix ? thanks in advance
The #render_wizard method defined in the Wicked::Controller::Concerns::RenderRedirect is a wrapper method around the ActionController::Base#render method. It
accepts a hash of options and passes it down to the controller's regular #render method.
This is the source code from the Wicked library:
def render_wizard(resource = nil, options = {})
...
if #skip_to
...
else
render_step wizard_value(step), options
end
end
def render_step(the_step, options = {})
if the_step.nil? || the_step.to_s == Wicked::FINISH_STEP
...
else
render the_step, options #<-- Here
end
end
Your code:
respond_to do |format|
format.js { render :js => ( render_wizard #r ) }
end
is basically doing:
respond_to do |format|
format.js { render :js => ( render #r ) }
end
which is in fact calling the render method twice.
As it is searching for a .html template rather than a .js.erb one, try adding a formats: 'js' option to the render_wizard method. It should prepend ['js'] to the :formats=>[:html] we see in the Missing template error message.
respond_to do |format|
format.js { render_wizard(#r, formats: 'js') }
end
also, make sure the template's filename follows the rails convention and start with a _. (ie: _second_step.js.erb)
About the double render error, you are correct. You must return from the controller #show or #update method and prevent further code from calling the #render method a second time. You seem to have fixed that problem already.
EDIT#1
It seems like you may be able to call this method directly in your controller.. :
def render_step(the_step, options = {})
# Wicked::FINISH_STEP = "wicked_finish"
if the_step.nil? || the_step.to_s == Wicked::FINISH_STEP
redirect_to_finish_wizard options
else
render the_step, options
end
end
I believe the_step will be the partial's name. I think you should be able to call the #render_step method from your controller.
You may be able to do:
def show
respond_to do |f|
f.js do
case step
when :first_step
#r = R.new
render_step(step) and return
when :second_step
...
end
end
end
end
I solved in this way:
in r model:
validates :x, presence: true
in step controller :
...
steps :first_step,
:second_step,
:finish_step
...
def show
case step
when :first_step
#room_types = R.new
end
render_wizard
end
def update
case step
when :view_new_room_type_step
#r = R.create(r_params)
when :view_desc_step
#r = R.find(params[:r][:id])
#r.update(r_params)
end
respond_to do |format|
if !#r.errors.any?
format.js { !next_step?(:finish_step) ? ( render next_wizard_path ) : ( redirect_to_finish_wizard ) }
else
format.js { render wizard_path, r: #r }
end
end
end
private
...
def redirect_to_finish_wizard
redirect_to r_index_path , notice: "Ok"
end
in first_step.js.erb
$("#modal").html("<%= escape_javascript(render 'form_first_step') %>")
in _form_first_step.html.erb:
<% if #r.errors.any? %>
<div class="alert fade in alert-danger alert-dismissable"><button aria-hidden="true" class="close" data-dismiss="alert" type="button">×</button>
<ul>
<% #r.errors.full_messages.each do |msg| %>
<%= content_tag :li, msg, :id => "error_#{msg}" if msg.is_a?(String) %>
<% end %>
</ul>
</div>
<% end %>
<%= form_for(#room_types, url: wizard_path, method: :put, remote: true) do |f| %>
...
<% end %>
in second form ( of second step ):
<%= form_for(#room_types, url: wizard_path(:second_step), method: :put, remote: true) do |f| %>
....
<%= f.hidden_field :id %>
<% end %>
Idea: to validate the data in the steps you can use jquery-form-validator-rails

Ruby on rails AJAX submit form error

So I'm trying to update my comments section with AJAX without the full page refresh for a college project. However I can't seem to get this working.
In the console it gives me s
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
My show.html.erb file:
<h1>Title: <%= #post.title %></h1>
<h2>Body: <%= #post.body %></h2>
<hr />
<h1>Your comments</h1>
<%= link_to "View comments", "#", id: "comments-link" %>
<ol id="comments">
<%= render 'comments' %>
<hr />
<h1>Create comment</h1>
<%= form_for(#comment, :html => {class: "form", role: "forms"}, :url => post_comments_path(#post), remote: true) do |comm| %>
<div class="container">
<div class="input-group input-group-md">
<%= comm.label :body %>
<%= comm.text_area :body, class: "form-control", placeholder: "Enter a comment" %>
</div>
<%= comm.submit "custom", id: "button" %>
</div>
<% end %>
</ol>
My comments.coffee:
$(document).on "page:change", ->
$('#comments-link').click ->
$('#comments').fadeToggle()
$('#comments_body').focus()
My create.js.erb:
$('#comments').append("<%= j render #comment %>");
and my Comments controller:
class CommentsController < ApplicationController
def index
end
def new
end
def new
#comment = Comment.new
end
def create
#comment = Comment.new(comment_params)
#comment.post_id = params[:post_id]
if #comment.save
flash[:success] = "Successfully created comment"
respond_to do |format|
format.html { redirect_to post_path(#comment.post_id) }
format.js
end
else
flash[:danger] = "Failed to create comment"
redirect_to root_path
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
I may have missed some files so just let me know, it is basic as it's just a post and comment system - no styling needed for the project, so yeah. I have been trying this for the last 4 hours and other places just don't work. I've looked on here, Youtube - everywhere however no one else's code works for me so I have come here! Thank's for you're help.
EDIT:
I noticed it said to create a view in the error response, however I made that view and rendered the comment's body onto the create.html.erb however I just need to display the form now.
I notice you are posting to the url post_comments_path(#post). For nested routes, it might be cleaner to do the following:
1) Post to the nested route directly:
<%= form_for([#post, #comment], html: {class: "form", role: "forms"}, remote: true) do |comm| %>
2) Make sure your routes are nested properly, in routes.rb:
resources :posts do
resources :comments
end
3) Refactor your CommentsController, to build through the #post instance:
class CommentsController < ApplicationController
before_action :get_post
def index
#comments = #post.comments
end
def new
#comment = #post.comments.build
end
def create
#comment = #post.comments.build(comment_params)
if #comment.save
flash[:success] = "Successfully created comment"
respond_to do |format|
format.html { redirect_to post_path(#post) }
format.js
end
else
respond_to do |format|
format.html { render :new }
format.js { render :error }
end
end
end
private
def comment_params
params.require(:comment).permit(:body)
end
def get_post
#post = Post.find(params[:post_id])
end
end
4) Render the validation errors in app/views/comments/error.js.erb. I'll let you decide how best to do that, but here's a quick dump to the console:
<% #comment.errors.full_messages.each do |message| %>
console.log("<%= message %>");
<% end %>
This file is not to be confused with your app/views/comments/create.js.erb which is handling successful save of the comment. That should look something like this:
$('#comments').append("<%= j render #comment %>");
$(".form")[0].reset();
5) Tweak your view a little bit. I notice you need to output the comments differently:
<ol id="comments">
<%= render #post.comments %>
</ol>
which should correspond to a partial in app/views/comments/_comment.html.erb so make sure this is there.

Ruby on Rails file uploading using a form with remote: true

I am trying to upload files using a Rails form where the remote is set to true. I'm using Rails 4.1.1. Let's say that my model is a Message, and it is using JavaScript so that the user could easily send multiple messages without reloading the page. The form is set like this:
<%= form_for #message, url: {action: "create"}, html: {:class => "message-form", multipart: true}, remote: true do |f| %>
The user can upload images with the Message, if they wish to do so. MessageImage acts as a nested attribute in the form, and is declared like this (http://railscasts.com/episodes/196-nested-model-form-revised way):
<%= f.fields_for :message_images do |builder| %>
<%= render 'message_image_fields', f: builder %>
<%= link_to_add_fields "Add an image", f, :message_images %>
<% end %>
On my controller the action is roughly like this:
if #message.save
flash.now[:success] = "Message sent"
else
flash.now[:alert] = "Error sending the message"
end
respond_to do |format|
format.html { render 'new' }
format.js { render 'new' }
end
Now, this works perfectly as long as the user doesn't send any images, but if they do, it uses format.html instead of format.js. Removing the format.html gives ActionController::UnknownFormat-exception.
Now, this obviously has to do with the fact that you can't submit files with remote set to true. I tried searching a bit, and found this gem https://github.com/JangoSteve/remotipart , which seems to be exactly what I'm looking for. I installed it following the instructions, but for some reason it still doesn't work and gives ActionController::UnknownFormat-exception if I remove the format.html. However, I couldn't find any example of it involving nested attributes. Are there any alternatives for this gem or any other way to fix this, or should I just set that it renders HTML if the user submits files?
JQuery
I don't know how to get the nested model aspect of this, but we've done file uploading with JQuery / asynchronicity before here (register for account, log into profile):
We used the jquery-file-upload gem - basically allowing you to pass the files through Ajax to your controller backend. To give you a clear idea of how we did this:
--
Code
#app/assets/javascripts/application.js
$('#avatar').fileupload({
url: '/profile/' + $(this).attr('data_id'),
dataType: 'json',
type: 'post',
add: function (e, data) {
$(this).avatar_loading('avatar_loading');
data.submit();
},
success: function (data, status) {;
$("#avatar_img").fadeOut('fast', function() {
$(this).attr("src", data.avatar_url).fadeIn('fast', function(){
$(this).avatar_loading('avatar_loading');
});
});
}
});
#app/views/users/index.html.erb
<%= form_for :upload, :html => {:multipart => true, :id => "avatar"}, :method => :put, url: profile_path(current_user.id), "data_id" => current_user.id do |f| %>
<div class="btn btn-success fileinput-button avatar" id="avatar_container">
<%= f.file_field :avatar, :title => "Upload New" %>
<%= image_tag(#user.profile.avatar.url, :width=> '100%', :id => "avatar_img", :alt => name?(#user)) %>
</div>
<% end %>
#app/controllers/profile_controller.rb
Class ProfileController < ApplicationController
def update
def update
#profile = User.find(current_user.id)
#profile.profile.update(upload_params)
respond_to do |format|
format.html { render :nothing => true }
format.js { render :partial => 'profiles/update.js' }
format.json {
render :json => #profile.profile.as_json(:only => [:id, :avatar], :methods => [:avatar_url])
}
end
def upload_params
params.require(:upload).permit(:avatar, :public, :description)
end
end
end
--
Implementation
For your implementation, I would recommend firstly creating the message, and then getting the user to append some images to it in another action
After you've got that working, you could get it to work as one form

Categories

Resources