Passing variable to Javascript doesn't work in Rails 4.2 - javascript

I have just created a simple Rails 4.2 application to upload file to S3. I'm trying to follow this article https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails#submitting-and-rendering-the-images and it looks like in javascript <%= #variable %> gets parsed as string.
I tried this.
This is what I have in /users/new
# GET /users/new
def new
#s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: 201, acl: :public_read)
#user = User.new
end
And this is what I tried in application.js
window.vaz = <%= #s3_direct_post.url %>;
but when I do console.log(window.vaz); I get "<%= #s3_direct_post.url %>" instead of the real value.
I tried gon as well but gon has a lot of issues with Rails 4.2
I verified that #s3_direct_post.url returns something in the console.

Add .erb extension to application.js file (application.js.erb). <%= => is erb syntax and you need to process it as erb file first so that <%= #s3_direct_post.url %> is evaluated as ruby code.
http://guides.rubyonrails.org/asset_pipeline.html#preprocessing

Related

How to store in localStorage in Javascript from Ruby variable

I try to store a token in my localstorage from a ruby variable,
As I understood, you can only set LocalStorage with Javascript,
So here is my question, how can I pass my variable from Ruby to Javascript ? I saw some answers with script tag in the view, but is there any solution to do it directly in the app.js folder ?
So here is my Controller :
#token = HTTParty.post('https://test.pro/2.0/auth/token/access',
body: {
client_id: XXX,
client_secret: "YYYYYYYYYYYYYY",
code: LLLLLL
}
)
And in my view if I do a <%= #token %>, I have the following result :
{"access_token": "1VwCAjhsfCsdEoBoQs1G9kLHKoWOcJjamyj1s8_NQPrHeGNagzYYFrXKp_VlY", "token_type": "Bearer"}
Thanks for your help !
If you want to perform javascript from within a ruby file you can use a javascript_tag:
https://apidock.com/rails/ActionView/Helpers/JavaScriptHelper/javascript_tag
An example of how to use it:
Accessing Ruby objects in javascript
In your case, something in your view like:
<% javascript_tag do %>
localStorage.setItem('accessToken', <%= #token["access_token"] %>);
localStorage.setItem('tokenType', <%= #token["access_token"] %>);
<% end %>
Should do the trick

Locals issue in Rails application

I am getting errors while doing an AJAX call with remote: true with locals. Here's the error:
ActionView::Template::Error (undefined local variable or method `f' for #<#<Class:0x94db890>:0x9ab41d0>):
Here is my code below:
new.html.erb
<%= form_for(#author) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :age %><br>
<%= f.number_field :age %>
</div>
<%= link_to "Add Books" , remote: true %>
<div id = "add_book"></div>
<%= f.submit %>
Controller
def new
#author = Author.new
#books = #author.books.build
respond_to do |format|
format.html
format.js
end
end
new.js.erb
$("#add_book").append(" <%= escape_javascript(render partial: 'addbook', :locals => { :f => f }) %> ");
You are calling the new action of your controller using remote: true (an AJAX call). This will result in new.js.erb being rendered to be sent back as the response - the new.html.erb will not be processed.
Within new.js.erb you have:
$("#add_book").append(" <%= escape_javascript(render partial: 'addbook', :locals => { :f => f }) %> ");
Meaning it is attempting to render the addbook partial and set up a local variable for it, to be referenced in the partial as f, and assigning it the value (local to new.js.erb) f.
new.js.erb has not declared the local var f and thus your error.
To assist more we would need to know what the intention of this variable is - in what way does _addbook.html.erb require it?
So... the way that an erb template works is this:
Rails generates a page, in your example, this would be new by going through the new action in the controller and gathering up all the variables (eg author and books).
Then it gets the right template for the action. In the case when you first open up the new page you get the form... right? That comes from the new.html.erb template.
So rails reads through the template and runs the ruby code... replacing it with appropriate html. Then it sends the html to the browser.
By the time it's sent it to the browser, there is no longer any ruby code... just html, because the browser doesn't understand ruby, just html - so that's all that gets sent.
Now... the browser has an html form, that contains some js - this would be the add books link - it's html, that calls some js that sends a request to your rails app.
The rails app then goes through the new action, gathering the variables again, but this time, it doesn't render the html template... because it got called by js. So it grabs the js template instead.
At this point rails knows nothing about the html template. Anything that was in the html template is long gone - sent away to the browser. All it knows about is what is in the js template. It tries to run the ruby code in the template... and it's asking for f... and f doesn't exist in your js template... so it gets confused and tells you it doesn't know about this f thing.
It doesn't matter that at some point in the past it rendered a form called f, because that belonged to a way distant past request that no longer exists. The browser doesn't tell rails about f because it never knew about it (that was part of the ruby, not the html that is all that the browser ever saw). So... there is no f. ;)
In this case - I am not sure you need to pass f in as a local variable at all. If you are rendering the template that contains the form... then you don't need to pass in a form because it will already be rendering the form. Try just rendering the partial without passing in locals, and see what happens... if you get a bug, we can then work on that.

How can I render via CoffeeScript in Ruby on Rails 4

I was trying to solve this problem for a while with no result.
I've wanted to pass variables and load a render into a div using CoffeeScript in rails 4.
(I'm using SpreeCommerce platform).
view:
<%= link_to taxonomy.name,root_path+'t/'+tid, {class: "uno", remote: true} %>
controller:
respond_to do |format|
format.html
format.js # menu.js.coffee.erb
end
menu.js.erb.coffee:
$('div#productos').html("<%= escape_javascript(render :partial => /shared/products) %>")
I'd like to load the page '_products.erb.html' and the partial processes the variables that I give it. As soon as I know, view and controller are ok, the problem is in menu.js.erb.coffee
Any help will be apreciated!
ADDITIONAL:
I've modified the extension to .js.coffee.erb. When I try to run the app, it shows me:
"undefined method `render' for #<#:0xa70317c>"
I tryied using <%= raw escape_javascript( render :partial =>... almost always "render" method give me problems.
NEW INFO:
I added gem 'coffee-script' to the Gemfile (then 'bundle install').
Now, when I click the link_to, it shows me into the HTML <%= escape_javascript(render :partial => /shared/products) %> as a text instead of loading the "partial"... any suggestion please?
I wrote a post about this after struggling through the same problem.
You need to:
Name it menu.js.coffee. Suffixing .erb causes it not to be evaluated as CoffeeScript.
Use raw to escape it.
I used these two on my website. Here's how it looks:
<%= raw render 'path/to/menu.js.coffee' %>
It still processes ERB within your CoffeeScript.
I would recommend changing it from menu.js.erb.coffee to menu.js.coffee.erb.
Rails will process the file extensions from right to left. Meaning right now, your file is treated first as coffeescript, then as ruby, and finally as javascript. It looks like you want to make the ruby substitutions first, then parse the coffeescript into javascript, so that would be menu.js.coffee.erb
First of all, you should change file name from menu.js.erb.coffee to menu.js.coffee.erb and you need configuration file as follow, which is a contribution by cervinka on coffee-rails issue #36
config/initializers/coffee_erb_handler.rb
ActionView::Template.register_template_handler 'coffee.erb', Coffee::Rails::TemplateHandler # without this there will be template not found error
class ActionView::PathResolver < ActionView::Resolver
EXTRACT_METHODS = %w{extract_handler_and_format_and_variant extract_handler_and_format} # name for rails 4.1 resp. 4.0
method_name = EXTRACT_METHODS.detect{|m| method_defined?(m) || private_method_defined?(m)}
raise 'unknown extract method name' if method_name.nil?
old_method_name = "old_#{method_name}"
alias_method old_method_name, method_name
define_method(method_name) do |path, default_formats|
self.send(old_method_name, path.gsub(/\.js\.coffee\.erb$/, '.js.coffee'), default_formats)
end
end

Multiple file upload rails 4

I am using Rails 4. I want to upload multiple files of type doc, doc.x, pdf etc, but not any image file. I implemented 'jquery-fileupload-rails' gem. But while uploading it shows "Internal Server Error".
Can you help me with this issue? Or is there any other way to upload multiple files of above mentioned file type?
you can use the gem Papaerclip available in rails for uploading images and files of any type.
Have a look in this article I wrote
http://www.abhijat.name/2013/08/carrierwave-s3-fog-jquery-file-upload-vs-cors-html5-canvas.html?m=1
As mentioned by the other responses, you're going to need to use an attachment-handling gem, such as Paperclip or Carrierwave
The problem you have is that although the JQuery upload gem is working to send files to your server, Rails doesn't know how to handle the files you send, hence why you're seeing the 500 error
Paperclip
We love Paperclip - it's perfect for uploading any attachment to your server
Here's how to use it:
#GemFile
gem "paperclip", "~> 3.5.2"
#app/models/attachment.rb
Class Attachment < ActiveRecord::Base
has_attached_file :attachment
end
#db/migrate
def change
create_table :attachments do |t|
t.attachment :attachment
t.timestamps
end
end
#app/controllers/attachments_controller.rb
def new
#attachment = Attachment.new
end
def create
#attachment = Attachment.new(attachment_params)
#attachment.save
end
private
def attachment_params
params.require(:attachment).permit(:attachment)
end
#app/views/attachments/new.html.erb
<%= form_for #attachment, {multipart: true} do |f| %>
<%= f.file_field :attachment %>
<%= f.submit %>
<% end %>
Hope this helps?

Include a Coffeescript file with ERB in a view

This is giving me a major headache...
So I have an app which requires a sidebar that lists various information to do with a user's player. One section of this sidebar is a friends list. Now, when Player A sends a friend request to Player B, the request should be automatically logged in B's sidebar, and I intend to use WebSockets to do this.
Here is my cp.js.coffe.erb file (there's only a few snippets of ERB at the moment; there will be loads more and I rather get this working first):
$ ->
$("#cp").accordion()
if `"WebSocket" in window`
socket = new WebSocket("ws://localhost:8080")
socket.onopen = =>
console.log("Connection Open")
init = {
sender: "cp"
action: "init"
user: <%= #user.id %>
token: <%= cookies["remember_token"] %>
}
socket.send(init.to_json)
socket.onerror = (e)=>
console.log(e)
socket.onclose = =>
console.log("Closed")
socket.onmessage = (m)=>
console.log("Recieved: #{m.data}")
msg = m.data.JSON.parse
switch msg.action
when "ret_init"
when "friend_udt"
refreshFriend()
refreshFriend() ->
html = "<%= j render 'layouts/friends' %>"
$('#friends').empty()
$('#friends').add(html)
Theoretically, the code itself works fine, the problem being that Rails doesn't let you use ERB in the assets pipeline, and so this file has to sit in app/views/layouts.the file cannot access the variables declared within a controller or use the render method (or most other ERB methods).
Here's the thing: I can't include said file in my application.html.erb file, and I looked into requesting the file with AJAX, but from my understanding that will immediately execute the Javascript once and once only, and I need the methods in this to be constantly available to update the sidebar.
Is there any way of including this file so that it works with the ERB and the CoffeScript so that it would be continuously avaliable to the page? Am I misunderstanding the whole AJAX requesting method?
Thanks #nzifnab for your help with the JS. Now my friends partial looks like this:
<ul id="friendlist">
<% if Relation.find_by(owner: #user.id, type: "freq") != nil %>
<% Relation.find_by(owner: #user.id, type: "freq").each do |r| %>
<li class="friend-request-cp"><%= link_to "/#{User.find(r.character).name}" %></li>
<% end %>
<% end %>
<% if Relation.find_by(owner: #user.id, type: "friend") != nil %>
<% Relation.find_by(owner: #user.id, type: "friend").each do |r| %>
<li class="friend-cp"><%= link_to "/#{User.find(r.character).name}" %></li>
<% end %>
<% end %>
</ul>
I need to apply two different styles to each item, hence why I'm using the ERB here. This works fine, as it's loaded when the page is first navigated to, but my code was supposed to re-render that partial every time a notification comes through of any new interactions. It would then repopulate the list using the data from the database again. Is there a more efficient way of doing this? Can I still do this with the hamlcoffeeassets gem you showed me?
Slight tangent ensues:
By the way, I'm using Ruby 2.0.0-p247 and Rails 4 on Windows 7. I felt the need to include that because of some major compatibility issues with gems that are much different from Ubuntu. I had to move from Ubuntu to Windows because updating from 13.04 to 13.10 broke everything Ruby Gem on that OS. I don't have tome to find a fix: I literally have only four days to get this app built.
You can kinda use erb in the asset pipeline, but you have to remember that it only gets rendered ONCE, EVER, and not once for every user and so even if there was an #user variable (which there won't be), it would never change. You can use erb in your coffee file for things like route paths and environment variables, but not for things like user-specific config and dynamic changes to the JS. It's bad practice anyway.
What you should really do is use a javascript library to read cookies instead of trying to do it with rails (This will give you access to some of the things you appear to be trying to do). And when you need more dynamic behavior you should render data-attributes or other values into the html DOM itself and use the javascript to read that.
Take a look at this cookie library: https://github.com/carhartl/jquery-cookie
There's many others to look at via a quick google search.
socket.onopen = =>
console.log("Connection Open")
init = {
sender: "cp"
action: "init"
user: $.cookie('user_id')
token: $.cookie('remember_token')
}
There are a couple of ways to render new markup for your view using JS. One way is to use js templates. I'm a big fan of the hamlcoffeeassets library here: https://github.com/netzpirat/haml_coffee_assets Although it uses haml for the view, and not ERB. There are ERB variants as well.
You would add some markup to app/assets/templates/friend.jst.hamlc like so:
%p This is my friend markup #{#friend.name}
And then you can render it from your JS like this:
$('#friends').append(JST['templates/friend'](friend: {name: 'Bob'}))
Which will append the markup from your template with the values you've passed interpolated in. In my example you'd end up with this markup inside your #friends container:
<p>This is my friend markup Bob</p>
Alternatively you can render the partial you want via rails into your JSON response as just a string, and then insert that into your document...
So your JS might look something like this:
socket.onmessage = (m)=>
console.log("Recieved: #{m.data}")
msg = m.data.JSON.parse
switch msg.action
when "ret_init"
when "friend_udt"
refreshFriend(msg.friendHTML)
refreshFriend(html) ->
$('#friends').html(html)
UPDATE
In reference to your ERB question... First of all your partial is incredibly inefficient making similar calls to the database four times every time you render it. haml_coffee_assets is for use with the haml markup language (which I prefer over ERB), if you want ERB then use eco instead: https://github.com/sstephenson/eco
If you want to render this in the JS, then you need to send this "friend relation" data as JSON through the notification data response, you do not have access to active record OR any controller methods or instance variables when rendering javascript partials - they don't hit back to the server, they only use what is accessible by your javascript at the time.
This should really go to app/assets/javascripts/cp.js.coffee.erb, you can use erb in the asset pipeline just fine (see here) Make sure you are spelling the coffee extension right, though!
Doing this, you should be able to call this via ajax without problems, the path would be /assets/cp.js.
try this gem: 'coffeebeans'
name your coffee file as "some_file.html.erb"
<%= coffeescript_tag_do %>
# your coffee script here ...
<% end %>
in another erb file:
<%= render file: '.../some_file' %>

Categories

Resources