Creating a chat component with ActionCable in Rails5. I learned today that render partial, even in .js.erb does supposedly doesn't work. render specifically is the issue. So now I'm trying to call a partial, and pass a ruby variable to it.
I have tried many things, currently this is how I'm trying to accomplish it:
chatbox.append(<%= j (Erubis::Eruby.new(File.read(File.join(Rails.root, 'app/views/msgs',
'_msg.html.erb'))).result(msg: #msg)) %>);
and the error is
undefined method `self_or_other' for #<Erubis::Eruby:0x007f832bbb8590>
So the partial is there, but the variable is not correct.
views/msgs/_msg.html.erb
<li class="<%= self_or_other(msg) %>">
<div class="avatar">
<%# if msg_interlocutor(msg).avatar.url(:thumb) == "thumb/missing.png" %>
<%= image_tag(msg_interlocutor(msg).avatar.url(:thumb), class: "convoPic")%>
</div>
</li>
I also tried:
chatbox.append(<%= j (Msg.new(File.read(File.join(Rails.root, 'app/views/msgs',
'_msg.html.erb'))).result(msg: #msg)) %>);
And get this error:
When assigning attributes, you must pass a hash as an argument.
Trying with render:
App.msgs = App.cable.subscriptions.create('MsgsChannel', {
received: function(data) {
var chatbox = "#chatbox_" + data.convo + " .chatboxcontent"
var id = data.convo;
var sender_id = data.user.id;
var reciever_id = data.receiver;
<%= #msg %> = data.msg
$(chatbox).append("<%= escape_javascript render(:partial => 'msgs/msg', :locals => { :msg => #msg }) %>");
chatbox.scrollTop(chatbox[0].scrollHeight);
},
renderMsg: function(data) {
console.log(data)
}
});
And error I receive:
undefined method `render' for #<#<Class:0x007fe976b5c180>:0x007fe9773ed470>
How do I properly call in a partial, pass it a ruby variable (with the msg record). Thank you!!
do you have a "messages_helper.rb" file with the following
module MessagesHelper
def self_or_other(message)
message.user == current_user ? "self" : "other"
end
def message_interlocutor(message)
message.user == message.conversation.sender ? message.conversation.sender : message.conversation.recipient
end
end
This is where it's defined.
Related
I got a situation, there is a Javascript variable I need pass to rails partial view.
For example in test.html.erb
<script type="text/javascript">
var array = <%= raw sort_section %>
for(i = 0; i < array.length; i++) {
$('#test').append("<%= j render :partial => 'section_in_panel', :locals => {:section => raw array[i]} %>");
}
</script>
But it keep throwing syntax error, I have try many ways like
{:section => j sort_section_js[i]} %>"
{:section =>" + sort_section_js[i] + "}%>"
I need to use that because I want to call ajax to change array dynamically.
Update
So, maybe I need to write a controller
def get_new_variable
...
return new_variable
end
Then in the test.html.erb
<script>
$('test').onclick(function(event) {
//write some ajax call
//get new_variable
$('#test').empty();
$('#test').append("<%= j render :partial => 'section_in_panel', :locals => {:section => raw new_variable} %>");
});
)</script>
Is that right direction?
As Alberto Juan wrote in comment, you can't use javascript variable in ruby code.
Ruby code will be interpreted before javascript and will not know what is the i.
Update: Your update will not work either as your using new_variable you get in your javascript code.
For using partial after ajax call, follow instructions in this link
Example:
items/index.html.erb
<div class="grid">
<% #categories.each do |cat| %>
<%= link_to cat.name, fetch_items_path(:cat_id => cat.id), :remote => true %>
<% end %>
<%= render partial: 'item_grid', locals: { items: #items} %>
</div>
routes.rb
get "/fetch_items" => 'items#from_category', as: 'fetch_items'
items_controller.rb
def index
#items = Item.all
#categories = Category.all
end
def from_category
#selected = Item.where(:category_id => params[:cat_id])
respond_to do |format|
format.js
end
end
views/items/from_category.js.erb
$("#items_grid").html("<%= escape_javascript(render partial: 'items_list', locals: { items: #selected } ) %>");
You can use gem gon or simply do something like this:
<script>
MyVars[:someVar] = <%= some_value %>;
</script>
But, I recommend you to separate your backend an frontend code and use JSON for passing data to JS.
My app has a live search implemented and it works properly. The problem that I have is that now I have a table with a lot of columns and I want to search through all elements. I want to have only one text input and a drop down that will select the column that I want to search in.
Searchable module
module Searchable
extend ActiveSupport::Concern
module ClassMethods
def search_for(present_column,record)
record.present? ? where(present_column+' LIKE ?', record+"%") : all
end
end
end
People controller
class PeopleController < ApplicationController
def index
#people = Person.page(params[:page]).per(5).search_for(params[:object_columns], params[:search])
end
The search engine
<%= form_tag do %>
<% valid_column_names = target.column_names.reject{|r| r == "created_at" || r == "updated_at" || r == "slug"} %>
<%= select_tag :object_columns, options_for_select(valid_column_names) %>
<%= text_field_tag :search, '', autocomplete: :off %>
<% end %>
The live search js
$(function () {
$("input#search").keyup(function () {
$.get($("#object_columns option:selected").text(), $("#object_columns").serialize(), null, "script");
$.get($("#search").attr("action"), $("#search").serialize(), null, "script");
});
});
The input text is returned as expected, but nothing comes from the object_columns. When I type the word "n", for example, my server terminal returns this message:
Started GET "/people?&search=n&_=1463081307356" for 127.0.0.1 at 2016-05-12 16:28:34 -0300
Processing by PeopleController#index as JS
Parameters: {"search"=>"n", "_"=>"1463081307356"}
EDIT:
I had an idea later on and made some changes. I gave the form an ID and used it to do the request, now I can have both information (column names and the search record) at the same time. The only problem now is with the path, it is returning an URI error.
The search engine
<%= form_tag people_path, class: "form_id" do %>
<% valid_column_names = target.column_names.reject{|r| r == "created_at" || r == "updated_at" || r == "slug"} %>
<%= select_tag :object_columns, options_for_select(valid_column_names) %>
<%= text_field_tag :search, '', autocomplete: :off %>
<% end %>
The live search
$(function () {
$("input#search").keyup(function () {
$.get($(".form_id"), $(".form_id").serialize(), null, "script");null, "script");
});
});
In your search_for method, the return value is always all. You really want the return value to be the result of the ternary operator directly above it. Remove the last all and you should be good to go.
module ClassMethods
def search_for(present_column,record)
record.present? ? where(present_column+' LIKE ?', record+"%") : all
# all <== this should be removed
end
end
In your javascript, you'll need a few changes:
$(function () {
function search() {
$.get("/search", { column_name: $("select#column_name").val(), search: $("#search").val() }, null, "script");
};
$("select#column_name").change(search);
$("input#search").keyup(search);
});
The actual Ajax call has been changed to:
remove the undefined "action" attribute on the search input field
explicitly reference the URL to send the request
create hash parameters for the ajax call
remove the unnecessary calls to serialize()
This also installs a handler on the change event of the column selection, hence the refactoring of the Ajax call to a separate function.
I'm creating a simple voting partial, but it doesn't update because of this:
error
NameError - undefined local variable or method `scribble' for
app/views/scribbles/vote.js.erb:1:in `_app_views_scribbles_vote_js_erb
The only voting methods work without any issues, but it just doesn't update in the view because of the error above. I guess I just need to know how to pass the scribble object into vote.js.erb ???
_vote.html.erb
<%if current_user.voted_for?(scribble)%>
<a href="" id="vote_<%=scribble.id%>" name="promote" class="votes">
<div class="scribblevote">
<i class="fa fa-cloud-upload"></i>
<%=scribble.votes.count%> <%= link_to "demote", vote_scribble_path(:id => scribble.id, :vote => false), remote: true, method: :post%>
</div></a>
<%else%>
<a href="" id="vote_<%=scribble.id%>" name="promote" class="votes">
<div class="scribblevote">
<i class="fa fa-cloud-upload"></i>
<%=scribble.votes.count%> <%= link_to "promote", vote_scribble_path(:id => scribble.id, :vote => true), remote: true, method: :post%>
</div></a>
<%end%>
vote.js.erb
$("#vote_<%=scribble.id%>").html("<%= escape_javascript( render(:partial => "scribbles/vote") ) %>");
controller method
def vote
#scribble = Scribble.find(params[:scribble_id])
#vote = params[:vote]
if #vote == "true"
#v = :up
else
#v = :down
current_user.unvote_for(#scribble)
end
current_user.vote(#scribble, :direction => #v)
respond_to do |format|
format.js{}
end
scribble/_scribbles.html.erb
<% #scribbles.each do |scribble| %>
<%= render :partial=> "scribbles/vote", :locals => {scribble: scribble}%>
<%end%>
$("#vote_<%=scribble.id%>")
vs
$("#vote_<%=#scribble.id%>")
you've set up #scribble not a local var called scribble
#scribble and scribble are not the same thing.
If you never assign a value to something called scribble - then it doesn't exist... and if you try to use in anywhere... then it will explode and tell you that it doesn't exist.
You will need to use #scribble everywhere unless you deliberately set up a local variable called scribble
eg:
<%= render :partial=> "scribbles/vote", :locals => {scribble: #scribble}%>
Note. in this code you are using the variable called #scribble and setting aup a local variable (called scribble) for the partial to use.
the partial will now have access to scribble
You can pass a local to a partial like this:
render(partial: 'path_to_partial', locals: { var_in_partial: var_in_current_scope })
So...
render(partial: 'scribbles/vote', locals: { scribble: scribble })
It'll be called scribble in the _vote partial, and we're passing in scribble from our current scope.
Alternately you could use #scribble which is defined in your controller and is available to all views/partials during your current request cycle.
I want to render my form partial in javascript erb file. When I place it in there it gives me a no method error. Any ideas?
script.js.erb
$(document).ready(function() {
$(".enquiry-button").click(function(e) {
e.preventDefault();
e.stopPropagation();
if ($(this).parents('.cell').find('form').length == 0) {
$(this).closest('.cell').append(
"<div class='enquiry-form'>" +
"<%= escape_javascript( render '/static/form' ) %>" +
"</div>");
$(this).closest('.cell').find(".enquiry-form:last").slideDown("slow");
} else {
$(this).closest('.cell').find(".enquiry-form:last").slideToggle("fast");
}
});
});
_form.html.erb
<%= simple_form_for #user do |f| %>
<%= f.error_messages %>
<%= f.input :email %>
<%= f.button :submit %>
<% end %>
error
NoMethodError in Static#home
Showing /Users/colton/kitchen_ninja/kitchen_ninja/app/views/layouts/application.html.erb where line #6 raised:
undefined method `render' for #<#<Class:0x007f90cc875c80>:0x007f90cd2017e0>
(in /Users/colton/kitchen_ninja/kitchen_ninja/app/assets/javascripts/script.js.erb)
Extracted source (around line #6):
You are mixing two separate things together. Your render needs to run on the server. JavaScript's job is just to make an AJAX request and get a resource.
So, supposed you were to to this:
$.ajax(url: "/test").done(function(html) {
$("#results").append(html);
});
AJAX's only role is to grab a remote resource and parse the results.
In Ruby, you can use render to emit the HTML:
<%= escape_javascript( render '/static/form' ) %>
Just make sure you point the AJAX request to the right resource so Ruby can do the rest of the magic.
Since you are pre-processing the script with .erb, you could use Ruby's string interpolation to return the partial as a string literal in the JavaScript file.
html = "<div class='enquiry-form'>" +
"#{j render partial: 'static/form'}}" +
"</div>"
$(this).closest('.cell').append( html );
I have this form that renders a partial for selecting a person's task.
new.html.slim:
= form_for(#person) do |f|
= f.text_field :name
= f.fields_for :assignment do |a|
= a.collection_select :project_id, Project.order(:name), :id, :name
div id="task_list"
= render 'shared/_task_select', a: a
= f.submit 'Save'
shared/_task_select.html.slim:
= a.collection_select :task_id, #tasks, :id, :name
Changing the project triggers a javascript that runs a "create_tasklist"-method in the PersonsController.
new.js:
$(document).ready(function() {
$('#person_assignment_attributes_project_id').change(function() {
var selection = $('#person_assignment_attributes_project_id').val();
$.ajax({
url: "/create_tasklist",
data: {
project_id : selection
},
dataType: "script"
});
});
});
The "create_tasklist"-method triggers a javascript that updates the partial:
create_tasklist.js.erb:
$("#task_list").html("<%= escape_javascript(render 'shared/task_list', a: a) %>");
Now this raises the error:
undefined local variable or method `a' for #<#<Class:0x42cd770>:0x4213ef0>
The same form works well when editing existing persons - until changing the project. Thus, FormBuilder "a" loses its definition through the javascript actions. I have to use a partial here because I want to do more stuff with it in a later stage. Any ideas how to get that variable to keep its defintion?
Edit 1:
I already tried adding this below the third line of new.html.slim:
javascript:
var a = "#{a}";
and then adding: a: a in the "data" declaration of new.js.
Edit 2:
With this the FormBuilder seems to pass through until the "create_tasklist"-method, but I do not know how to access it properly there. If I declare ´#a = params[:a]´ in the "create_tasklist"-method and then use (in create_tasklist.js.erb):
$("#task_list").html("<%= escape_javascript(render 'shared/task_list', a: #a) %>");
I recieve the error:
undefined method `collection_select' for "#<ActionView::Helpers::FormBuilder:0x4760400>":String
So the FormBuilder has become a string but a least it "got through" somehow. How can I leave it intact and is a more efficent way to achieve this?