I've js file with embeded ruby code:
function MyController($scope) {
$scope.sayHello = function() {
var hello = <%= current_user.name %>;
$scope.greeting = hello;
};
}
But I've catch an error undefined local variable or method current_user which points on
= javascript_include_tag "application", "data-turbolinks-track" => true
How can I fix it? Thanks!
Use gon gem https://github.com/gazay/gon the easiest way to pass data from your rails environment to your javascript environement (in other words: from your server to your client).
in your controller
# Make sure first that current_user is not nil
gon.current_user_name = current_user.name
# As you can see we don't pass the whole `current_user` but only the data we need
# gon and javascript won't know how to use your Rails objects
# so pass only strings, integers, arrays and hashs
# (but you'll figure out all of this by your self, it's pretty natural)
in your layout (more info on gon installation can be found in gon github pages)
<head>
<%= include_gon %>
...
in your javascript
var hello = gon.current_user_name;
JS
To extend Benjamin Sinclaire's answer, you have to remember Rails is a back-end system; JS is front-end.
This means all of the Rails variables are only available in the Rails files in the backend - what your browser receives is a pre-rendered set of HTML files, populated with your data
The problem you have is that JS cannot read Rails data without having that data translated from Rails into "JS". All JS sees is the DOM - the HTML elements on your page
--
Data
In order to share variables such as current_user in your JS / front-end, you basically need to pass it through to the HTML layer
You can either do this by setting a hidden element (and setting its data attributes), or by setting the variable in your layout
As mentioned, the most efficient way to do this is to use the gon gem, which Benjamin Sinclaire has discussed
Related
Action: a javascript action attempting to retrieve data based on two parameters, a user-input one and an issuing page parameter.
whereas in the past something like the following would work:
<%= javascript_tag do %> chart.data = data_<%=j params[:nation_id] %>; <% end %>
now with the javascript being 'packed', the compilation of syntax like <%=j params[:nation_id] %> does not work and returns :
/search.json?nation_id=%22%3C%=j%20params[:nation_id]%20%%3E%22&q=mi
modifying the packs/muni.js file as follows, also does not generate a proper url for the search function
var options = {
url: function(phrase) {
return '/search.json?nation_id="#{params[:nation_id]}"&q=' + phrase;
},
getValue: "name",
};
returning
/search.json?nation_id=%22
the page is set with /new?top%5Bnation_id%5D=1&commit=Set
How does the javascript pack need to be written?
Javascript pack is not rendered for each request/visitor - whole point is that it is packed once per deploy and is the same for all (except for when you have several packs, dynamic module loading and other advanced techniques, but still code is not changed per request). In fact, older method with asset pipeline is very similar in this aspect.
Do not try using ruby inside the pack, but instead think of a way to pass the parameter to the js code.
For example, you can pass it via a adding some html tag an querying it from javascript:
In view:
<meta name="nation_id" content="<%= params[:nation_id] %>" />
in js:
nation_id = document.querySelector('meta[name="nation_id"]').content;
return `/search.json?nation_id=${nation_id}&q=${phrase}`;
PS. also you might need to escape your phrase with encodeURIComponent
First let me tell you that I've searched for this and didn't manage to find any answer for my problem.
I have a lot of Javascript on my layouts/application.html.haml file:
### at the end of application.html.haml file
- if #instance_variable.condition
:javascript
// a lot js/jQuery code
:javascript
// more js code that uses #{ #instance.vars }
- if #another_condition.using.ruby.cody
:javascript
// more js code that uses #{ #instance.vars }
And I'm using instance vars within this code, which means that this vars will only be declared on the controller (application controller). As this is the application layout, these scripts run on every page of my website (and that's what I need of course).
What I want is to move all this code to a js.erb file (or .haml if possible). First - because is easier to manage this code in a separate file; Second - because I don't want to have <script> // a lot of js code </script> tags at the end of every html file, I want to have a <script async src='link'/> tag only.
Also I'm including already the application.coffee at the beginning of the file, but I need this script tag at the end of the html files.
I wouldn't recommend using partials for this. Because, your code uses variables, this means it changes depending on your variables. If you put them into a separate javascript file, the browser wouldn't know about the changes and use a cached file. A workaround would be to add some string (that changes when vars change) at the end of your filename, but then, you would lose all the benefits of moving your javascript into separate files.
A better way would be to define variables in your application.html.haml, move out your javascript code into separate files and just use defined variables.
application.html.haml
- if #instance_variable.condition
%script(src="path/to/my/script.js")
:javascript
= var some_var = #{#instance.vars}
%script(src="path/to/my/second_script_that_uses_var.js")
Thank you for your answer Uzbekjon, but after some research I found a way to do exactly what I wanted :)
In my 'layouts/application.html.haml' file, I added a script tag:
### at the end of application.html.haml file
%script{async: 'async', src: application_scripts_path}
Then I added this path to routes:
get 'application_scripts' => 'controller#application_scripts', as: :application_scripts
Then I just had to set this action application_scriptson my controller and create a new view (app/views/controller/application_scrips.js.erb):
class Controller < ActionController::Base
# 1
protect_from_forgery except: :application_scripts
def application_scripts
# 2
if condition.that.tells.me.this.request.is.valid
# 3
render formats: [:js] and return
end
render plain: ''
end
These steps were of course the harder ones to find out:
1 - I had to disable this protection, or else I would get this error:
ActionController::InvalidCrossOriginRequest at /application_scripts
Security warning: an embedded tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript embedding.
2 - To make sure that no other site can request this script file (not that it could be a problem for me, but I preferred this way) I added a condition that makes sure the request comes from my website. In this case I was just checking if a user as logged in.
3 - The formats: [:js] tells Rails that the view is not .html, instead it's a .js file 'application_scripts.js.erb'
Finally I just had to move all my code from the application.html.haml file to the view file 'application_scripts.js.erb' and convert from haml code to erb as well.
<% #instance_variable.condition %>
// a lot js/jQuery code
// more js code that uses <%= #instance.vars %>
<% #another_condition.using.ruby.cody %>
// more js code that uses <%= #instance.vars %>
<% end %>
<% end %>
I am using the FileStack API and the file picker gem (https://github.com/Ink/filepicker-rails). I have an Attachment model that has a :title as a string. When a file is uploaded, the URL from the FilePicker API is stored as the :title. But the gem has a onchange method that returns an event variable as a JSON object that contains attributes of the file. I use JavaScript to access those attributes but I want to find a way in Rails to store those attributes, accessed via JavaScript, in a Model so that I can access it through the rest of the Rails app.
<%= filepicker_js_include_tag %>
<%= simple_form_for(#attachment) do |f| %>
<%= f.filepicker_field :title, onchange: 'onUpload(event)' %>
<%= f.submit %>
<% end %>
<script>
function onUpload(event) {
console.log(event);
var name = event.fpfile.filename;
console.log(name);
}
</script>
Update:
So after looking into your solution and googling around I am using ajax to send the data via routes to the controller. Below is my updated Javascript as well as the route and controller. When I render and inspect the #foo instance variable it is nil. So my data isn't getting passed properly. Furthermore, this whole process from the firing of the Javascript function to displaying the index view is now very very slow. I think I have the right idea after viewing your solution and doing more digging but I'm missing something and/or overcomplicating this. Any advice would be much appreciated.
<script>
function onUpload(event) {
var name = event.fpfile.filename;
jQuery.ajax({
data : { data_value: event },
type: 'post',
url: "/attachment/index"
});
}
</script>
Route
post 'attachments/' => 'attachment#index'
Controller
def index
#attachments = Attachment.all
#foo = params[:data_value]
end
View (returns nil)
<%= raise #foo.inspect %>
If you're using Postgres 9.3 or above you should consider using the hstore module and creating a JSON column. In a migration you can do:
add_column :your_model, :your_attribute, :json
And then you can just update YourModel.your_attribute => {'your': 'JSON here'}
Docs here: http://edgeguides.rubyonrails.org/active_record_postgresql.html#json
If you're using MySQL it's tricky, but doable. You have to create a text column and save the JSON as a string, and parse it every time you interact with it. Postgres is definitely better at handling JSON. I realize that this answer relies on an assumption, so if you're not using one of the two data stores mentioned, let me know and I'll pull it down.
I have a backbone application which, upon load, needs to fetch data from four different collections (Rails-->JSON back end).
That's four hits to the server, and I'm guessing there's a better way.
I started out by trying to pass Rails to_json() of the query results into the router initialization in the Rails view such as:
<script type="text/javascript">
$(function() {
window.router = new Backbonedemo.Routers.CalendarsRouter({calendars: [], tasks: <%= #tasks %>});
Backbone.history.start();
});
</script>
but that brought no joy.
So, what's the correct way to run the equivalent of fetch() at startup, without having to hit JSON for each collection I want to collect?
Check out the rabl gem. It allows you to customize your json response to a much greater degree than regular to_json will allow.
Here's a basic way to set up a project where you need to deliver a load of JSON up front:
First, set up your controller to pull data on page load, for examlpe localhost:3000/home would look in the home controller index:
class HomeController < ApplicationController
def index
#user = current_user
render 'user.json' # this line is not actually required most of the time when using backbone and proper ajax/json requests
end
end
Next, set up a rabl template, this takes the place of a view or a partial, and returns JSON to your client. I'm actually going to use a partial, to make loading into the home/index.html view nice and easy:
# views/home/_user.json.rabl
object #user
attributes :id, :first_name, :last_name, :birthdate, :gender, :nickname, :email
node(:avatar_thumb_url) { |u| u.avatar.url :thumb }
node(:roles) { |u| u.roles }
node(:name) { |u| "#{u.first_name} #{u.last_name}".strip }
node(:errors) { |u| u.errors.to_hash if u.errors.present? }
child :awesome_calendars => :calendars do
attributes :id, :date, :description
child :events do
attributes :title, :description
end
end
That's some relatively fancy rabl that will deliver a bunch of json, including a related set of records, all in one JSON object.
In your html view that loads up backbone, you need to pass the controller's object to the partial:
# views/home/index.html.erb
<script type='text/javascript'>
$(function() {
window.router = new Backbonedemo.Routers.CalendarsRouter(<%= render('user.json.rabl', user: #user).html_safe -%>);
Backbone.history.start();
});
</script>
To recap:
controller renders regular html.erb view (the one that starts up backbone)
that view also renders a partial--this partial is a rabl template that returns strictly JSON
backbone takes that JSON and does whatever you want with it.
The beauty of this is that you can set up json.rabl responses for any of your controller actions and have them return a variety of json stuff, all easily controllable. The thing I did above is the "difficult" part where you want to load up stuff from many tables into a single JSON call on your first page load--avoiding multiple AJAX/backbone fetch requests.
Make sense? I hope so... : / let me know if anything is unclear.
I don't know Rails, but see the "bootstrap" example in the Backbone docs:
<script>
Accounts.reset(<%= #accounts.to_json %>);
Projects.reset(<%= #projects.to_json(:collaborators => true) %>);
</script>
Generally, I think you need to create the collection objects, then reset() them with inline JSON data.
I have this Javascript view in my Rails 3 project:
app/views/expenses/new_daily.js.erb
var i = parseInt($('#daily').attr('data-num')) + 1;
//$('#daily').append('agrego fila ' + i + ' <br />');
$('#daily').append('<%= escape_javascript(render(partial: 'new_expense', locals: { i: i })) %>');
$('#daily').attr('data-num', i);
I want to pass my 'i' javascript variable to a ruby partial through locals, How I can accomplish this?
As far as i know there is no way to do it directly and the reason is fairly simple too, html is executed at the server side and javascript is a client side language which means its executed in your local browser, thats why if you even try to pass a variable between the two you'll have to make a request to the server,
However this problem is tackled by calling an AJAX request, this AJAX request does the same thing as sending a new request to the server however it does that without refreshing or reloading the page to it gives the users the illusion that no request was made.
a guy asks a similar question Here
and you can learn more about AJAX Here on MDN:
Yes you can pass the value by using jquery;
<%=f.text_field :email ,:id=>"email_field" %>
<script type="text/javascript">
var my_email= "my#email.com"
$(document).ready(function(){
$("#email_field").val(my_email);
});
</script>
Simple answer is you can't. Partials are expanded at server side, and JavaScript variables are set later at client side. You could make i (as a variable name) a parameter of the partial and use it there.
render :partial => 'xx', :locals => { :variable => 'i' }
And in partial
alert(<%= variable %>);
Check out the gon gem. https://github.com/gazay/gon
It gives you a simple object you can pass variables to that will be available to your scripts via window.gon
Also referenced here
http://railscasts.com/episodes/324-passing-data-to-javascript
1) You may create a js tag with global variable in you erb template, after that you will be able to access that variable from any js file
<%= javascript_tag do %>
window.productsURL = '<%= j products_url %>';
<% end %>
2) You can pass data to data-attribute in erb template and access it by js on client side that way $('#products').data('products')
<%= content_tag "div", id: "products", data: {products: Product.limit(10)} do %>
Loading products...
<% end %>
3) You can use gon, to use your Rails variables in your js
There is a good article, read it and fine solution for your specific case
http://railscasts.com/episodes/324-passing-data-to-javascript,
more comments are here http://railscasts.com/episodes/324-passing-data-to-javascript?view=asciicast
Here's a few different options on how to do it:
http://jing.io/t/pass-javascript-variables-to-rails-controller.html
The best other answers here are right that this can't be done by passing the javascript variable into an erb partial, since it is rendered on the server, not the client.
But since anyone looking for this is probably interested in a work-around solution, which I don't see here, I will post this example that works well with Rails UJS and Turbolinks.
First, you set up your controller to return a partial as HTML:
format.html { render partial: "new_expense" }
Next, write a javascript AJAX function in app/views/expenses/new_daily.js.erb:
var i = parseInt($('#daily').attr('data-num')) + 1;
$.ajax({
url: '/daily',
type: 'GET',
dataType: 'html',
contentType: "application/html",
success: function(response) {
$('#daily').replaceWith(response)
$('#daily').attr('data-num', i);
}
});
This is going to get your Rails partial as an html fragment that you can use to replace that part of your rendered page. You can use jQuery to get your data-num attribute value, do some math on it, replace the partial in your view, and then set the attribute value again.
You may ask why go to all the trouble of getting the Rails partial and replace it on the page, instead of just getting the data attribute, doing math on it, and setting that? The answer is that this is the best, and perhaps the only way of doing something which is really essential when rendering a Rails partial using UJS while handling an asynchronous response to an action.
If you are handling an asynchronous response from your server in a create.js.erb template, then your variables (#daily, for example) are not going to reflect the work done after the request has completed (for example, if there has been processing on a background server like Sidekiq). In that case you don't have up-to-date action response variables to pass into your Rails partial in the js.erb file, but you also can't pass the javascript data response into your partial, as pointed out in this question.
As far as I know, this approach is the only way to get a fully up-to-date partial after receiving a response to an asynchronous response (not shown). This get you the up-to-date partial, allows you to get your javascript into it, and is flexible enough to work in pretty much any use case.
Let's make shure we understand each other. Your erb template (new_daily.js.erb) will be processed on the server side, ruby code will be evaluated (within <% %>), substitution made, and then resulting javascript will be sent to browser. On the client side the browser will then evaluate this javascript code and variable i will be assigned a value.
Now when do you want to pass this variable and to what partial?