What want to do:
I creating ec site by ROR. What I want to do is can select the quantity each items in checkout page(Specifically, make plus and minus button then select the quantity).
The variable are #items and #user_items_quantity and Im adding index number on it.
Then what I mean, getting the (div class) in Javascript which is had index number and I want to edit quantity and show it.
But impossible to get (div class with index) in js each items. I was thinking to roop "for" in js but I can't.
Can you tell me how I work out please?
_user_basket_items.html.erb
<% #items.zip(#user_items_quantity).each.with_index(1) do | (item, uiq), index | %>
<div class="entry value-minus quantity-minus[#{index}]" id="value-minus"> </div>
<div class="entry value score-value quantity-score[#{index}]" id="score-value"><%= uiq %></div>
<div class="entry value-plus active quantity-plus[#{index}]" id="value-plus"> </div>
<% end %>
///
item_quantity.js
var add = document.getElementsByClassName( "quantity-plus" )["#{index}".to_i];
var remove = document.getElementsByClassName( "quantity-minus" )["#{index}".to_i];
var scoreValue = document.getElementsByClassName( "quantity-score" )["#{index}".to_i];
var int = 0;
add.addEventListener('click', function() {
int = parseInt(scoreValue.innerHTML, 10)+1;
scoreValue.innerHTML = int;
});
remove.addEventListener('click', function() {
int = parseInt(scoreValue.innerHTML, 10)-1;
scoreValue.innerHTML = int;
});
the css classes that you are generating looks like this:
<div class="entry value-minus quantity-minus[1]"
or
<div class="entry value-minus quantity-minus[2]"
but your javascript is looking for document.getElementsByClassName( "quantity-minus" ) with a mix of ruby syntax (["#{index}".to_i]) and js. Is your js template parsing ruby? You don't provide how index is initialized.
if it is, I would change the javascript as follows:
var add = document.getElementsByClassName( "quantity-minus[#{index.to_i}]" );
If your template isn't parsing ruby, you have to find another way.
As a side note, in your partial you are iterating over a list and giving static ids to the elements (id="value-minus") you should try avoid doing that
Related
I am making something where I have dynamic ID for element i.e something like this in my index.ejs
<% for ( var i= 0; i<100; i++ ) { %>
<th> <span class="Pr" id=price"<%=i%>">$<%= result[i]["price"] %></span></th>
<%} %>
This should generate ID which increments with i for example price1, price2 and so on..
Now I am using Socket.io to obtain new data. After obtaining the new data, I want to change the innerHTML
Traditionally, I know I can do something like
document.getElementsByID("id").innerHTML = newData;
But I knew the ID there well in advance, Here the ID obtained is when the data obtained from socket.io matches my iTH element
document.getElementById('price' + i).innerHTML
I was hoping that this would make my ID as price0, price1, price2 (when i=0,1,2) but this doesn't seem to be working.
Please do let me know how I can fix this?
There appears to be quotes in your id attribute, which would produce ids like
id=price"0"
id=price"1"
id=price"2"
As you can see that is incorrect formatting. Change to:
id="price<%=i%>"
Which will produce the correctly formatted attributes:
id="price0"
id="price1"
id="price2"
I try dynamic ID using your code it is working fine.
You can see this
for (let i = 0; i < 4; i++) {
document.getElementById('price' + i).innerHTML ="test";
}
<span class="Pr" id="price0">price0</span>
<span class="Pr" id="price1">price1</span>
<span class="Pr" id="price2">price2</span>
<span class="Pr" id="price3">price3</span>
My objective is to show a grid of products and ads between them.
warehouse.query({limit: limit, skip: skip}).$promise
.then(function(data) {
for (var i = 0; i < data.length; i++) {
var auxDate = new Date(data[i].date);
data[i].date = auxDate.toISOString();
}
Array.prototype.push.apply($scope.products, data);
//add an img ad
var warehouseElem = angular.element(document.getElementsByClassName('warehouse')[0]);
var newAd = $sce.trustAsHtml('<img src="/ad/?r=' + Math.floor(Math.random()*1000) + '"/>');
warehouseElem.append(newAd);
skip += 9
});
Doesn't work.
I already tried simply using pure javascript like,
var warehouseElem = document.getElementsByClassName('warehouse')[0];
var newAd = document.createElement('img');
warehouseElem.appendChild(newAd);
Also doesn't work.
I suppose I need to do something with angular, can't find out what. I think it's sanitize but maybe I just don't know how to use it.
Remember I need to inject an img every once in a while between products.
This is a job for ng-repeat!
<div ng-repeat="data in datas">
<div>[show data here]</div>
<img src="/ad/?r=' + Math.floor(Math.random()*1000) + '"/>
</div>
If you have bind your "datas" in scope and Math too like this in your controller like this it should works
$scope.datas // this is your list of products
$scope.Math = Math;
If you don't want to spam add for each line you can use ng-if with $index like this :
<div ng-if="$index%2==0">
<img src="/ad/?r=' + Math.floor(Math.random()*1000) + '"/>
</div>
This will make it display add every 2 lines.
Since you seemed to come from a jQuery-like (or native DOM manipulation) background, I suggest you to read that post : "Thinking in AngularJS" if I have a jQuery background?.
This will explain you why in angular you almost don't manipulate DOM and quite some other things (only in directives).
EDIT : to fix the grid problem, just merging my two html block build your array of datas like this :
$scope.myArray = [product[0], ad[0] or just an empty string it will work still, product[1], ad[1]]
And the html
<div ng-repeat="data in datas">
<div ng-if="$index%2==0">[show data here]</div>
<img ng-if="$index%2==1 src="/ad/?r=' + Math.floor(Math.random()*1000) + '"/>
</div>
In AngularJS you should generally avoid doing DOM manipulation directly and rather rely on angular directives like ng-show/ng-hide and ng-if to dynamically hide sections of a template according to the specific case.
Now back to the problem at hand.
Assuming that you are trying to render a list of products loaded with the code displayed above and display an ad for some of them, you can try the following.
<!-- place the img element in your template instead of appending -->
<div ng-repeat="product in products">
<!-- complex product template-->
<!-- use ng-if to control which products should have an ad -->
<img ng-src="product.adUrl" ng-if="product.adUrl" />
</div>
Then in your controller set adUrl for products that should have an ad displayed.
warehouse.query({limit: limit, skip: skip}).$promise
.then(function(data) {
for (var i = 0; i < data.length; i++) {
var hasAd = // set to true if this product should have an add or not
var auxDate = new Date(data[i].date);
data[i].date = auxDate.toISOString();
if(hasAd){
data.adUrl = "/ad/?r=" + Math.floor(Math.random()*1000);
}
}
Array.prototype.push.apply($scope.products, data);
skip += 9
});
I am most probably assuming too much. If that is the case please provide more details for your specific case.
If you declare a scope variable,
$scope.newAd = $sce.trustAsHtml('<img src="/ad/?r=' + Math.floor(Math.random()*1000) + '"/>');
and in your HTML template, have a binding like
<div ng-bind-html="newAd"></div>,
it should work.
I have a app that has jobs and messages. When somebody enters a message for a job, I want to append the message to the list of messages in the jobs specific show page.
Ex: If a user add a message to job 7, then I want to append that message to the <ul> with class msg_list for the url /jobs/7
The way I have it set up, all jobs share a show page, and can be accessed with the URL /jobs/:id. Where id is the specific job id.
<div class="show_messages">
<div class="row">
<div class="small-12 columns">
<% messages = #job.messages %>
<ul class="msg_list">
<% messages.each do |m| %>
<% login = m.user_id.present? ? m.user.login : m.runner.login %>
<li class="message">
<p>
<strong><%= login %></strong>: <%= m.body %><br>
<span id="sent-at">Sent on <%= m.created_at.strftime("%b %d at %l:%M%p") %></span>
</p>
</li>
<% end %>
</ul>
<% end %>
</div>
</div>
And when I try to append a message like so:
function addMessage(msg) {
var new_msg = "<li><p> NEW TEST MESSAGE </p></li>";
$('.msg_list').append(new_msg);
}
I am appending it to every jobs show, when I only want to append it to 1 specific job (Ex: job with id 7)
Can anyone help me figure out how to do this?
You can change this to include the id:
<ul class="msg_list_<%= id %>">
And then the selector looks like this:
$('.msg_list_' + id).append(new_msg);
You have to figure out how the javascript knows the id and how to get the id set when the template language builds the html.
Get the URL segment, then use .eq() to get the job you want.
var pathArray = window.location.pathname.split( '/' );
var id_from_url = pathArray[2];
$('.msg_list').eq(id_from_url).append(new_msg);
Here is a guide on getting the URL segment with javascript.
Note: .eq() is zero-based, so you may have to offset your id if it is not zero-based.
Both solutions work, but to solve it I instead checked the url and only added the msg to the list if the id's matched
function addMessage(msg) {
// var body = msg["body"];
// $('.msg_list').append(body)
var new_msg = "<li><p> NEW TEST MESSAGE </p></li>";
$('.msg_list').append(new_msg);
}
var msgChannel = pusher.subscribe('msg_channel');
msgChannel.bind('msg_added', function(msg) {
// if (msg["job_id"] == '#job.id'){
// addMessage(msg)
// }
var message_job_id = msg["job_id"];
var url = window.location.pathname;
var job_show_id = url.substring(url.lastIndexOf('/') + 1);
var show_integer = parseInt(job_show_id);
if(message_job_id == show_integer) {
addMessage(msg);
}
});
});
<div id="team-name">{{teams[0].name}}</div>
<button id="next">Next</button>
When the "next" button is hit I would like the team-name to be the next team name in the list, i.e. 0 becomes 1?
I have a feeling I need JS to do this - but I am not sure how I would use JS to do this.
Also, the list is generated from the server.
UPDATE
{{ }} is part of a templating system - Jinja2 to be precise.
The teams list is passed into the webpage through Jinja2 - so the webpage has access to the entire teams list - I hope that makes sense.
class Team(db.Model):
__tablename__ = 'Team'
name = db.Column(db.String(21))
matches_total = db.Column(db.Integer())
matches_won = db.Column(db.Integer())
matches_lost = db.Column(db.Integer())
Make a list containing the names available as team_names and update your template like this:
<div id="team-name" data-index="0" data-entries="{{ team_names|tojson }}">{{teams[0].name}}</div>
<button id="next">Next</button>
In case you are using flask which seems to be the case, pass this to your render_template() call:
team_names=[t.name for t in Team.query]
Then you can use the following jQuery snippet to do what you want:
$('#next').on('click', function(e) {
e.preventDefault();
var nameElem = $('#team-name');
var entries = nameElem.data('entries');
var index = (nameElem.data('index') + 1) % entries.length;
nameElem.text(entries[index]).data('index', index);
})
Note: This answer assumes the list is not too big.
i am attempting to do a dynamic select here using rails and jquery. the code is as follows
<div class = "line_items">
<%- calc = Hash[Item.all.map{|p| [p.id, p.quantity]}].to_json %>
<div class = "item"><%= f.collection_select :item_id,Item.all,:id,:title, :prompt => "Select a Item", input_html: {data:{calc: calc} %></div>
<div class ="quantity"> <%= f.text_field :quantity %></div>
/*rest of code*/
</div>
javascript for the view is as follows
jQuery(document).ready(function(){
jQuery('.item').bind('change',function() {
var selectElement = jQuery(this);
var itemSelected = jQuery('.item:selected').val();
var wrapperDivElement = selectElement.parent(".line_items");
var quantity= eval(selectElement.data("calc"))[itemSelected];
jQuery(".quantity", wrapperDivElement).val(quantity);
});
});
when i change the item i am getting the following error
eval(selectElement.data("calc"))[itemSelected] is undefined in firebug. Can anyone point out where i am going wrong? also any better way to get the quantity. i feel the method i am doing is crude. any guidance would be helpful. Thanks in advance.
jQuery(document).ready(function(){
jQuery('.item select').bind('change',function() {
var selectElement = jQuery(this);
var itemSelected = selectElement.val();
var wrapperDivElement = selectElement.parents(".line_items");
var quantity= eval(selectElement.data("calc"))[itemSelected];
jQuery(".quantity input", wrapperDivElement).val(quantity);
});
});
i guess the value of itemSelected was not getting detected, hence the error. the above redefined code should work. But i strongly urge you not to get data like this. its better to do a json call or ajax call to get the related data from the controller. and use parents instead parent in the code:)
Ik think you mixup the reference by index and the reference by ID. I'm no Javascript wizard, but you fill the hash with a list of . Then you get the 'val'-value in the change event. That contains an integer, but that is not the index.
Then you request in Javascript the item with index val, not the item with value val. Maybe javascript cannot distinguish between them because both need an Int...
it's this line:
var quantity= eval(selectElement.data("calc"))[itemSelected];