I am trying to render a partial with ajax when submitting a form.
Here is my code:
index.html.erb
<% #inbox.each do |conversation| %>
<div class="message">
<div id="messages">
<%= render conversation.messages %>
</div>
<div class="inner-message">
<%= form_tag({controller: "conversations", action: "reply", id: conversation.id}, {remote: true, method: :post}) do %>
<%= hidden_field_tag :recipient_id, current_user.id %>
<%= hidden_field_tag :subject, "#{current_user.name}" %>
<div class="form-group">
<%= text_area_tag :body, nil, class: "form-control", placeholder: "Odgovori" %>
</div>
<div class="form-group">
<%= submit_tag 'Pošlji', class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
<% end %>
index.js.erb
$("#messages").html("<%= escape_javascript(render conversation.messages) %>")
conversations_controller.rb
def reply
conversation = current_user.mailbox.conversations.find(params[:id])
current_user.reply_to_conversation(conversation, params[:body])
respond_to do |format|
format.html { redirect_to messages_path }
format.js { redirect_to messages_path }
end
end
when I submit the form, I get an undefined local variable error:
ActionView::Template::Error (undefined local variable or method
`conversation' for #<#:0x007fd287172fa8>)
How do I pass the local variable from the loop to the .js.erb view?
Thanks!
I usually don't do much rendering of js in applications so I'm a bit rusty on the specifics. However there are a couple of problems with your code.
First by issuing a redirect your instructing the browser to load a new url . Any variables such as 'conversation' that you would have set would be forgotten.
As the Stan Wiechers alluded you need to use an instance variable (e.g. #conversation) if you want to preserve conversation for the view. Unfortunately that won't help you in this case because of the redirect which wipes out all variables not stored in the session, cookies, or flash hash.
What I think you want to do is render your partial in stead of redirecting. Typically when you are using ajax you don't want to reload the page on the server side. In Rails you would typically render json or in your case a js partial.
Try
format.js{render partial:[PARTIAL NAME], locals:{conversation: conversation} }
This will render the partial without redirecting and will pass your local variable. If you change 'conversation', to #conversation then you can leave off the locals:{conversation: conversation} but your partial should reference
#conversation
not
conversation
hope that helps
Related
I created a button where users can input stuff in a field and then press the button to update the database (put request) which can be seen here in show.html.erb:
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
<br>
<%= #user.email %>
<% if #errors %>
<p>THE FORM COULD NOT BE SAVED </p>
<ul id='errors'>
<% #errors.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>
<br>
<% if is_admin? %>
<% if !#user.admin %>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user) do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", :action => "set_wistia_project_ID", :method => :patch, :form_class => "form-control" %>
<% end %>
</div>
</div>
<% end %>
<% end %>
</h1>
</section>
</aside>
</div>
The function is in user_controller.rb:
# Sets wistia_project_ID.
def set_wistia_project_ID
#user = User.find(params[:id])
#user.set_project_id
unless #user.valid?
#errors = #user.errors.full_messages
render :show
end
end
That function calls another function, just to separate things more clearly. This other function lives in user.rb:
# Sets the wistia_project_ID.
def set_project_id!(val)
self.wistia_project_ID = val # self is necessary here
save # or self.save, but the self is unnecessary here
end
My routes.rb:
.
.
.
resources :users do
member do
patch 'set_wistia_project_ID'
end
end
My problem is that right now, when you press the button, it says: Completed 500 Internal Server Error in 26ms (ActiveRecord: 0.7ms)
and
NoMethodError (undefined method `set_project_id' for #<User:0x000055b1a0914ab8>
2019-06-26T14:46:34.940086+00:00 app[web.1]: Did you mean? wistia_project_id):
Zavitoski got it right. I suggest, however, that you're doing a number of things more fundamentally wrong. Given that you're early in your rails journey, I hope you don't mind if I point a few things out.
First, and to be nit-picky, yes, you created a button. But, it is not a button "where users can input stuff in a field and then press the button to update the database". You created a button on a form. And you created a field on that form. The user can input stuff into the field. And when clicked, the button submits the form which includes the information in the field.
Now, on that form, you did:
<%= form_for(#user) do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", :action => "set_wistia_project_ID", :method => :patch, :form_class => "form-control" %>
<% end %>
There are a few things wrong with:
:action => "set_wistia_project_ID"
First, set_wisteria_project_ID is not a very ruby-ish action name. set_wistia_project_id would be more like it. Also, you're using old-form key-value formatting. And, you can use a symbol instead of a string for your action name so your code is prettier. Something, perhaps, like:
<%= f.submit "Save", action: :set_wistia_project_id, method: :patch, form_class: "form-control" %>
But, that's a mistake, too. Because you don't need a set_wistia_project_id action. (It's an action or a method, not a function.) You already have the update action. And form_for is smart enough to submit to this action if #user is an instance of User. So, really, you should do:
<%= form_for #user do |f| %>
<%= f.label :wistia_project_id %>
<%= f.text_field :wistia_project_id, class: 'form-control' %>
<%= f.submit "Save", form_class: "form-control" %>
<% end %>
I'm not sure what form_class is, but I'll trust that it's correct.
Now, in your UsersController, just do:
class UsersController < ApplicationController
def update
#user = User.find(params[:id])
if user.update(user_params)
# do something successful
else
# do something unsuccessful
end
end
private
def user_params
# NOTE: You'll probably want to permit other stuff here, too.
params.require(:user).permit(:wistia_project_id)
end
end
Get rid of this:
class User < ApplicationRecord
# Sets the wistia_project_ID.
def set_project_id!(val)
self.wistia_project_ID = val # self is necessary here
save # or self.save, but the self is unnecessary here
end
end
Because you're just duplicating the update method. And, you probably want that attribute to be wistia_project_id, not wistia_project_ID. (Again, you never see _ID as the suffix in rails core and you might as well be conventional.) And, if you make sure you have your association set up correctly, ActiveRecord should make sure that wistia_project_id is actually a valid value.
And write your routes.rb like this:
resources :users
Because you don't need all that set_wistia_project_id business.
It appears that you are not calling the function by the name you defined, neither passing the parameter (project_id) needed.
def set_wistia_project_ID
#user = User.find(params[:id])
#user.set_project_id!(params[:wistia_project_id])
unless #user.valid?
#errors = #user.errors.full_messages
render :show
end
end
This should use the function you created and pass the parameter from the form.
I decided to make a clone of Facebook in Rails. First I'm working on getting status updates working. I got it setup as the StatusUpdate model that is called by the Pages controller to render on the index page.
The issue I'm having is that if I use form_for(#status_update) I get:
undefined method to_key' for
<StatusUpdate::ActiveRecord_Relation:0x00000000049d3448>
Did you mean? to_set to_ary
If I use form_with(model: #status_update):
undefined method to_model' for
<StatusUpdate::ActiveRecord_Relation:0x000000000471cd80>
Did you mean? to_xml
If I use form_with(model: status_update):
undefined local variable or method status_update' for
<#<Class:0x0000000005801678>:0x0000000002ec8ec8>
Did you mean? #status_update
My action:
def create
#status_update = StatusUpdate.new(status_update_params)
respond_to do |format|
if #status_update.save
format.html { redirect_to root_path, notice: 'Status successfully posted!' }
else
format.html { render :new }
end
end
and erb view:
<%= form_with(model: status_update) do |sp| %>
<div class="form-group">
<%= sp.label :status_update %>
<%= sp.text_area :status_update, class: 'form-control', rows: 15, placeholder: 'Content' %>
</div>
<div class="form-group">
<%= sp.submit 'Submit', class: 'btn btn-primary' %>
</div>
<% end %>
I think you are missing the initialisation step. You have to first initialise the model object in new action of the controller.
def new
#status_update = StatusUpdate.new
end
and then use it in form.
form_with(model: #status_update)
The argument to form_with must be a single model instance. Not a whole collection.
class Pages
def index
#status_updates = StatusUpdate.all
#new_status_update = StatusUpdate.new
end
end
---
# app/views/pages/index.html.erb
<%= form_with(model: #new_status_update) %>
# ...
<% end %>
<%= #status_updates.each do |s| %>
# ...
<% end %>
Which is why you need to pay attention to pluralization when naming variables!
Another way to solve this is by using a condition:
# app/views/status_updates/form.html.erb
<%= form_with(model: local_assigns(:status_update) || StatusUpdate.new) %>
...
<% end %>
Which lets you use the form as a partial even without a StatusUpdate instance:
# app/views/pages/index.html.erb
<%= render partial: 'status_updates/form' %>
<%= #status_updates.each do |s| %>
# ...
<% end %>
I want to flash a notice/error if the email is/isn't saved, without using a redirect. I am using Rails 4 for the project, and here is my code:
layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>FarFlung Jobs</title>
<!-- /.You can include style.css into stylesheet_link_tag too. If you do so, dont forget to add style.css in asset.rb -->
<%= stylesheet_link_tag 'application', 'jobs', media: 'all' %>
<%= javascript_include_tag 'application' %>
<%= javascript_include_tag 'config', 'sharebutton', 'jobsalert', 'modernizr'%>
<%= render 'payola/transactions/stripe_header' %>
<%= csrf_meta_tags %>
</head>
<body>
<%= render 'shared/navbar' %>
<div>
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, class: 'alert alert-info' %>
<% end %>
</div>
<%= yield %>
<%= render 'shared/footer' %>
</body>
</html>
users/new.html.erb
<section class="cd-form-wrapper cd-container">
<div class="column panel panel-default">
<div class="cd-filter"><h4>SUBSCRIBE FOR JOBS ALERT</h4></div>
<%= simple_form_for User.new do |f| %>
<%= f.input :email, label: false, :placeholder => 'Enter your email address...', :input_html => { :class => 'newsletter-form-field-styling' } %>
<%= f.button :submit, 'SUBMIT', :class => 'btn-block newsletter-form-styling btn-primary submit' %>
<% end %>
</div>
</section>
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(secure_params)
if #user.valid?
#user.save
flash.now[:notice] = "#{#user.email} is signed up for Jobs Alert."
else
flash.now[:alert] = 'Error Subscribing!'
end
end
private
def secure_params
params.require(:user).permit(:email)
end
end
How do I get the Rails flash message working to appear on my subscription form without redirecting the page?
You can submit your form passing remote: true. It'll (as you can expect) remotely submit your form, so you can return an json an render the expected flash. Ex:
Your form:
simple_form_for User.new, remote: true, class: 'your-form' do
<your code>
Your controller
def create
#user = User.new(secure_params)
if #user.save
render json: { status: 'success', message: "#{#user.email} is signed up for Jobs Alert." }
else
render json: { status: 'failure', message: 'Error Subscribing!' }
end
end
Your JS (perhaps new.js - and be sure to include it in your view)
// you can specify your bind container, here I just used the document
$(document).on('ajax:success', '.your-form', function(e, data) {
if(data.status == 'success'){
showSuccessFlash(data);
}else{
showErrorFlash(data);
}
});
Explaining:
Using the remote: true, your page will wait for an ajax answer, which you can get listening to ajax:success (or other bindings).
Then, it will receive the json and will store in the data variable. So you will get the data.status and the data.message, which you can show to your user as a feedback.
More infor about the remote: true http://edgeguides.rubyonrails.org/working_with_javascript_in_rails.html#form-for
More info about the JS callbacks https://github.com/rails/jquery-ujs/wiki/ajax
You can use a gem like Gon that will monitor variables and handle all of the Ajax for you automatically.
It's very easy to setup, and you can have it perform the update at whatever time interval you choose. If you're simply transporting a variable, this will save you some Ajax coding. And it will update your page without any kind of redirect, so it does exactly what you're looking for.
However, one other tweak I figured out to solve this problem is with/without redirect is using application helper to make your Flash Global and used on any View of your choice.
Move your Flash Message to Partials e.g.
shared/_flash_messages.html.erb
<div class="text-center">
<% flash.each do |name, msg| %>
<%= content_tag :div, msg, class: 'alert alert-info' %>
<% end %>
</div>
Define custom helper method for your flash messages using the exact same old rails method use to have. Such that the method renders our partials and the object will be parsed along to your partials.
helpers/application_helper.rb
module ApplicationHelper
def flash_messages_for(object)
render(:partial => 'shared/flash_messages', :locals => {:object => object})
end
end
Inside my View, you can call it with erb tags and call the form object on it such as <%= flash_messages_for(User.new) %> or <%= flash_messages_for(#user) %>. See code below:
users/new.html.erb
<section class="cd-form-wrapper cd-container">
<div class="column panel panel-default">
<%= flash_messages_for(User.new) %>
<div class="cd-filter"><h4>SUBSCRIBE FOR JOBS ALERT</h4></div>
<%= simple_form_for(User.new) do |f| %>
<%= f.input :email, label: false, :placeholder => 'Enter your email address...', :input_html => { :class => 'newsletter-form-field-styling' } %>
<%= f.button :submit, 'SUBMIT', :class => 'btn-block newsletter-form-styling btn-primary submit' %>
<% end %>
</div>
With this, my error messages are flashed on any view or form I call <%= flash_messages_for(User.new) %> on.
You can refer to Kevin Skoglund formerrors Ruby on Rails Tutorials on Lynda.com Click to watch
I tried to set up a form which I wanted it to move to members#index.
But with the code①, I failed and the form moved to members#show.
☆code①
<%= form_tag :action => 'index' do %>
<div class = "field">
<%= label_tag 'place', '活動場所:' %><br />
<%= text_field_tag 'place' %>
</div>
<%= submit_tag '検索' %>
<% end %>
And I got some advice and fixed the code②.
I have a question. Why did I have to change the method from "post" to "get"?
☆code2
<div class= "form_index">
<%= form_tag({:action=>"index"}, {:method=>"get"}) do %>
<div class="from_field_index">
<%= label_tag 'place', '場所:' %>
<%= text_field_tag 'place' %>
</div>
<div class="search_button">
<%= submit_tag '検索' %>
<% end %>
</div>
</div>
☆members_controller
def index
if params[:place].present?
#members = Member.where("place like ?" , "%" + params[:place] + "%")
else
#members = Member.all
end
respond_to do |format|
format.html # index.html.erb
format.json
end
end
GET implies to retrieve something from server. POST implies to add something to server.
Search will get some results from server, so the conventional way is to use GET on this action. Search form is the perfect example of form using GET.
Also, your controller action index responds to 'GET' only, defined by default resource route. It also need the request sent by your client side to be 'GET'.
Another benefit of using GET on search is, the params will be in url so the url is bookmarkable, shareable and history nagivatable. Think about Google, you can share a search result by just copying the link.
I am new to rails so sorry if sometimes I don't make much sense. Here is what I am trying to do. I am trying to build a vote system. So next to a blog post there is a link that says 'vote' (will probably say like later). So far I have working: when the vote button is clicked, a value of '1' is delivered to the vote table and then that particular posts vote records display beneath the vote via AJAX (I copied a comment functionality). Instead of rendering all the number '1's below, I want it to render the updated count.
My vote table has the columns 'vote' and 'post_id' that are successfully being entered. My thinking was that I could just change my partial template to do this. Here is the code:
votes_controller:
class VotesController < ApplicationController
def create
#post = Post.find(params[:post_id])
#vote = #post.votes.create!(params[:vote])
respond_to do |format|
format.html { redirect_to #post}
format.js
end
end
end
def count
#post = Post.find(params[:post_id])
#vote = calculate :count
respond_to do |format|
format.html { redirect_to #post}
format.js
end
end
end
Here is the page where is is showing, /posts/show.html.erb:
<div id="backto"<%= link_to 'Back to all BattleCries', posts_path %></div>
<%= render :partial => #post %><br/>
<p5>Add a Comment</p5>
<div id="belt">
<div id="belttext">
<% remote_form_for [#post, Comment.new] do |f| %>
<p>
<%= f.text_area ( :body, :class => "commentarea") %>
</p>
<%= f.submit "Add Comment"%>
<% end %>
</div>
<div id="beltbottom">
</div>
</div><br/>
<br/><p5>Comment Stream </p5>
<div id="comments">
<%= render :partial => #post.comments %>
</div>
<p>
<% remote_form_for [#post, Vote.new] do |f| %>
<p>
<%= f.hidden_field :vote, :value => '1' %>
</p>
<%= f.submit "Vote" %>
<% end %>
<div id="vote">
<div id="votes">
<%= render :partial => #post.votes %>
</div>
</div>
</p>
Here is the :partial, /votes/_vote.html.erb: (this is where I thought I would just need to change it to vote.count, or post.count or something but can't get it to work).
<% div_for vote do %>
<%= h(vote.vote) %>
<% end %>
Here is the /votes/create.js.rjs file:
page.insert_html :bottom, :votes, :partial => #vote
page[#vote].visual_effect :highlight
I hope that all makes sense.
I think it's repeating because your .rjs is "inserting at the bottom" of the div instead of "replacing" ... you probably want page.replace_html
It would be better to have a DIV or SPAN tag that contains the number of votes for a post ... then have your .rjs file update the DIV's inner_html with the number of votes (which would be #post.votes.count) ... I don't think you really need a partial.
You probably want:
<%= #post.votes.count %>
You also probably want to use replace instead of insert_html - does that make sense? Insert is just adding more elements to the DOM whereas replace will replace the element.