Right now I loop through my RoR objects and create a new jquery function for each separate object. I understand this is extremely inefficient and amateurish, and I would like to have just one jquery function that handles this operation.
<% #startups.each do |startup| %>
<div class="panel-body showVideo<%=startup.id%>">
...
</div>
<div class="panel-body theVideo<%=startup.id%>">
...
</div>
<script type="text/javascript">
$(function () {
$('.theVideo<%=startup.id%>').hide();
$('.showVideo<%=startup.id%>').on('click', function () {
$('.theVideo<%=startup.id%>').show();
$('.showVideo<%=startup.id%>').hide();
});
});
</script>
<% end %>
Basically this code waits for a user to click the div, and hides that div while showing another div. The code currently works, but I don't want to create tons of functions when it could be one!
I think you just need to give the JS access to your IDs. You could do that by writing them into your HTML like this:
<script type="text/javascript">
// Write the IDs into the script tag
var ids = [ <% #startups.each do |startup| %>
<%=startup.id%>,
<% end %>];
// Loop over the IDs
for (var i=0; i<ids.length; i++) {
var id = ids[i];
// Do your stuff here
}
</script>
Of course add some logic to make sure not to add a comma to the last one.
Related
I'm writing a twitter aggregator and I need some help on solving the error 'Uncaught ReferenceError: sqTweetData is not defined.' It looks like console is pointing me to my for loop. I have set up a partial that is compiled and loaded in #main-content using underscore js.
For Loop Code
<!-- Main Content -->
<main class="main">
<div class="container-flex" id="main-content"></div>
</main> <!-- End Main Content -->
<!-- Current Tweet Partials -->
<script id="active-tweet-partial" type="underscore/template">
<section class="tweetFlexItem">
<% for (var i = 0; i < sqTweetData.length; i++) { %>
<div class="activeTweet">
<div class="activeTweet__avatar"><img src="<%= sqTweetData[ i ].user.profile_image_url %>"></div>
<div class="activeTweet__wrapper">
<div class="activeTweet__name"> <%= sqTweetData[ i ].user.name %> </div>
<div class="activeTweet__message"><%= sqTweetData[ i ].text %></div>
</div>
</div>
<% } %>
</section>
</script>
home.js Compiling Code
var Home = (function() {
var sqTweetData = {
user: [{
profile_image_url : "assets/avatar.png",
name : "#johnsnow"
}],
text : "Someone once said that I know nothing..."
};
console.log("this is sqTweetData", sqTweetData);
// Partials
var tweetPartial = $('#active-tweet-partial').html();
tweetPartialCompiled = _.template( tweetPartial );
// DOM Handlers
// KICKSTART VIEW
function initHome() {
// load main content
$('#main-content').html(tweetPartialCompiled( sqTweetData ));
// bind events
}
return {
init: initHome
};
})();
The console.log on line 11 works just fine, so I'm assuming my variable object is set up correctly. There just seems to be a disconnect between the partial and the rest of the javascript.
Any thoughts?
This is a scoping issue. sqTweetData says it's undefined because it's exactly that. window["sqTweetData"] does not exist. When you declare a variable outside of a function it's inserted into the global namespace, in this case the browser window is the namespace.
Since you're declaring the variable inside of home using the var keyword, the variable will only be accessible within the Home function. So you'd have to add it as either a this.sqTweetdata and return it with the object, or add a separate getTweetData() that return the variable, or something along those lines.
Check out this answer that covers scoping very comprehensively:
https://stackoverflow.com/a/500459/3629438
Yours falls under:
Advanced: Closure
var a = 1;
var six = (function() {
var a = 6;
return function() {
// JavaScript "closure" means I have access to 'a' in here,
// because it is defined in the function in which I was defined.
alert(a);
};
})();
EDIT:
In your case you would do something along the lines of
var Home = (function() {
// ....... //
function getTweetData() {
return sqTweetData;
}
return {
init: initHome,
get: getTweetData
};
})();
When i call my function in my js.erb template it replaces the entire page rather than just the div that i indicate. Can anyone help?
remove.js.erb
$('div.mini-basket-wrapper').html("<%= j(render 'shop/baskorder/mini_basket') %>");
#This replaces the page completely
$('#basket-info').load(document.write(basket_text()));
view
<div id="basket-info">
<div id="basket-amount">
<div class='mini-basket-icon'>
<%= image_tag 'shop/icons/basket.svg', alt: '' %>
</div>
<script type='text/javascript'>
document.write(basket_text());
</script>
</div>
</div>
JS
function fc_basket_text_from_cookie(empty_text, normal_text)
{
var basket = readCookie('bk');
if (basket)
{
var parts = decodeURIComponent(basket.replace(/\+/g, '%20')).split('|')
if (parseInt(parts[1]) == 0)
return normal_text.replace(/##VALUE##/g, parts[0]).replace(/##ITEMS##/g, parseInt(parts[1]));
// return empty_text
else
return normal_text.replace(/##VALUE##/g, parts[0]).replace(/##ITEMS##/g, parseInt(parts[1]));
} else {
return '';
}
}
var emptyBasketHTML = "<span class='header_text'>Items in basket: 0 Total: £0.00</span>";
function basket_text(){
var populated = "<span class='header_text'>Items in basket: ##ITEMS##</span><span class='header_text'>Total: ##VALUE##</span>";
//populated += "<input type='submit' value='Checkout' name='commit' class='go_botton header-checkout-button'>"
return fc_basket_text_from_cookie(emptyBasketHTML,populated);
}
Reading here: Using document.write.
When the page finishes loading, the document becomes closed. An attempt to document.write in it will cause the contents to be erased.
Further the .load() function is used to load data from a server. I believe you want the .html function.
[Untested] Change the line $('#basket-info').load(document.write(basket_text()));
To $('#basket-info').html(basket_text());
Thanks for all the input. Realised what i was doing wrong and decided to add an ajax on success to my js file:
$(document).on('ajaxSuccess', function(){
$('#basket-amount-info').html(basket_text());
});
Added this id #basket-amount-info to contain the script in the view.
I am using will_paginate in order to list huge number of files. I also have a check_box in order to choose files for the futher analysis.
To save the ids in the cookie while changing the pages I used following javascript:
<script src="/assets/jquery.cookie.js" type="text/javascript"></script>
<script type="text/javascript">
var checkedIds = []
$('.checkmeplease').on("change", function(){
if($(this).is(':checked')) {
checkedIds.push($(this).val())
}
else {
checkedIds.splice(checkedIds.indexOf($(this).val()), 1);
}
$.cookie('checked_file_ids', checkedIds,{path:'/'});
});
</script>
My checkboxes:
<% #files.each do |file| %>
<p><td> <%= check_box_tag "files[]", file.id,false,:class=>'checkmeplease' %></td> <%= file.name %></p>
<%end%>
It saves the IDs but when I change the page with will_pagination, the saved IDs disappear.
I was introduced as well this code:
<script type="text/javascript">
var checkedIds = $.cookie('checked_file_ids');
$('p td input[type=checkbox]').on('change', function () {
if($(this).is(':checked')) {
checkedIds.push($(this).val())
}
else {
checkedIds.splice(checkedIds.indexOf($(this).val()), 1);
}
$.cookie('checked_file_ids', checkedIds);
})
</script>
But $('p td input[type=checkbox]').on('change', function () does not seem to work.
Where is my mistake? How can I save the IDs from the previous pages as well?
Thanks a lot.
Use your first example, but instead of this line:
var checkedIds = []
Use this line from your second example.
var checkedIds = $.cookie('checked_file_ids');
The problem I see with the first example is that you never load the previously saved ids from the cookie. So, come page two, when you check some, you're overwriting the previous values.
I'm trying to assign a javascript variable data from ror. I already made the query, and it gives me what i want (a single integer), but i can't assign it to a js variable. Here is the js script i'm using:
<script type="text/javascript">
$(function() {
var number_of_products = '<%= Post.where(:id => 1).select(:amount).pluck(:amount)[0] %>';
$(document).ready(function(){
for (i=1; i <=number_of_products; i++) $('#itemsAmount').append('<img src="images/box.svg"/>');
})
})
</script>
But i can't seem to pass the ror value (<%= Post.where(:id => 1).select(:amount).pluck(:amount)[0] %>) to the js variable (number_of_products)
Any ideas?
Thanks,
Shai.
It would seem your problem isn't with getting the value into javascript, its simply the wrong type try and change your code to the following
<script type="text/javascript">
$(function() {
var number_of_products = <%= Post.where(:id => 1).select(:amount).pluck(:amount)[0] %>;
$(document).ready(function(){
for (i=1; i <=number_of_products; i++) $('#itemsAmount').append('<img src="images/box.svg"/>');
})
})
</script>
Edit:
Do you have #itemsAmount on your page?
Working fiddle http://jsfiddle.net/DeMd2/
I am creating a web application framework to be used by other groups in my department to standardize the UI of our web apps. It's written in javascript using HTML templating through underscore.js. In order for the app to be totally extensible however, I'd like them to be able to extend HTML templates as they see fit without modifying the source.
Source
templates.html
...
<script type="text/template" id="fooTemplate">
<div class="foo">
</div>
</script>
<script type="text/template" id="barTemplate">
<p>Bar!</p>
</script>
...
Implementation
newTemplates.html
...
<!-- Only overwrite foo, not bar !-->
<script type="text/template" id="fooTemplate">
<ul class="foo">
<li class="bar">Blah!</li>
</ul>
</script>
...
Is there a way to intuitively enable users to extend HTML templates without forcing them to overwrite the file and copy/paste the templates they're not modifying?
You're not going to be able to use id attributes to identify your templates without a bunch of server-side processing to take care of the uniqueness issues. But you can use classes to identify your templates.
If you mash all your template HTML files together in override order ("base" templates first, "subtemplates" after) and use class attributes to identify the templates:
<!-- templates.html -->
<script type="text/template" id="fooTemplate">
<div class="foo">
</div>
</script>
<script type="text/template" id="barTemplate">
<p>Bar!</p>
</script>
<!-- newTemplates.html -->
<script type="text/template" id="fooTemplate">
<ul class="foo">
<li class="bar">Blah!</li>
</ul>
</script>
Then you can use things like
var foo = _.template($('.fooTemplate:last').html());
var bar = _.template($('.barTemplate:last').html());
to access your templates.
Demo: http://jsfiddle.net/ambiguous/gYHkF/
You could also stick with ids and try to load templates from newTemplates.html first and fallback to templates.html if you don't find it. If you load the template files into two separate variables but don't insert them into the DOM:
var $base = $('stuff from templates.html');
var $subs = $('stuff from newTemplates.html');
Then add a simple function to look for templates in $subs before $base:
function tmpl(id) {
var $t = $subs.filter('#' + id);
if($t.length)
return _.template($t.html());
return _.template($base.filter('#' + id).html());
}
Then you could do this:
var foo = tmpl('fooTemplate');
var bar = tmpl('barTemplate');
and The Right Thing would happen.
Demo: http://jsfiddle.net/ambiguous/EhhsL/
This approach also makes it easy to cache the compiled templates and not only avoid double lookups but avoid compiling the same thing over and over again:
function tmpl(id) {
if(tmpl.cache.hasOwnProperty(id))
return tmpl.cache[id];
var $t = $subs.filter('#' + id);
if($t.length)
return tmpl.cache[id] = _.template($t.html());
return tmpl.cache[id] = _.template($base.filter('#' + id).html());
}
tmpl.cache = { };
Demo: http://jsfiddle.net/ambiguous/YpcJu/