Inside tag in my index.html.eex i can use this syntax to retrive text.
<%= gettext "Login failed!"%>
Now i have a js file used for validating fields, and i load this file as a script inside index.html.eex.
Everything works fine but i need to use gettext for Phoenix text translation inside the js file. I know that <%= %> syntax won't compile outside .eex files, however i refuse to put all my validation logic code inside a in index.html.eex
What can i do to get phoenix code inside a js file?
You can store the translations in a variable in index.html.eex and then access it from the JS files. Here's an example:
Add this to the top of index.html.eex:
<script>
var Translations = {
"login_failed": <%= raw Poison.encode!(gettext("Login failed!")) %>,
};
</script>
Now in your JS file, do:
console.log(Translations.login_failed)
A better way would be to create a list of such translations and inject them in your layout file:
<script>
var Translations = {
"login_failed": <%= raw Poison.encode!(gettext("Login failed!")) %>,
"some_other_error": <%= raw Poison.encode!(gettext("Some other error!")) %>,
};
</script>
This way all translations will be available in app.js.
Why raw and Poison.encode!?
Simply doing "<%= thing %>" or '<%= thing %>' will break if thing contains quotes or newlines (and probably some other special characters). Poison.encode! will convert the value into valid JSON (which is also valid JS) and raw will ensure this value is not double escaped.
Related
I'm working on a Node.js app (it's a game). In this case, I have some code set up such that when a person visits the index and chooses a room, he gets redirected to the proper room.
Right now, it's being done like this with Express v2.5.8:
server.get("/room/:name/:roomId, function (req, res) {
game = ~databaseLookup~
res.render("board", { gameState : game.gameState });
}
Over in board.ejs I can access the gameState manner with code like this:
<% if (gameState) { %>
<h2>I have a game state!</h2>
<% } %>
Is there a way for me to import this into my JavaScript logic? I want to be able to do something like var gs = ~import ejs gameState~ and then be able to do whatever I want with it--access its variables, print it out to console for verification. Eventually, what I want to do with this gameState is to display the board properly, and to do that I'll need to do things like access the positions of the pieces and then display them properly on the screen.
Thanks!
You could directly inject the gameState variable into javascript on the page.
<% if (gameState) { %>
<h2>I have a game state!</h2>
<script>
var clientGameState = <%= gameState %>
</script>
<% } %>
Another option might be to make an AJAX call back to the server once the page has already loaded, return the gameState JSON, and set clientGameState to the JSON response.
You may also be interested in this: How can I share code between Node.js and the browser?
I had the same problem. I needed to use the data not for just rendering the page, but in my js script. Because the page is just string when rendered, you have to turn the data in a string, then parse it again in js. In my case my data was a JSON array, so:
<script>
var test = '<%- JSON.stringify(sampleJsonData) %>'; // test is now a valid js object
</script>
Single quotes are there to not be mixed with double-quotes of stringify. Also from ejs docs:
"<%- Outputs the unescaped value into the template"
The same can be done for arrays. Just concat the array then split again.
I feel that the below logic is better and it worked for me.
Assume the variable passed to the ejs page is uid, you can have the contents of the div tag or a h tag with the variable passed. You can access the contents of the div or h tag in the script and assign it to a variable.
code sample below : (in ejs)
<script type="text/javascript">
$(document).ready(function() {
var x = $("#uid").html();
alert(x); // now JS variable 'x' has the uid that's passed from the node backend.
});
</script>
<h2 style="display:none;" id="uid"><%=uid %></h2>
In the EJS template:
ex:- testing.ejs
<html>
<!-- content -->
<script>
// stringify the data passed from router to ejs (within the EJS template only)
var parsed_data = <%- JSON.stringify(data) %>
</script>
</html>
In the Server side script:
ex: Router.js
res.render('/testing', {
data: data // any data to be passed to ejs template
});
In the linked js (or jquery) script file:
ex:- script.js
In JavaScript:
console.log(parsed_data)
In JQuery:
$(document).ready(function(){
console.log(parsed_data)
});
Note:
1. user - instead of = in <% %> tag
2. you can't declare or use data passed from router to view directly into the linked javascript or jquery script file directly.
3. declare the <% %> in the EJS template only and use it any linked script file.
I'm not sure but I've found it to be the best practice to use passed data from router to view in a script file or script tag.
This works for me.
// bar chart data
var label = '<%- JSON.stringify(bowlers) %>';
var dataset = '<%- JSON.stringify(data) %>';
var barData = {
labels: JSON.parse(label),
datasets: JSON.parse(dataset)
}
You can assign backend js to front end ejs by making the backend js as a string.
<script>
var testVar = '<%= backEnd_Var%>';
</script>
This should work
res.render("board", { gameState : game.gameState });
in frontend js
const gameState = '<%- JSON.stringify(gameState) %>'
Well, in this case you can simply use input text to get data. It is easy and tested when you use it in firebase.
<input type="text" id="getID" style="display: none" value="<%=id%>">
I know this was answered a long time ago but thought I would add to it since I ran into a similar issue that required a different solution.
Essentially I was trying to access an EJS variable that was an array of JSON objects through javascript logic like so:
<script>
// obj is the ejs variable that contains JSON objects from the backend
var data = '<%= obj %>';
</script>
When I would then try and use forEach() on data I would get errors, which was because '<%= obj %>' provides a string, not an object.
To solve this:
<script>
var data = <%- obj %>;
</script>
After removing the string wrapping and changing to <%- (so as to not escape html going to the buffer) I could access the object and loop through it using forEach()
Suppose you are sending user data from the node server.
app.get("/home",isLoggedIn,(req,res)=>{
res.locals.pageTitle="Home"
res.locals.user=req.user
res.render("home.ejs");
})
And now you can use the 'user' variable in the ejs template. But to use the same value using client-side javascipt. You will have to pass the data to a variable in the tag.
Passing ejs variable to client-side variable:
<script>
let user= '<%- JSON.stringify(user) %>';
</script>
<script>home.js</script>
Now you can access the user variable at home.js
I'm sending a var in rails to use it in the view, I have a js file name charts.js.erb that is called and I need to use the var in that js file but is nil
in my controller a have the method
def index
#asd = 'hellow'
end
in my view i can do
.div= #asd
but in my javascript file
alert('<%= #asd %>');
#asd var is nil, i've tested using other ruby code an works, but the vars doesn't work
That's not how ERB works with JavaScript. The JS files are generated before runtime, so they do not have access to dynamic server values like that.
The pattern that I usually follow is to place the server-generated value somewhere in the DOM for the JS to pick up.
<div id="asd" data-asd="<%= #asd %>">
Then if you're using jQuery, for example:
var asd = $('#asd').data('asd');
I use this for different languages on our site:
Locale locale2 = (Locale)session.getAttribute("org.apache.struts.action.LOCALE");
ResourceBundle bundle = ResourceBundle.getBundle("content.test.Language", locale2);
I can easy access the string values of the ResourceBundle in HTML to include it on the site via:
<%= bundle.getString("line1") %>
But in some cases I need to access the string values out of javascript.
I have not found a way to do this so far.
I only found a ugly workaround to get the string values.
On the HTML part I include:
<input type="hidden" name="hiddenLine2" id="hiddenLine2" value=<%= bundle.getString("line2") %>>
I do this for all strings I could possibly need.
To access one of them out of javascript I do this:
var line2 = document.getElementById("hiddenLine2").value;
This is working so far, but I donĀ“t like it.
I am sure there could be a better solution.
Some of the possible solutions.
Use an ajax method to get your resource by passing a key.
Use Hidden input fields and load values.
Use a dedicated jsp page to declare js variables or even a js function to get values according to key.
like this.
<script type="text/javascript">
var messageOne = '<%=bundle.getString("line1") %>';
var messageTwo = '<%=bundle.getString("line2") %>';
</script>
It is normally bad practice to use scriplets <% %> inside your jsp files.
You can use the fmt tag from the jstl core library to fetch information from your resource bundles.
<fmt:bundle basename="bundle">
<fmt:message var="variableName" key="bundleKey" />
</fmt:bundle>
<input type="hidden" name="hiddenLine2" id="hiddenLine2" value="${variableName}">
should work
infact, i think you can also directly embed it into the javascript with EL aswell
var line2 = ${variableName}; //instead of getting it from document.getElement(...)
Based on what I have tried, you can use jstl library to print the translated messages directly into JavaScript like:
alert("<fmt:message key='line1'/>");
And if you are using struts2 for handling the locales you can easily define you Bundles getting either the struts2 locale, saved by the i18nInterceptor present on the default stack, or the user request locale (the clients' browser one)
<!-- //Import the requierd libraries -->
<%#taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- //Take the Locale from struts if it's present and from the user request if not -->
<c:set var="locale" value="${not empty sessionScope.WW_TRANS_I18N_LOCALE
? sessionScope.WW_TRANS_I18N_LOCALE : pageContext.request.locale}"/>
<!-- //Get the bundle based on the Locale -->
<fmt:setLocale value="${locale}"/>
<fmt:setBundle basename="content.test.Language"/>
But if you want to be able to extract that JavaScript code into an external .js file on the future I recommend you to use some of the internalionalization libraries available for JavaScript, like Globalize (It's the only one I have used, but there are plenty on the net).
The downside of using an external JavaScript library for internationalization is that you will have to define the tranlation resources directly on .js files, it's impossible to access to your .properties on the server from a client-based language like JavaScript.
Here is a different solution.
Load bundle like OP did, with the method getBundle().
Using the third option on Arun's answer, create a separate JSP file to create a custom JavaScript object.
This is the content of said JSP:
<%#page import="com.tenea.intranet.conf.Conf" %>
<%#page import="java.util.ResourceBundle,
java.util.Enumeration" %>
<script type="text/javascript">
var _get = function(ID){
if (this.hasOwnProperty(ID)) return this[ID];
else {
console.warn("[Resources] Error al obtener clave <"+ ID +">");
return "[ERROR]";
}
};
var _search = function(text){
var elems = { }
Object.keys(this).map(e => {
if (typeof (this[e]) !== "function" && this[e].includes(text)) { elems[e] = this[e]; }
});
return elems;
};
var Resources = {
<%
ResourceBundle labels = ResourceBundle.getBundle("content.test.Language", locale2);
Enumeration<String> e = labels.getKeys();
while (e.hasMoreElements()) {
String param = e.nextElement();
out.print(param +":\""+ labels.getString(param) +"\"");
if (e.hasMoreElements()) out.println(",");
}
%>
};
Resources._get = _get;
Resources._search = _search;
</script>
What this JSP does is:
Creates object Resources
Using some snippets (sorry Martin :p), and iterating on the list of keys from the resourceBundle, for each key I print a line like "key: value" with out.println().
The resulting object is something like this:
Resources {
abrilAbrText: "Apr"
abrilText: "April"
...
}
To make some extra functionality, I also added 2 functions inside Resources.
_get() returns the text related to the key passed as parameter. If said key doesn't exist, return the text '[ERROR]'.
_search() is a function I added for development purposes. It searches and returns a custom object with every key whose corresponding text contains the text passed as parameter. NOTE: since it uses "e => {}", it won't work on IE or Safari, so it's best to comment it once the development phase has ended.
Once you have this JSP created, to use it you just have to import it to any JSP you want with this:
<%#include file="[relative_path]" %>
Hope it helps! :)
I've been looking into how to use a javascript variable within erb <% %> tags. This will have to be done via AJAX (see How to pass a javascript variable into a erb code in a js view?). I'm quite new to JS and especially new to AJAX and finding an example of this in action would be awesome.
Consider the following simple scenario where all that is needed to be passed from the JS to the ERB is a simple bit of text:
HTML:
<input id="example-input" type="text">
$(function() {
$('input#example-input').keyup(function(e) {
if(e.keyCode == 13){
var input = $('input#example-input').val();
<% puts input.upcase %>
}
}
});
Notice that the input will not be defined within the erb tags, and hence this will throw an error.
I believe you're assuming that the line <% puts input.upcase %> will execute ruby code after your javascript line var input = $('input#example-input').val();. If that is what you were thinking, this is incorrect. In the example you've given:
The <% puts input.upcase %> gets executed when the page loads.
input in <% puts input.upcase %> is a ruby variable
input in var input = $('input#example-input').val(); is a javascript variable
If you had a variable that you set on the server side, you could say something like this in your javascript:
var my_js_var = '<%= a_str_in_ruby %>';
and that would work fine for intializing a variable on the javascript side.
However since you want to request data from the client side (aka javascript) to your server side (to be handled by rails controller), what you should be doing is submitting an ajax request. There's a section in the rails documentation that contains examples, and a good railscasts episode (if you're a paying member).
I have two variables, <%= #user.lat %> and <%= #user.lng %>
These variables change depending on the user whose logged into my system - it's the address the user gave when registering with my app.
In a scripts.js file I've been trying to define them, so my Google map can show with the user's latitude and longitude.
But
function initialize_google_maps() {
var currentlatlng = new google.maps.LatLng(<%= #user.lat %>, <%= #user.lng %>);
etc, etc doesn't work, because it can't understand my ruby code.
I tried defining them at the top of the scripts.js file like:
var map_latitude = "<%= #user.lat %>";
var map_longitude = "<%= #user.lng %>";
and then using:
function initialize_google_maps() {
var currentlatlng = new google.maps.LatLng(map_latitude, map_longitude);
but I've learnt that you just can't put ruby in a js file. I did try renaming it to scripts.js.erb but that didn't work either.
So, how can I define <%= #user.lat %> and <%= #user.lng %> so they'll be recognised by my scripts.js file, and show up in my maps? I did try this answer here, creating a partial, but it didn't work for me. Maybe I was doing it wrong.
Please note: I can't simply put the code and maps function between script tags in a html.erb file because I'm using some ajax, and things get messed up - the ruby variables need to be recognised by the js file. Thanks.
It's possible to use Ruby in JavaScript file, but it's not recommended so I will not explain how.
To answer your question, you can just put the variable in your HTML attribute:
<div id="foo" data-lat="<%= #user.lat %>">Books are coming soon!</div>
And then extract it with JavaScript:
var lat = $("#foo").data("lat");
http://railscasts.com/episodes/324-passing-data-to-javascript will get you going in the right direction.
I think unobtrusive javascript is probably a good way to do this:
http://railscasts.com/episodes/205-unobtrusive-javascript
As the others suggested, you should aim for unobtrusive javascript.
Yet, you might want to use embedded javascript code in a js response for a view, for example. In this cases, you should use the erb extension in your javascript files, so the correct is.js.erb.