Related
I want to store the value of the select from a form into my javascript.
This works:
<div id="tshirt-div">
....
</div>
<select id="tshirt-color">
<option value="#fff">White</option>
<option value="#000">Black</option>
<option value="#f00">Red</option>
<option value="#008000">Green</option>
<option value="#ff0">Yellow</option>
</select>
<script>
document.getElementById("tshirt-color").addEventListener("change", function(){
document.getElementById("tshirt-div").style.backgroundColor = this.value;
}, false);
</script>
I am trying to do this:
<%= f.grouped_collection_select :color_id, #items.order(:title), :colors, :title, :id, :title, include_blank: "Select Color", id: "tshirt-color" %>
<script>
document.getElementById("tshirt-color").addEventListener("change", function(){
var bg_color = this.value
document.getElementById("tshirt-div").style.backgroundColor = <%= Color.find(bg_color) %>;
}, false);
</script>
A few of my attempts:
var bg_color = document.getElementById("tshirt-color").value
inside and outside of the function
var bg_color = document.getElementById("tshirt-color").addEventListener("change", function(){
document.getElementById("tshirt-div").style.backgroundColor = <%= Color.find(bg_color.value) %>;
, false);
I have tried storing the var many different ways but each gives me the following error:
undefined local method or variable 'bg_color'
The value of the f.grouped_collection_select is an integer so I want to then find the color title (red, blue, green, etc.) and return that to the ...style.backgroundColor.
How can I store the select from the form, store it as a variable, and then find the color from the Color model?
You can't access JavaScript variables from inside the ERB tags. ERB lies on top of the script. Meaning that when the page request is made to the Rails server it will compile the page so that the ERB is interpreted. Then a plain JavaScript file or HTML file is send to the client. The JavaScript will be executed on the client.
<%= Color.find(bg_color) %>
Is run on the server side and has no access to the JavaScript variable bg_color.
A simple solution is to provide all the colors up front and pick the color from that.
const colors = <%= Color.pluck(:id, :value).to_h.to_json.html_safe %>;
document.getElementById("tshirt-color").addEventListener("change", function(){
document.getElementById("tshirt-div").style.backgroundColor = colors[this.value];
}, false);
If you don't want to provide all colors up front you'll have to set-up an AJAX request that queries the server when selecting a color.
Let me quickly explain <%= Color.pluck(:id, :value).to_h.to_json.html_safe %>.
tmp = Color.pluck(:id, :value) # retrieve the id and value of the colors
#=> [[1, "#fff"], [2, "#000"]]
tmp = tmp.to_h # convert the nested array into a hash
#=> { 1 => "#fff", 2 => "#000" }
tmp = tmp.to_json # convert the hash to a JSON string
#=> '{"1":"#fff", "2":"#000"}'
tmp = tmp.html_safe # mark the string as HTML safe so ERB won't replace characters such as " with "
#=> '{"1":"#fff", "2":"#000"}'
Since JSON is short for JavaScript Object Notation it can simply be read by read as part of JavaScript.
Alternatively you could manually build the options, and provide the color value through a custom attribute. An option could for example look something like this:
<option value="1" data-color-value="#fff">White</option>
Then use the additional attribute to set the color.
const color = this.options[this.selectedIndex].dataset.colorValue;
document.getElementById("tshirt-div").style.backgroundColor = color;
You could always just store the value in a hidden field element and just update the value of it whenever the select changes.
Then do something like
<%= hidden_field_tag 'color_id', '0', onchange: "changeColor()", id: 'selected-color-field'%>
<script>
function changeColor() {
var selectedColor = document.getElementById("selected-color-field").value
// Do whatever
}
</script>
I have created 15 fields in my MySQL Table and would like to give the end user the option of using a form to add up to that many items. However, to keep the interface clean, I would like to only present them with maybe 2-3 Textboxes and give them a button that would allow them to add more should they need it.
I don't believe adding the textboxes to the form using Javascript would be an issue, but I am confused as to how to process it exactly once I have submitted the POST Data to the form handler. Can anyone shed some light on the best way to go about this?
If you have to use a normal POST variable containing all the form values, you should be able to do something like this:
When generating the textboxes with the server language and/or javascript, the way they are sent to the server is with their name attribute. If you provide a consistent way of naming the elements, you can "combine" things with numbers. For example, if you provide 2 textboxes every time the user clicks "Add" (one for "foo" and one for "bar"), then you can increment the number at the end to make sure they match.
<input type="text" name="foo1" /><input type="text" name="bar1" />
<input type="text" name="foo2" /><input type="text" name="bar2" />
and so on
Then on the server, you need to find every item in the POST variable that starts with "foo" and "bar"
for (item in POST) {
if (item startswith "foo") {
// Extract the number at the end, and find the related "bar"
}
}
Assuming that you are using ASP.NET MVC for web application, along with jQuery for client side framework.
Let's more assume that you have a model like this:
public class Gift
{
public string Name { get; set; }
public double Price { get; set; }
}
Your initial action and data could be like this:
public ActionResult Index()
{
var initialData = new[] {
new Gift { Name = "Tall Hat", Price = 39.95 },
new Gift { Name = "Long Cloak", Price = 120.00 },
};
return View(initialData);
}
Whereas, your view could be this:
<h2>Gift List</h2>
What do you want for your birthday?
<% using(Html.BeginForm()) { %>
<div id="editorRows">
<% foreach (var item in Model)
Html.RenderPartial("GiftEditorRow", item);
%>
</div>
<input type="submit" value="Finished" />
<% } %>
And partial view for gift editor could be this:
<div class="editorRow">
<% using(Html.BeginCollectionItem("gifts")) { %>
Item: <%= Html.TextBoxFor(x => x.Name) %>
Value: $<%= Html.TextBoxFor(x => x.Price, new { size = 4 }) %>
<% } %>
</div>
The key is "BeginCollectionItem" helper method, which is not standard in ASP.NET MVC. It will generate some keys for variable length models. I will add a link to files later.
Your Handler would be like this:
[HttpPost]
public ActionResult Index(IEnumerable<gift> gifts)
{
// To do: do whatever you want with the data
}
You get a list of gifts with this approach, filled with values in textboxes.
To add one more item, you need to send an ajax request to this view:
Hope it helps
Source: http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Download: http://blog.codeville.net/blogfiles/2010/January/ListEditorDemo.zip
I need a bit of help with some Ruby array jujitsu.
I have the following array called #tasks:
[#<PivotalTracker::Story:0x007f9d6b8 #id=314, #url="http://www.pivotaltracker.com/", #created_at=#<DateTime: 2012-06-18T20:23:42+00:00 ((2456097j,73422s,0n),+0s,2299161j)>, #accepted_at=nil, #project_id=357031, #name="Test ", #description="This is the description for \"Test\"", #story_type="feature", #estimate=-1, #current_state="unstarted", #requested_by="joe jones", #owned_by=nil, #labels=nil, #jira_id=nil, #jira_url=nil, #other_id=nil, #integration_id=nil, #deadline=nil, #attachments=[]>, #<PivotalTracker::Story:0x007f9d6b8 #id=315, #url="http://www.pivotaltracker.com/", #created_at=#<DateTime: 2012-06-18T20:25:20+00:00 ((2456097j,73520s,0n),+0s,2299161j)>, #accepted_at=nil, #project_id=357031, #name="Test 2", #description="This is the description for \"Test 2\"", #story_type="feature", #estimate=-1, #current_state="unstarted", #requested_by="joe jones", #owned_by=nil, #labels=nil, #jira_id=nil, #jira_url=nil, #other_id=nil, #integration_id=nil, #deadline=nil, #attachments=[]>, #<PivotalTracker::Story:0x007f9d6b8 #id=316, #url="http://www.pivotaltracker.com/story/", #created_at=#<DateTime: 2012-06-18T20:25:26+00:00 ((2456097j,73526s,0n),+0s,2299161j)>, #accepted_at=nil, #project_id=357031, #name="Test 3", #description="Description for Test 3 ", #story_type="feature", #estimate=-1, #current_state="unstarted", #requested_by="joe jones", #owned_by=nil, #labels=nil, #jira_id=nil, #jira_url=nil, #other_id=nil, #integration_id=nil, #deadline=nil, #attachments=[]>]
My end goal is to create a JavaScript array in my .erb view with just the ID values in the above array.
I was thinking of trying something like:
var myJSArray = [<%= #tasks.each { |t| print t.id.to_s+", " } %>];
However, that obviously appends a "," to the end of the string, which is not desirable (i.e. it returns "314, 315, 316,". It also seems like a bit of a hack and not the right way to do it.
Any ideas on how to do this properly?
Thank you!
UPDATE: After some more research on SO, it seems I can do this in two steps:
#ids = #tasks.map { |t| t.id }
and then use that in the view with:
var myJSArray = [<%= #ids.map(&:to_s).join(", ") %>];
Not sure if this is ideal or if it could be done in a single step, though.
var myJSArray = [<%= j #tasks.map(&:id).join(',') %>];
Or you may prefer something like this:
var myJSArray = <%= #tasks.map(&:id).to_json %>;
Use Array#join :
<%=print #task.map {|t| t.id.to_s}.join(', ') %>
I am making a css grid system that relies on the concept of blocks. So I have a base file like:
$max-columns: 4;
$block-width: 220px;
$block-height: 150px;
$block-margin: 10px;
And it is used by a mixin:
#mixin block ($rows, $columns, $max-columns) {
display: block;
float: left;
margin: $block-margin 0 0 $block-margin;
box-sizing: border-box;
width: ($block-width * $columns) - $block-margin;
}
But I'd also like javascript to have access to the variables in the base file. I was thinking that I could make an invisible div, and give it the $block-width, $block-height, and $block-margin attributes and pull the values from there. But max-columns, doesn't map to anything directly, so I'd have to come up with a hacky way to render it in a div. Is there a cleaner way to share values from sass/css to javascript or vice versa?
If you use webpack you can use sass-loader to exportvariables like:
$animation-length-ms: $animation-length + 0ms;
:export {
animationMillis: $animation-length-ms;
}
and import them like
import styles from '../styles/animation.scss'
const millis = parseInt(styles.animationMillis)
https://blog.bluematador.com/posts/how-to-share-variables-between-js-and-sass/
I consider my solution to be quite hokey; but it does work...
In my _base.scss I have some variables defined:
$menu_bg: rgb(45, 45, 45);
$menu_hover: rgb(0, 0, 0);
In a menu.scss I have:
#import "base";
#jquery_vars {
.menu_bg {
background-color: $menu_bg;
}
.menu_hover {
background-color: $menu_hover;
}
}
And in a handy page template:
<span class="is_hidden" id="jquery_vars">
<span class="is_hidden menu_bg"></span>
<span class="is_hidden menu_hover"></span>
</span>
Finally this allows in a nearby jQuery script:
var menu_bg = $('#jquery_vars .menu_bg').css("background-color");
var menu_hover = $('#jquery_vars .menu_hover').css("background-color");
This is so ugly my dad is wearing a bag on his head.
jQuery can pull arbitrary CSS values from page elements; but those elements have to exist. I did try pulling some of these values from raw CSS without creating the spans in the HTML and jQuery came up with undefined. Obviously, if these variables are assigned to "real" objects on your page, you don't really need the arbitrary #jquery_vars element. At the same time, one might forget that .sidebar-left nice-menu li is the vital element being use to feed variables to jQuery.
If someone has anything else, it's got to be cleaner than this...
sass-ffi should do the trick, but the opposite way (from JS to SASS/SCSS). It will define a function called ffi-require, which allows you to require .js files from SASS:
config.js:
module.exports = {
maxColumns: 4,
};
style.scss:
$max-columns: ffi-require('./config', 'maxColumns');
Works with sass-loader (webpack) and node-sass.
You can read the sass file with a server side script, "parse" it and echo the values you need to javascript.
I would like to add that there are now several ways to share data between Sass and JavaScript using JSON. Here are some links to articles detailing various techniques:
Making Sass talk to JavaScript with JSON
SassyJSON: Talk to the browser
Sharing Data Between Sass and JavaScript with JSON
It's probably just a matter of time until JSON importing becomes supported natively in Sass.
I would recommend looking at sass-extract which uses native sass features in order to extract the computed variable values into JSON.
Also if you are using webpack the sass-extract-loader will make it very easy to just require/import the sass files as in const variables = require('sass-extract-loader!./variables.scss'); and have your sass variables in a nice JSON object.
Since it also supports #import statements you can still separate your variables in different files, and no need to add additional preprocessing or separate json files with variables.
There are many alternative ways of accomplishing this as mentioned in other answers, and which one you choose will depend on your use case and environment.
Disclaimer, I am the author of both mentioned libraries.
Another way could be to use gulp-template so you can generate any structure you want for your JavaScript.
Sharing Variables between Javascript and Sass using Gulp with gulp-template
https://youtu.be/UVeUq8gMYco
It's created from scratch so people could see it from the ground up and there is a git repo with the end result:
https://github.com/PocketNinjaCoUk/shared-js-sass-vars-using-gulp/tree/master/dev
You basically have your config object
saved at ./dev/config.js
module.exports = {
defaults: {
colours: {
primary: '#fc0'
},
sizes: {
small: '100px',
medium: '500px',
large: '1000px'
},
zIndex: {
model: 100,
dropdown: 50,
header: 10
}
}
}
Then you have both of your templates for Sass and Javascript, or less or whatever you want.
Sass underscore template
saved at ./dev/templates/sass-config.txt
<% _.each(defaults, function(category, key) { %>
// Var <%= key %>
<% _.each(category, function(value, key) { %>
$<%= key %>: <%= value %>;
<% }) %>
<% }) %>
Javascript underscore template
saved at ./dev/templates/js-config.txt
namespace.config = {};
<% _.each(defaults, function(monkey, key) { %>
namespace.config.<%= key %> = {
<% i = 1 %>
<% _.each(monkey, function(value, key) { %>
<% comma = (Object.keys(monkey).length === i) ? '': ',' %>
<% if(typeof value === 'string') {%>
<%= key %>: '<%= value %>'<%= comma %>
<%} else { %>
<%= key %> : <%= value %><%= comma %>
<% } %>
<% i++ %>
<% }); %>
};
<% }) %>
Then the gulp to compile it
var gulp = require('gulp');
var template = require('gulp-template');
var rename = require('gulp-rename');
var removeEmptyLines = require('gulp-remove-empty-lines');
var sharedVars = require('./dev/config');
gulp.task('compile', function() {
gulp.src('./dev/templates/sass-config.txt')
.pipe(template(sharedVars))
.pipe(rename('_sass-config.scss'))
.pipe(removeEmptyLines())
.pipe(gulp.dest('./dev/sass'));
gulp.src('./dev/templates/js-config.txt')
.pipe(template(sharedVars))
.pipe(rename('js-config.js'))
.pipe(removeEmptyLines())
.pipe(gulp.dest('./dev/js'));
});
This can be done using gulp-sass-vars-to-js. It generates a .js file from your .scss file. The .js file contains all variables declared in your .scss file. You can then 'require' this generated js into your .js
I'm trying to learn about new usages of javascript as a serverside language and as a functional language. Few days ago I heard about node.js and express framework. Then I saw about underscore.js as a set of utility functions. I saw this question on stackoverflow
. It says we can use underscore.js as a template engine. anybody know good tutorials about how to use underscore.js for templating, especially for biginners who have less experience with advanced javascript. Thanks
Everything you need to know about underscore template is here. Only 3 things to keep in mind:
<% %> - to execute some code
<%= %> - to print some value in template
<%- %> - to print some values HTML escaped
That's all about it.
Simple example:
var tpl = _.template("<h1>Some text: <%= foo %></h1>");
then tpl({foo: "blahblah"}) would be rendered to the string <h1>Some text: blahblah</h1>
<!-- Install jQuery and underscore -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<!-- Create your template -->
<script type="foo/bar" id='usageList'>
<table cellspacing='0' cellpadding='0' border='1' >
<thead>
<tr>
<th>Id</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<%
// repeat items
_.each(items,function(item,key,list){
// create variables
var f = item.name.split("").shift().toLowerCase();
%>
<tr>
<!-- use variables -->
<td><%= key %></td>
<td class="<%= f %>">
<!-- use %- to inject un-sanitized user input (see 'Demo of XSS hack') -->
<h3><%- item.name %></h3>
<p><%- item.interests %></p>
</td>
</tr>
<%
});
%>
</tbody>
</table>
</script>
<!-- Create your target -->
<div id="target"></div>
<!-- Write some code to fetch the data and apply template -->
<script type="text/javascript">
var items = [
{name:"Alexander", interests:"creating large empires"},
{name:"Edward", interests:"ha.ckers.org <\nBGSOUND SRC=\"javascript:alert('XSS');\">"},
{name:"..."},
{name:"Yolando", interests:"working out"},
{name:"Zachary", interests:"picking flowers for Angela"}
];
var template = $("#usageList").html();
$("#target").html(_.template(template,{items:items}));
</script>
JsFiddle Thanks #PHearst!
JsFiddle (latest)
JsFiddle List grouped by first letter (complex example w/ images, function calls, sub-templates) fork it! have a blast...
JsFiddle Demo of XSS hack noted by #tarun_telang below
JsFiddle One non-standard method to do sub-templates
In it's simplest form you would use it like:
var html = _.template('<li><%= name %></li>', { name: 'John Smith' });
//html is now '<li>John Smith</li>'
If you're going to be using a template a few times you'll want to compile it so it's faster:
var template = _.template('<li><%= name %></li>');
var html = [];
for (var key in names) {
html += template({ name: names[i] });
}
console.log(html.join('')); //Outputs a string of <li> items
I personally prefer the Mustache style syntax. You can adjust the template token markers to use double curly braces:
_.templateSettings.interpolate = /\{\{(.+?)\}\}/g;
var template = _.template('<li>{{ name }}</li>');
The documentation for templating is partial, I watched the source.
The _.template function has 3 arguments:
String text : the template string
Object data : the evaluation data
Object settings : local settings, the _.templateSettings is the global settings object
If no data (or null) given, than a render function will be returned. It has 1 argument:
Object data : same as the data above
There are 3 regex patterns and 1 static parameter in the settings:
RegExp evaluate : "<%code%>" in template string
RegExp interpolate : "<%=code%>" in template string
RegExp escape : "<%-code%>"
String variable : optional, the name of the data parameter in the template string
The code in an evaluate section will be simply evaluated. You can add string from this section with the __p+="mystring" command to the evaluated template, but this is not recommended (not part of the templating interface), use the interpolate section instead of that. This type of section is for adding blocks like if or for to the template.
The result of the code in the interpolate section will added to the evaluated template. If null given back, then empty string will added.
The escape section escapes html with _.escape on the return value of the given code. So its similar than an _.escape(code) in an interpolate section, but it escapes with \ the whitespace characters like \n before it passes the code to the _.escape. I don't know why is that important, it's in the code, but it works well with the interpolate and _.escape - which doesn't escape the white-space characters - too.
By default the data parameter is passed by a with(data){...} statement, but this kind of evaluating is much slower than the evaluating with named variable. So naming the data with the variable parameter is something good...
For example:
var html = _.template(
"<pre>The \"<% __p+=_.escape(o.text) %>\" is the same<br />" +
"as the \"<%= _.escape(o.text) %>\" and the same<br />" +
"as the \"<%- o.text %>\"</pre>",
{
text: "<b>some text</b> and \n it's a line break"
},
{
variable: "o"
}
);
$("body").html(html);
results
The "<b>some text</b> and
it's a line break" is the same
as the "<b>some text</b> and
it's a line break" and the same
as the "<b>some text</b> and
it's a line break"
You can find here more examples how to use the template and override the default settings:
http://underscorejs.org/#template
By template loading you have many options, but at the end you always have to convert the template into string. You can give it as normal string like the example above, or you can load it from a script tag, and use the .html() function of jquery, or you can load it from a separate file with the tpl plugin of require.js.
Another option to build the dom tree with laconic instead of templating.
I am giving a very simple example
1)
var data = {site:"mysite",name:"john",age:25};
var template = "Welcome you are at <%=site %>.This has been created by <%=name %> whose age is <%=age%>";
var parsedTemplate = _.template(template,data);
console.log(parsedTemplate);
The result would be
Welcome you are at mysite.This has been created by john whose age is 25.
2) This is a template
<script type="text/template" id="template_1">
<% _.each(items,function(item,key,arr) { %>
<li>
<span><%= key %></span>
<span><%= item.name %></span>
<span><%= item.type %></span>
</li>
<% }); %>
</script>
This is html
<div>
<ul id="list_2"></ul>
</div>
This is the javascript code which contains json object and putting template into html
var items = [
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
},
{
name:"name1",
type:"type1"
}
];
$(document).ready(function(){
var template = $("#template_1").html();
$("#list_2").html(_.template(template,{items:items}));
});
with express it's so easy. all what you need is to use the consolidate module on node so you need to install it :
npm install consolidate --save
then you should change the default engine to html template by this:
app.set('view engine', 'html');
register the underscore template engine for the html extension:
app.engine('html', require('consolidate').underscore);
it's done !
Now for load for example an template called 'index.html':
res.render('index', { title : 'my first page'});
maybe you will need to install the underscore module.
npm install underscore --save
I hope this helped you!
I wanted to share one more important finding.
use of <%= variable => would result in cross-site scripting vulnerability. So its more safe to use <%- variable -> instead.
We had to replace <%= with <%- to prevent cross-site scripting attacks. Not sure, whether this will it have any impact on the performance
Lodash is also the same
First write a script as follows:
<script type="text/template" id="genTable">
<table cellspacing='0' cellpadding='0' border='1'>
<tr>
<% for(var prop in users[0]){%>
<th><%= prop %> </th>
<% }%>
</tr>
<%_.forEach(users, function(user) { %>
<tr>
<% for(var prop in user){%>
<td><%= user[prop] %> </td>
<% }%>
</tr>
<%})%>
</table>
Now write some simple JS as follows:
var arrOfObjects = [];
for (var s = 0; s < 10; s++) {
var simpleObject = {};
simpleObject.Name = "Name_" + s;
simpleObject.Address = "Address_" + s;
arrOfObjects[s] = simpleObject;
}
var theObject = { 'users': arrOfObjects }
var compiled = _.template($("#genTable").text());
var sigma = compiled({ 'users': myArr });
$(sigma).appendTo("#popup");
Where popoup is a div where you want to generate the table