Error "undefined is not a funcion" when performing a $.find - javascript

guys,
once again I come begging for help. I can't get my head around this - all I need is update the name attribute's indexes of texts in my form. I want to show to the user one line in the table and, as he/she clicks on the plus sign, another line is added.
For my form to work on the server side, I need do reindex the name parameter. That's how I'm trying to do it:
function reindex () {
$("table").find("tr.faixa").each(function(index, value) {
value.find("input.faixa_min").attr("name", "data[" + index + "][BonusRecarga][faixa_min]"); // <- here's where I get the error
value.find("input.faixa_max").attr("name", "data[" + index + "][BonusRecarga][faixa_max]");
value.find("input.valor_bonus").attr("name", "data[" + index + "][BonusRecarga][valor_bonus]");
value.find("input.perc_bonus").attr("name", "data[" + index + "][BonusRecarga][perc_bonus]");
});
}
And here's a fragment of the form:
<tr class="faixa">
<td>
<input name="data[0][BonusRecarga][faixa_min]" class="faixa_min" type="text" required="required"/>
</td>
<td>
<input name="data[0][BonusRecarga][faixa_max]" class="faixa_max" type="text" required="required"/>
</td>
<td>
<input name="data[0][BonusRecarga][valor_bonus]" class="valor_bonus" type="text" required="required"/>
</td>
<td>
<input name="data[0][BonusRecarga][perc_bonus]" class="perc_bonus input-small" type="text" required="required"/>
</td>
<td>
<span class="help-inline"><i class="icon-plus" style="cursor: pointer"></i></span>
<span class="help-inline"><i class="icon-minus" style="cursor: pointer"></i></span>
</td>
</tr>
I have also tried using a common for(var i = 0; i < $(".faixa").lenght; i++), but I always get the "undefined is not a function" in the first value.find(...). It looks like "value" doesn't have the find() method, but when I log it, it shows me a regular "....
Do you know why the find() method is not working on the "value" variable?
Thanks in advance.

value is a DOM Element object while .find() is a jQuery method. You need to create a jquery object :
var $value = $(value);
//Then use $value.find()

Try using $(this).find('input...')... . it works, and is better inside each.

A much faster result for jquery would be to use the EQ function and append.
https://api.jquery.com/eq/
if EQ is not what your looking for leave a comment and ill look further into your issue.

Related

Null response when parsing value to a function, stuck on it. Is there something I am missing?

I am developing a Point of Sale cart module to sharpen my javascript skills. I am currently trying to calculate the total of a product, by multiplying it's price and quantity needed. I am loading the products through a json file, so far so good. But I have a problem in this javaScript function. The quantity is being loaded through the HTML as shown below.
function AddToCart(food){
const row = document.createElement('tr');
row.innerHTML = `
<tr data-price='100' data-quantity='5' id='${food.FoodID}'>
<td>
${food.FoodName}
</td>
<td id = "PriceFood">
${food.FoodPrice}
</td>
<td>
**<input class='' id="Quantity" type="number" min="1" max="9" step="1" value="1">**
</td>
<td id="ProductTotal">
**${getTotal(food.FoodPrice, document.getElementById('#Quantity'))}**
</td>
<td>
X
</td>
</tr>
`
;
cart_content.appendChild(row);
}
function getTotal(price, qty){
console.log(price, qty)
return Number(price) * qty;
}
By running the result above in the console. console.log(price, qty) returns this :-
100 null
The price is being parsed but it returns a null for the quantity.
I can't really be able to identify the problem why it returns a null.
Some help would be appreciated..thanks :)
.getElementById() does just that, gets an element by its id. Because of this you don't prepend the id with a # as you are doing here:
document.getElementById('#Quantity')
Remove the # to correctly get the quantity element.
Then, to get the value of that element, you'll need to use qty.value, not just qty.
getElementById, assumes you are supplying the ID of the DOM node, therefore you don't need to precede it with #.
With that, you would be returning just the DOM node to the function - you need to return the value of the input, like so:
**${getTotal(food.FoodPrice, document.getElementById('Quantity').value)}**

How to initialize checkbox from hashmap (Angular 2)

How can we initialize and manipulate a check box value? I've looked at quite a number of examples, but haven't been able to get any to work.
I'm trying to present a N x M table where the rows represent tasks, and the columns students. The idea is that checking one of the check-boxes in the table assigns a task to a student.
There is a typescript hash map which contains the value of all the checkboxes;
assigned : { [key:string]:boolean; } = {};
the hash key is:
var key = a.TaskId + '_' + a.StudentId;
The table is generated with a nested ngFor:
<tr *ngFor="let t of tasks">
<td>Task Name for task... {{t.taskId}} </td>
<td *ngFor="let s of students">
<input type="checkbox" name=#{{t.TaskId}}_{{s.StudentId}} change="onAssignmentChange(t,s)" [checked]="cbValue(t, s)">
</td>
</tr>
the cbValue(t, s) is below:
cbValue(taskItem, studentItem) {
var key = taskItem.TaskId + '_' +studentItem.StudentId;
return this.assigned[key];
}
This doesn't work, all the checkboxes in the table come up unchecked, no matter what the values in the hash.
I've also tried:
<input type="checkbox" change="onAssignmentChange(t,s)" [checked]="cbValue(t, s)">
<input type="checkbox" change="onAssignmentChange(t,s)" [(ngModel)]={{t.TaskId}}+'_'+{{s.StudentId}} >
<input type="checkbox" change="onAssignmentChange(t,s)" [(ngModel)]="assigned[t.TaskId"+'_'+"s.StudentId"]>
none of which works.
I seem to be quite in the dark here; onAssignmentChange doesn't get triggered either, there are no Errors in console.
Also,
... name=#{{t.TaskId}}_{{s.StudentId}} ...
is this supposed to be a local target or something?
thanks in advance
This is fairly trivial as we're just going to bind straight to the assigned object, since we're using JavaScript's bracket property accessor we also get a free dynamic instantiation of the property through the template (dirty, maybe, but powerful). Additionally, wherever you're processing this later assume that a missing value is false:
template
<tr *ngFor="let t of tasks">
<td>Task Name for task... {{t.taskName}} </td>
<td *ngFor="let s of students">
<input type="checkbox" name="{{t.taskId}}_{{s.studentId}}" [(ngModel)]="assigned[t.taskId + '_' + s.studentId]">
</td>
</tr>
Here's a plunker to demonstrate: http://plnkr.co/edit/9RorhJnv42cCJanWb80L?p=preview

Check if all input/select fields of a table row have values

There is a table with some input and select fields in a row. I want to check if all input and select fields of an row have a value. This is how I would think to do that, but do I have to use closest and find? I think this is not optimal.
HTML
<table>
<tr>
<td><select><option></option><option>Select anything</option></td>
<td><input type="text" name="field1"></td>
<td><input type="text" name="field2"></td>
</tr>
<tr>
<td><select><option></option><option>Select something</option></td>
<td><input type="text" name="field3"></td>
<td><input type="text" name="field4"></td>
</tr>
</table>
JS
'change #table input, change #table select': function(event) {
var $this = $(event.currentTarget),
$row = $this.closest('tr'),
$elements = $row.find('input, select');
var empty = false;
$elements.each(function(index) {
if (!$(this).val()) empty = true;
});
if (empty)
console.log('some value is missing')
else {
console.log('valide');
// do something with values
}
}
There are really two questions here:
Most optimal method to select all inputs in a table row
Ensure all the inputs have a value
For the first question there is a subliminal side to that. Ensure that it IS an input and then select it within the context of the current row of the changed input.
First off, jQuery uses the Sizzle (https://sizzlejs.com/) engine under the covers for selection. One thing to be aware of is the "right to left" processing of the selector string by that engine.
Thus the most optimal selection is somewhat browser specific but the fastest way to select is an ID followed in modern browsers by a class. Some older browsers do not select by class as well but let's leave that for your research.
Selection: Bad way to do stuff
So given that, let's look at a complex selector that you might use:
'div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td select, div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td input'
First off DO NOT USE THAT. Now to explore why not: Remember we talked about the "right to left" selector processing? For discussion let us narrow down out selector to the last part:
"div.mycontainer div.mytablecontainer>table#mytable.mytableclass tr td input"
What this does then in starting on the right:
"find all the inputs in the DOM",
use that list of those inputs, "find all the inputs in a td element
use those td elements, find all those in a tr
find all those tr in a .mytableclass element
find all those in an element with an id of mytable (remember this ID MUST be unique)
Now keep going, find that single element id that is a table element
That is an immediate child of an element with classmytablecontainer
That is a DIV element div
That is a child of an element with class mycontainer
That is a DIV element div
Whew that's a lot of work there. BUT we are NOT DONE! We have to do the same thing for the OTHER selector in there.
Selection: Better way to do stuff
NOW let's do this better; first off let's leverage the modern browser class selector by adding a class to all our "scoped" inputs - things we want to check for entry.
<input class="myinput" />
It does really need a type="" attribute but ignore that for now. Let's use that.
$('#mytable').find('.myinput');
What this does is:
Select the element with ID of 'mytable' which is the FASTEST selector in all browsers; we have already eliminated those 47 other tables in our DOM.
Find all the elements with a class of class="myinput"; within that table; in modern browsers this is also very fast
DONE. WOW! that was SO much less work.
Side note on the .find() instead of "#mytable input"
Remember our right to left once again? Find all inputs in the DOM, then narrow to those inputs we found that are in that table NO STOP THAT right now.
Or (better likely) "#mytable .myinput"
SO our "rules" of selecting a group of elements are:
Use an ID to limit scope to some container if at all possible
Use the ID by itself NOT part of a more complex selector
FIND elements within that limited scope (by class if we can)
Use classes as modern browsers have great selection optimization on that.
When you start to put a space " " or ">" in a selector be smart, would a .find() or .children() be better? In a small DOM perhaps maintenance might be easier, but also which is easier to understand in 4 years?
Second question: not specific but still there
You cannot simply globally use !$(this).val() for inputs.
For a check box that is invalid. What about radio buttons? What about that <input type="button" > someone adds to the row later? UGH.
SO simply add a class to all "inputs" you DO wish to validate and select by those:
<input type="text" class="validateMe" />
<select class="validateMe" >...
Side note you MIGHT want to sniff the TYPE of the input and validate based upon that: How to get input type using jquery?
EDIT: Keep in mind your validation input MIGHT have a "true/false" value so then this might fail: !$(this).val() (radio buttons, checkbox come to mind here)
Some code and markup:
<table id="mytable">
<tr>
<td>
<select class="myinput">
<option></option>
<option>Select anything</option>
</select>
</td>
<td>
<input class="myinput" type="text" name="field1" />
</td>
<td>
<input class="myinput" type="text" name="field2" />
</td>
</tr>
<tr>
<td>
<select class="myinput">
<option></option>
<option>Select something</option>
</select>
</td>
<td>
<input class="myinput" type="text" name="field3" />
</td>
<td>
<input class="myinput" type="text" name="field4" />
</td>
</tr>
</table>
<div id="results">
</div>
probably NOT want a global (namespace the "selectors")
var selectors = '.myinput';
$('#mytable').on('change', selectors, function(event) {
var $this = $(event.currentTarget),
$row = $this.closest('tr'),
$elements = $row.find(selectors);
var $filledElements = $elements.filter(function(index) {
return $(this).val() || this.checked;
});
var hasEmpty = $filledElements.length !== $elements.length
var rowIndex = $row.index();
$('#results').append("Row:" + rowIndex + " has " + $filledElements.length + ' of ' + $elements.length + ' and shows ' + hasEmpty + '<br />');
if (hasEmpty)
console.log('some value is missing');
else {
console.log('valide');
// do something with values
}
});
AND something to play with: https://jsfiddle.net/MarkSchultheiss/fqadx7c0/
If you're only selecting on particular element with knowing which parent to select with, you should try using .filter() to filter out only element that did't have a value like following :
$('button').click(function() {
var h = $('table :input').filter(function() {
return $(this).val() == "" && $(this);
}).length;
alert(h);
});
DEMO
I did this plunk
https://plnkr.co/edit/q3iXSbvVWEQdLSR57nEi
$(document).ready(function() {
$('button').click(function() {
var table = $('table');
var rows = table.find('tr');
var error = 0;
for (i = 0; i < rows.length; i++) {
var cell = rows.eq(i).find('td');
for (a = 0; a < cell.length; a++) {
var input = cell.eq(a).find(':input');
if (input.val() === "") {
input.css("border", "solid 1px red");
error++;
} else {
input.css("border", "solid 1px rgb(169, 169, 169)");
}
}
}
if (error > 0){
alert('Errors in the form!')
return false;
} else {
alert('Form Ok!')
return true;
}
})
})
Simple Jquery validation, searching all the inputs (including selects), if it's null, increment the error counter and change class. If the error counter is > 0, alert error and return false;
Maybe isn't the best solution, but it sure can help get started.

Checking variable for multiple inputs after onChange event

So I have a dynamic form that has two columns. One has a job name and the other has an input box where the user could enter their on description of the job.
while($install_table_r = tep_db_fetch_array($install_table_query))
{
echo'
<tr class="dataTableRow">
<td class="dataTableContent">
<input type="text" id="job_name" name="job_name"
value="'.$install_table_r['name_of_job'].'" disabled />
</td>
<td class="dataTableContent">
<input type="text" name="job_desc" value="'.$install_comment['comment'].'"
onChange="insertCommentInstall(this.value,)" />
</td>
</tr>
';
}
So as you can see I have a while loop that populates this form. So it could potentially have a lot of input boxes that you can use to describe the jobs.
The issue I am having is that, when I handle this form with the AJAX I have set up. The javascript simply grabs the last job on the list and uses that as it's jobs name. So in essence it is grabbing the input box correctly it's just placing it in the wrong row.
Here is the javascript that handles this change.
var job = document.getElementsByNames("job_name").value;
var comment = document.getElementsByNames("job_desc").value;
var url = "<?php echo FILENAME_ORDERS_EDIT_AJAX; ?>?action=insert_comment_install&oID=<?php
echo $_GET['oID']; ?> &new_comment=" + value + "&jobname=" + job;
I know I should be grabbing the elements with getElementByNames but I just don't know how to pair up the comment with the proper job that it's supposed to go with. So if someone comments next to the input box for Granite Job the comment should be paired up with the job name 'Granite Job' in the database. Instead currently it will just be paired up with the last job on the list which is 'Cabinet Assembly'.
Any help would be appreciated.
First of all, you have a HTML error for the attribute id
You may not in HTML standards to give a same value for id attribute to a multiple elements.
But fortunately we can use this unique identifier to make your code works
You can edit your PHP code to some thing like this:
$counter=0;
while($install_table_r = tep_db_fetch_array($install_table_query))
{
echo'
<tr class="dataTableRow">
<td class="dataTableContent">
<input type="text" id="job_name_'.$counter.'"
value="'.$install_table_r['name_of_job'].'" disabled />
</td>
<td class="dataTableContent">
<input type="text" id="job_desc_'.$counter.'" value="'.$install_comment['comment'].'"
onChange="insertCommentInstall(this.value,'.$counter.')" />
</td>
</tr>
';
$counter++;
}
You can see we added a counter to identify our rows
Updating your Javascript code will be as follow:
var insertCommentInstall=function(value,identifier){
var job = document.getElementById("job_name_"+identifier).value;
var comment = document.getElementById("job_desc_"+identifier).value;
var url = "<?php echo FILENAME_ORDERS_EDIT_AJAX; ?>?action=insert_comment_install&oID=<?php echo $_GET['oID']; ?> &new_comment=" + value + "&jobname=" + job;
}
When you use a selector like getElementsByClassName or getElementsByTagName you are retrieving a nodelist of all elements with the specified attribute (adding a classname to your inputs would make this easier). You need to specify one particular node out of the nodelist in order to fetch it's value. In order to retrieve all values in your nodelist you need to loop through it and push the values of all its nodes into an array.
//finds all elements with classname "jobs"
var jobs = document.getElementsByClassName("jobs");
//create new array that we push all the values into
var jobValues = [];
//loop through our jobs nodelist and get the value of each input
for (var i = 0; i < jobs.length - 1; i++) {
jobValues.push(jobs[i].value);
}
jobValues; //gives you a list of all the values you pushed into the array
jobValues[5]; //gives you the value of the 6th input you looped through

How do I use two .siblings with the same input type?

I want to get "The walking dead" also but it only gets the first hidden. Can i put a class on .this or how should I do?
$(".articel input[type='button']").click(function(){
var price = $(this).siblings("input[type='hidden']").attr("value");
var quantity = $(this).siblings("input[type='number']").attr("value");
var name = $(this).siblings("input[type='hidden']").attr("value");
var ul = document.getElementById("buylist");
var prod = name + " x " + quantity + " " + price + "$";
var el = document.createElement("li");
el.innerHTML = prod;
ul.appendChild(el);
<form class="articel">
Quantity: <input type="number" style="width:25px;"><br>
Add to cart: <input type="button" class="btn">
<input type="hidden" value="30">
<input type="hidden" value="The walking dead">
</form>
The conventional way to identify form fields is by the name property.
HTML:
<input type="hidden" name="title" value="The walking dead">
jQuery:
var name = $(this).siblings('input[name=title]').val();
Your current selector, siblings("input[type='hidden']"), selects all hidden field siblings, but since you have no way to discern them, attr will always just yield the value of the first match.
You could also have iterated over your collection of elements, or accessed them by index siblings('input[type=hidden]:eq(1)') or siblings('input[type=hidden]').eq(1), for instance, but it is a poor design that will break your code if you add another hidden field for something else. You really should prefer to name your elements so that you can access them in a meaningful way and know your data. That way you'll be free to move around and modify your markup according to new requirements, without breaking your script.
By the way, I'm using .val() above, which is shorthand for .attr('value').
One option is to use special selectors, e.g. :first and :last:
var price = $(this).siblings("input[type='hidden']:first").attr("value");
var name = $(this).siblings("input[type='hidden']:last").attr("value");
However, you always can set a class name to the elements:
<input type="hidden" class="price" value="30">
<input type="hidden" class="name" value="The walking dead">
var price = $(this).siblings(".price").attr("value");
var name = $(this).siblings(".name").attr("value");
I would add an class name to your hidden inputs (price, name). This way the html source code is more readable and also the js code will be more readable.

Categories