I'm using Jquery $.post to get string array from SQL based on selection from a drop-down that I'm then reading into an HTML table. Each time they change selection on drop down, it clears all but the header row from the table and reloads new values. That is working fine.
It is just a basic 3 row table; a unique identifier, a value and a count shown as string. Every record has all 3, so I'm just using for loop with counters to control start/end of rows. In my form it's header is defined as such:
<div class="col-md-10">
<table id="attEditTable" style="width:100%" cellpadding="0" cellspacing="0" border="1" class="row">
<tbody>
<tr style="background-color: #F0F8FF;">
<th></th>
<th>Attribute Value</th>
<th>Item Count</th>
</tr>
</tbody>
</table>
</div>
I'm now trying to change the 1st cell of each row to a radio button with the value set to the value I was displaying in that cell.
Currently when the view displayed it is showing [object Object] in the first cell of every row instead of a radio button.
Sorry I am a newb at this with no training on Java or MVC - so hoping just a simple syntax issue...
Trying this basic one returned syntax error on input:
<input type="radio" name="SelERow" value='+editValues[i]+' />
I've also tried (both had same result of [object Object]):
$("input:radio[name=\"SelERow\"][value="+editValues[i]+"]")
$('<input type="radio" id="ERow" name="SelERow" value='+editValues[i]+' />')
Per David's suggestions I've now also tried (both resulted in no data and no error):
'<input type="radio" name="SelERow" value='+editValues[i]+' />'
var tblCell = $('<td />'); // create an empty <td> node
if (x == 1 || (x - 1) % 3 == 0) {
var input = $('<input />', { type: 'radio', id: 'ERow', name: 'SelERow', value: editValues[i] });
tblCell.append(input); // append an <input> node to the <td> node
} else {
tblCell.text(editValues[i]); // or just set the text of the <td> node
}
With the 2nd one I also changed the line: tblRow = tblRow + ""; to instead be tblRow = tblRow + tblCell + "";
Current Script
<script>
$(function () {
$("#EditAttributeName").change(function () {
var selectedName = $(this).val();
// Delete all but first row of table
$("#attEditTable").find($("tr")).slice(1).remove();
var url2 = "EditDD2changed?selectedName=" + selectedName;
$.post(url2, function (editValues) {
var x = 0;
var tblRow = "<tr>";
for (var i=0; i < editValues.length; i++)
{
x++;
if (x == 1 || (x - 1) % 3 == 0) {
tblRow = tblRow + "<td>" + $('input:radio[name="SelERow"][value="' + editValues[i] + '"]');
}
else {
tblRow = tblRow + "<td>" + editValues[i] + "</td>";
}
if (x % 3 == 0)
{
tblRow = tblRow + "</tr>";
$("#attEditTable").append(tblRow);
tblRow = "<tr>";
}
}
})
});
});
</script>
Console is showing no error messages.
Looks like it's close. To start, there's an important difference in the two attempts. This is a jQuery selector syntax:
$('input:radio[name="SelERow"][value="' + editValues[i] + '"]')
So it's not creating an <input/>, but looking for an <input/>. Which isn't what you want in this case. Your other attempt uses the syntax for creating an element:
$('<input type="radio" id="ERow" name="SelERow" value=editValues[i] />')
Though an issue here (which may have just been a copy/paste error in posting the question? but for completeness and for future readers it may as well be addressed...) appears to be that the editValues[i] is just part of the string. You want to concatenate it into the string. There are a couple ways to do that. Either direct concatenation:
$('<input type="radio" id="ERow" name="SelERow" value="' + editValues[i] + '" />')
Or string interpolation (take note of the different overall quotes, using back-ticks this time):
$(`<input type="radio" id="ERow" name="SelERow" value="${editValues[i]}" />`)
The latter is newer syntax but should be widely enough supported by now. (Though in any given business environment who knows what ancient browsers one may need to support.) Could just be personal preference between the two.
it is showing [object Object]
The main issue producing the result you've observing is that you're concatenating the result of that jQuery operation directly as a string:
tblRow + "<td>" + $('<input type="radio" id="ERow" name="SelERow" value="' + editValues[i] + '" />')
(Coincidentally, whether you're creating an element or looking for an element, this observed output would be the same because both operations return an object.)
The result of an $() operation is not itself a string, but a more complex object. When concatenated with a string it has to be interpreted as a string, and unless the object has a meaningful .toString() implementation (this one doesn't appear to) then the default string representation of a complex object is exactly that: "[object Object]"
There are a couple approaches you can take here. One would be to just use strings entirely, you don't necessarily need a jQuery object here:
tblRow + '<td><input type="radio" id="ERow" name="SelERow" value="' + editValues[i] + '" /></td>'
Since you're using jQuery later to append the result to the HTML, you can just build up all the HTML you like as plain strings and let jQuery handle turning them into DOM objects when you send them to .append().
The other option, if you definitely want to "use jQuery" in this situation or are otherwise being instructed to, would be to build the hierarchy of HTML elements as jQuery objects and then pass that hierarchy to .append(). Constructing such a hierarchy can look something like this:
var tblCell = $('<td />'); // create an empty <td> node
if (x == 1 || (x - 1) % 3 == 0) {
var input = $('<input />', { type: 'radio', id: 'ERow', name: 'SelERow', value: editValues[i] });
tblCell.append(input); // append an <input> node to the <td> node
} else {
tblCell.text(editValues[i]); // or just set the text of the <td> node
}
Note that each $() operation creates an HTML element node, and you can supply attributes for it as a second argument to the $() function. Then those nodes can be .append()-ed to each other just like you .append() the HTML string to $("#attEditTable").
In your particular case this may get a little more cumbersome because your loop isn't just looping through cells or just through rows, but through both and using a hard-coded count to determine whether it's reached the end of a row or not. So, as part of learning/practicing jQuery, it may be worth the effort to try this approach. But I suspect the shortest path to getting your code working with minimal changes would be the string concatenation approach above.
Side note: This code is using the same id value for the radio buttons created within this loop. The result is that there is expected to be multiple elements on the page with the same id. This is technically invalid HTML. If you ever try to use that id to reference an element, the resulting behavior will be undefined. (It might work in some cases, might not in others, purely coincidentally.) Though if you don't need to use that id to reference the elements, you may as well remove it entirely.
Related
My application successfully creates elements and assigns them different (increasing) IDs.
Now my issue relies when the user deletes these elements (because they have the option to delete as well as create), the consistency of these IDs get broken therefore my application doesn't run well.
This Fiddle represents what I have so far. Just a textbox that appends its value and a few other elements inside a collapsible as many times as the user wants (For some reason my fiddle doesn't increment the alert value, but it works fine on my platform).
SCRIPT (Sorry the txt variable is too long)
$('#Add').click(function () {
if ($("#MedNameStren").val() != "") {
var value = $("#MedNameStren").val();
var noOfMeds = $('#NoOfMedicines').val();
//to check current value
alert(noOfMeds);
var text = '<div data-role="collapsible" data-collapsed="true" data-iconpos="left" data-content-theme="e">' + '<h2>' + desc + '</h2>' + '<div class="ui-grid-a">' + '<div class="ui-block-a" style="width:25%; margin-right:3%;">' + '<input id="quantity' + noOfMeds + '" class="quantity" type="text" placeholder="Quantity" />' + '</div>' + '<div class="ui-block-b" style="width:70%; margin-right:2%;"">' + '<textarea id="directions' + noOfMeds + '" class="directions" cols="40" rows="4" placeholder="Directions given by your GP." ></textarea>' + '</div>' + '</div>' + '<button key="' + vpid + '">Remove</button>' + '</div>';
$("#medListLi").append(text);
$('button').button();
$('#medListLi').find('div[data-role=collapsible]').collapsible();
$('#medListLi li').listview("refresh");
$('#medListLi').trigger("create");
document.getElementById("manuallyName").value = "";
noOfMeds++
$("#NoOfMedicines").val(noOfMeds);
}
else {
alert('Please Provide Medicine Name')
}
});
I am using a counter that neatly increments the ids of quantity and description like:
quantity0
quantity1
quantity2
..and so on, but once the following script is called...
//Deletes colapsible sets (Medicines) from the selected List
$('#medListLi').on('click', 'button', function (el) {
$(this).closest('div[data-role=collapsible]').remove();
var key = $(this).attr('key');
localStorage.removeItem(key);
var noOfMeds = $('#NoOfMedicines').val();
noOfMeds--
$("#NoOfMedicines").val(noOfMeds);
//location.reload();
});
depending on which element (collapsible) is deleted, the IDs stop being consistent. For example if the collapsible with id="quantity1" is deleted then the counter will go back to 1 (currently 2) and on the next addition the respective collapsible will get an id that's already taken, and unfortunately I don't need this to happen.
Maybe I'm making this sound more complicated that it is, but will appreciate any suggestions or ideas to solve this issue (if possible).
If more information is needed, please let me know.
Was brought to my attention that creating and deleting dynamic IDs can be done but keeping up with consistency of these IDs can be very tricky to work around it.
I've solved my own problem by simply creating a function that would keep count of the IDs from the amount of collapsibles inside my list and "renewing" the ID numbers on each Add and Delete.
I created a div and a button. when the button clicked, there will be a group of element(included 1 select box and 2 text inputs) inserted into the div. User can add as many group as they can, when they finished type in data of all the group they added, he can hit save button, which will take the value from each group one by one into the JSON object array. But I am stuck in the part how to get the value from each group, so please help, thank you.
The code for the div and the add group button function -- AddExtra() are listed below:
<div id="roomextra">
</div>
function AddExtra() {
$('#roomextra').append('<div class=extra>' +
'<select id="isInset">' +
'<option value="Inset">Inset</option>' +
'<option value="Offset">OffSet</option>' +
'</select>' +
'Length(m): <input type="text" id="insetLength">' +
'Width(m): <input type="text" id="insetWidth">' +
'Height(m): <input type="text" id="insetHeight">' +
'</div>');
}
function GetInsetOffSetArray (callBack) {
var roomIFSDetail = [{
"IsInset": '' ,
"Length": '' ,
"Width": '' ,
"Height": ''
}];
//should get all the value from each group element and write into the array.
callBack(roomIFSDetail);
}
This should just about do it. However, if you're dynamically creating these groups, you'll need to use something other than id. You may want to add a class to them or a data-* attribute. I used a class, in this case. Add those classes to your controls so we know which is which.
var roomIFSDetail = [];
var obj;
// grab all of the divs (groups) and look for my controls in them
$(.extra).each(function(){
// create object out of select and inputs values
// the 'this' in the selector is the context. It basically says to use the object
// from the .each loop to search in.
obj = {
IsInset: $('.isInset', this).find(':selected').val() ,
Length: $('.insetLength', this).val() ,
Width: $('.insetWidth', this).val() ,
Height: $('.insetHeight', this).val()
};
// add object to array of objects
roomIFSDetail.push(obj);
});
you'd better not to use id attribute to identity the select and input, name attribute instead. for example
$('#roomextra').append('<div class=extra>' +
'<select name="isInset">' +
'<option value="Inset">Inset</option>' +
'<option value="Offset">OffSet</option>' +
'</select>' +
'Length(m): <input type="text" name="insetLength">' +
'Width(m): <input type="text" name="insetWidth">' +
'Height(m): <input type="text" name="insetHeight">' +
'</div>');
}
and then, usr foreach to iterate
$(".extra").each(function() {
var $this = $(this);
var isInset = $this.find("select[name='isInset']").val();
var insetLength = $this.find("input[name='insetLength']").val();
// ... and go on
});
A common problem. A couple things:
You can't use IDs in the section you're going to be repeating, because IDs in the DOM are supposed to be unique.
I prefer to use markup where I'm writing a lot of it, and modify it in code rather than generate it there.
http://jsfiddle.net/b9chris/PZ8sf/
HTML:
<div id=form>
... non-repeating elements go here...
<div id=roomextra>
<div class=extra>
<select name=isInset>
<option>Inset</option>
<option>OffSet</option>
</select>
Length(m): <input id=insetLength>
Width(m): <input id=insetWidth>
Height(m): <input id=insetHeight>
</div>
</div>
</div>
JS:
(function() {
// Get the template
var container = $('#roomextra');
var T = $('div.extra', container);
$('#addGroup').click(function() {
container.append(T.clone());
});
$('#submit').click(function() {
var d = {};
// Fill d with data from the rest of the form
d.groups = $.map($('div.extra', container), function(tag) {
var g = {};
$.each(['isInset', 'insetLength', 'insetWidth', 'insetHeight'], function(i, name) {
g[name] = $('[name=' + name + ']', tag).val();
});
return g;
});
// Inspect the data to ensure it's what you wanted
debugger;
});
})();
So the template that keeps repeating is written in plain old HTML rather than a bunch of JS strings appended to each other. Using name attributes instead of ids keeps with the way these elements typically work without violating any DOM constraints.
You might notice I didn't quote my attributes, took the value attributes out of the options, and took the type attributes out of the inputs, to keep the code a bit DRYer. HTML5 specs don't require quoting your attributes, the option tag's value is whatever the text is if you don't specify a value attribute explicitly, and input tags default to type=text if none is specified, all of which adds up to a quicker read and slimmer HTML.
Use $(".extra").each(function() {
//Pull info out of ctrls here
});
That will iterate through all of your extra divs and allow you to add all values to an array.
I am using nested for loops to generate multiple instances of a table with details of projects; under which I wish to have a show/hide button that will give a short description of each project at a high level.
I am tring to manipulate code I found here: https://forums.digitalpoint.com/threads/javascript-to-show-hide-tables.1009918/
The following code produces a "Show/Hide" link that does not work on my page (see screenshot). Am I missing something?
FYI - "Separate" in the code below is an array containing unique project references to facilitate the separation of the tables per project. So where Separate contains 4 elements, there should be 4 projects, 4 tables, and so on.
Many Thanks,
Karl
function showhide(id){
if (document.getElementById){
obj = document.getElementById(id);
if (obj.style.display == "none"){
obj.style.display = "";
} else {
obj.style.display = "none";
}
}
}
for(i in Separate){
DescID[i] = "DescID"+i;}
var Table = "";
for(i in Separate){
Table += "<table id='dashboard' summary='Project Dashboard'>";
Table += "<THEAD>";
Table += "<TR><TH scope='col' colspan=4><B>"+ Separate[i] +"</B></TH></TR>";
Table += "<TR><TH scope='col'>Task Names</TH><TH scope='col'>Task Summary</TH><TH scope='col'>RAG</TH><TH scope='col'>Timeline</TH></TR></THEAD>";
Table += "</THEAD>";
Table += "<TBODY>";
for(j in Project){
if(Project[j] == Separate[i]){
Table += "<TR><TD title='" + Comments[j] + "'>"+ Task[j] +"</TD><TD>"+ Summary[j] +"</TD><TD><img src='/images/RAG/" + RAG[j] + "'></TD><TD>"+ DateType[j] +" "+ Status[j].substring(0,10) +"</TD></TR>";
}
}
Table += "</TBODY>";
Table += "</table>";
Table += "<a onclick ='javascript:ShowHide('" + DescID[i] + "')' href='javascript:;' >Show/Hide Project Description</a>";
Table += "<div class='mid' id='" + DescID[i] + "' style='DISPLAY: none' >Placeholder for Project Description</div>";
Table += "<BR>";
}
You're missing the fact that HTML tag "id"s (e.g., for your Projects' tables) should be unique, otherwise "getElementById" won't work. Now they're all set to "dashboard". At the very least you could add the index, and make them "dashboard1", "dashboard2", etc...
Also, IIRC, a better opposite for 'display = "none";' is 'display = "inline";', instead of 'display = "";'. Although this needs more testing on different browsers to select the best option.
Thirdly, your JavaScript call within the onclick events use single quotes BOTH for the attribute value definition (surrounding the JS call) and for the string parameter (the id to show/hide)... That's not valid syntax, and you need one of these two use-cases to be double-quotes.
And the other main problem your code has is as mikez302 already spotted: "Javascript function names are case-sensitive. Your function is called showhide but you are trying to call ShowHide." Correcting these two issues (the quotes and the function name) will allow the code to work, i've just tested it. :)
I have seen a similar question, HERE and have tried that, but I can't seem to get it working.
Here is my code for dynamically generating table rows.
for (var contribution = 0; contribution < candidate.contributions.length - 1; contribution++) {
var id = candidate.contributions[contribution].donor_id;
var uid = candidate.contributions[contribution].user_id;
$("#history-table").append(
"<tr onclick='" + parent.viewEngine.pageChange('public-profile', 1, id, uid) + ";>" +
"<td class='img-cell'>" +
"<img class='profile-avatar-small' src='/uploads/profile-pictures/" +
candidate.contributions[contribution].image + "' alt='' /></td><td class=''>" +
"<h2>" + candidate.contributions[contribution].firstname +
" " + candidate.contributions[contribution].lastname + "</h2></a><br/><br/>" +
"<span class='contribution-description'>" + candidate.contributions[contribution].contribution_description + "</span></td>" +
"<td><h3>$" + formatCurrency(candidate.contributions[contribution].contribution_amount) + "</h3></td></tr>");
}
This still executes the click event as soon as the page loads, which is not the desired behavior. I need to be able to click the tr to execute the click event.
Pass the whole thing as a string:
"<tr onclick='parent.viewEngine.pageChange(\'public-profile\', 1, " + id + ", " + uid + ");>" // + (...)
But, as you are using jQuery, you should be attaching the click handler with .on().
(I really don't recommend using inline event handlers like that, especially when you're already using jQuery, but anyway...)
The problem is that you need the name of the function to end up in the string that you are passing to .append(), but you are simply calling the function and appending the result. Try this:
...
"<tr onclick='parent.viewEngine.pageChange(\"public-profile\", 1, " + id + "," + uid + ");'>" +
...
This creates a string that includes the name of the function and the first couple of parameters, but then adds the values of the id and uid variables from the current loop iteration such that the full string includes the appropriately formatted function name and parameters.
Note that the quotation marks around "public-profile" were single quotes but that wouldn't work because you've also used single quotes for your onclick='...', so you should use double-quotes but they need to be escaped because the entire string is in double-quotes.
I'm wondering if you might be better simplifying things a bit.
If your rows are being dynamically added, then try putting some kind of meta-data in the <tr> tag, e.g. something like this:
<tr id="id" name="uid">
Then try the following with your jQuery (v.1.7 required):
$('#history-table tr').on('click', function(){
parent.viewEngine.pageChange('public-profile', 1, this.id, this.name);
});
This will likely require modification depending on how your page rendering works but it's a lot cleaner and easier to read having been removed from your main table markup.
Well that's because you're executing the function, not concatenating it. Try:
onclick='parent.viewEngine.pageChange("public-profile", 1, id, uid);'
Take this ->
$("#contribution-" + uid).click(function(){
parent.viewEngine.pageChange('public-profile',1, id, uid);
});
And do two things:
1) Move it outside of the 'for' statement
As soon as the for statement is executed, the click function will be executed as well. The click function is not being supplied as a callback function in this for statement.
2) Change it to ->
$("tr[id^='contribution-'").on('click', function(){
var idString = $(this).attr("id").split("-"); //split the ID string on every hyphen
var uid = idString[1]; //our UID sits on the otherside of the hyphen, so we use [1] to selec it
//our UID will now be what we need. we also apply our click function to every anchor element that has an id beginning with 'contribution-'. should do the trick.
parent.viewEngine.pageChange('public-profile',1, id, uid);
});
This is my solution.
I have a html table that I reorder based on a CSV list of custom attribute values that I have for each table row. I am using the following function to do it:
for (var i = 0; i < arrCSV.length; i++)
{
$('#' + tableId)
.find('[fname = ' + arrCSV[i] + ']')
.eq(0)
.parents('tr')
.eq(0)
.appendTo('#' + tableId);
}
The table structure is:
<table>
<tr>
<td fname='f1'>something here</td>
</tr>
<tr>
<td fname='f2'>something here</td>
</tr>
</table>
The CSV could be something like this "f2, f1"
I find this is very very slow performing function. Any help in optimizing it is really appreciated.
EDIT:
Based on the article at http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly, one can achieve the greatest boost in performance by calling append only once with the html concatenated string. Can someone help in using this technique to my problem? I am not sure how to go about getting the s HTML in the for loop and appending it once.
I would suggest finding the elements as few times as possible. Store all the matching rows into a "hash" using the attribute value of interest as the key. Go through your CSV, pick the corresponding row out of the hash, push it into an array, then finally append the elements of the array to the table using the jQuery object previously found.
var table = $('#' + tableId);
var rowHash = {};
table.find('[fname]').each( function() {
rowHash[$(this).attr('fname')] = $(this).closest('tr');
});
var rows = [];
for (var i = 0; i < arrCSV.length; ++i)
{
var row = rowHash[arrCSV[i]];
if (row) {
rows.push(row);
}
}
$(rows).appendTo(table);
EDIT: This seems like a slight improvement to my previous code where I was appending each row to the table as it was found. I tested on a table with 1000 rows and it seems to take about 1sec to sort a table that needs to be completely inverted.
If you want to append html only once (like that learningjquery.com article), try following:
$(document).ready(
function()
{
var arrCSV = ['f2', 'f1'];
var tableId = 'mainTable';
var newTable = [];
for (var i = 0; i < arrCSV.length; i++)
{
var row = $('#' + tableId)
.find('[fname = ' + arrCSV[i] + ']')
.eq(0)
.parents('tr')
.eq(0);
newTable.push(row.html());
}
$('#' + tableId).html(newTable.join(''));
};
});
Live version: http://jsbin.com/uwipo
Code: http://jsbin.com/uwipo/edit
Though I personally feel that you should profile your code first and see if it's append which is slow OR that 'find' method call. I am thinking that for a huge table, using 'find method' to find a custom attribute could be slow. But again, there is no point in any guesswork, profile the code and find it out.
If the 'find' method is slow, will it be possible to use id attribute on td instead of giving custom attribute.
e.g.
<table>
<tr>
<td id='f1'>something here</td>
</tr>
<tr>
<td id='f2'>something here</td>
</tr>
</table>
Then your code to find the parent row could be as simple as:
('#' + arrCsv[i]).parent('tr')
EDIT: As pointed out by tvanfosson, this code assumes that arrCSV contains attribute for all the rows. The final table will only contain those rows which are present in arrCSV. Also, this code does not copy 'thead', 'tfoot' section from the original table, though it should be easy to write code which does.
You may have to rethink your algorithm.
Without changing the algorithm, a slight optimization would be:
var $table = $("#" + tableId);
for (var i = 0; i < arrCSV.length; i++)
{
$('[fname = ' + arrCSV[i] + ']:first',$table).closest('tr').appendTo($table);
}
Wait a second...
$('#' + tableId)
Get #myTable
.find('[fname = ' + arrCSV[i] + ']')
Find anything with an attribute of fname equal to i
.eq(0)
Give me the first item of the previous expression
.parents('tr')
Find the parents of type TR
.eq(0)
Give me the first in the previous expression
.appendTo('#' + tableId);
Add that TR to #myTable
Okay. Now that I've broken it down - are you duplicating a Table Row only? If so, the .append() isn't your problem, your choices of selectors is. To further compound my confusion here, the only tags with an attribute of fname are your TR's, so why are you going to their parents() and seeking out the first TR? You're basically asking for TR tags to be placed within TR tags - but that isn't what your Markup example shows.