Modify HtmlHelper value when using JS clone.node - javascript

I have a table where a user inputs info for the DB. The 2 fields here are
"type" and "name". Type must be either "L" or "S", but it is depicted to user as "Large" or "Small".
Say the user needs a new row for more data. I have 2 buttons, "Large" and "Small", that
fire the same JS function to create a new row. I want the value from the button to be
input into the new row in the type column. How would I accomplish this?
I'd like to do this using the clone.node function somehow, but the HtmlHelpers are throwing me off as well.
<tr id=rowToClone>
<td style="width:30px">
#Html.DropDownList("manifests.Type",
new SelectList(new List<Object>
{
new { value = "L", text = "Large"},
new { value = "S", text = "Small"},
},
"value",
"text"
)
)
</td>
<td>
#Html.TextBox("manifests.Name")
</td>
</tr>
<input type="button" value="Add Large" id="btAddL" style="display: none; float:left" onclick="addRow('L')"/>
<input type="button" value="Add Small" id="btAddS" style="display: none; float: left" onclick="addRow('S')"/>
<script>
function addRow(type) {
document.getElementById("btAddL").style.display = "none";
document.getElementById("btAddS").style.display = "none";
var row = document.getElementById("rowToClone");
var table = document.getElementById("tableToClone");
var clone = row.cloneNode(true);
clone.style.display = "table-row";
/*clone.node.td.type = type;*/
table.appendChild(clone);
}
</script>

This answer is purely from the perspective of the JS, I'm not sure about those ASP.NET helpers at the top...
I think that what you want within the clone you're trying to reach for the <td> cell? is that right?
I imagine this is what you are thinking because you've commented out clone.node.td
Probably you need to find the <td> element itself
var td_element = clone.querySelectorAll('td:first-child')
td_element.type = type; // this line makes no sense as <td> elements do not have types so maybe the line above should select for a kind of an HTML element that has a type
If the thing you want to clone is pretty small it could be easy (and a good exercise) just to create new elements instead of the cloning.
https://javascript.info/searching-elements-dom#querySelectorAll
https://www.w3schools.com/tags/att_type.asp

Related

How to add html with thymeleaf and bind object or its fields to that html on button click?

This is simple java class that has a list of strings for example:
public class Apple {
private List<String> listOfStrings;
// getters setters
}
html with thymeleaf:
<form method="post" th:object="${apple}"> // apple is set through Model.addAttribute(.., ..) in Controller
<input type="text" th:each="str, iter : *{listOfStrings} th:field="*{listOfStrings[__${iter.index}__]}">
<button id="add" type="button" onclick="add()>Add</button>
<button id="remove" type="button" onclick="remove()">Remove</button>
<input type="submit" value="Save">
<form>
javascript add and remove new input:
const newInputTextString="<input type=\"text\" th:field="?">" // th:field won't be evaluated/bound to apples listOfStrings index
function add() {
const xhttp = new XMLHttpRequest();
xhttp.onload = function() {
document.getElementById("add").insertAdjacentHTML("beforebegin", newInputTextString);
}
xhttp.open("GET", window.location.href);
xhttp.send();
}
function remove() {
// TODO
}
So far I am only adding html through javascript, but I can not include th: tags there since these tags wont be evaluated and bind to specific object or fields.
Expected functionality:
on click of remove button, last input of type text that is bound to specific index of listOfStrings is removed.
on click of add button, new input of type text is added and bound to next index of listOfStrings.
In other words I want to make user able to remove and add strings in listOfStrings of that apple object with html buttons.
Feel free to correct my English
th:field essentially just gives you an ID and a NAME attribute on an element. You cannot add a th:field through JS but you can emulate its behavior by manually adding the appropriate ID and NAME attributes. With that said you should make some slight changes in your HTML and JS code.
You should always have a record of the current total number of strings so you can adjust your string index. For that I've created a div wrapper around all string inputs so we can access it in JS. Afterwards we just read the number of elements inside of it and create a new index out of it.
You can look the implementation below:
function createInput(index) {
var input = document.createElement('input');
input.setAttribute('type', 'text');
input.setAttribute('id', `listOfStrings[${index}]`);
input.setAttribute('name', `listOfStrings[${index}]`);
input.setAttribute('placeholder', `listOfStrings[${index}]`);
input.setAttribute('data-index', index);
input.setAttribute('data-input', '');
return input;
}
function addString() {
var inputsWrapper = document.querySelector('[data-inputs]');
var inputs = inputsWrapper.querySelectorAll('[data-input]');
var newIndex = inputs.length;
var newInput = createInput(newIndex);
inputsWrapper.append(newInput);
}
<form method="post" th:object="${apple}">
<div style="display: flex; flex-direction: column; max-width: 300px" data-inputs>
<input
type="text"
placeholder="listOfStrings[0]"
th:placeholder="${'listOfStrings[__${iter.index}__]'}"
th:each="str, iter : *{listOfStrings}"
th:attr="data-index=${__${iter.index}__}"
th:field="*{listOfStrings[__${iter.index}__]}"
data-input />
</div>
<button id="add" type="button" onclick="addString()">Add</button>
<button id="remove" type="button" onclick="remove()">Remove</button>
<input type="submit" value="Save">
<form>

Adding custom attribute values of dynamically created dropdowns to another element

I have a bit of HTML here:
<tr taskId="(#=obj.task.id#)" assigId="(#=obj.assig.id#)" class="assigEditRow" >
<td><select name="resourceId" class="get-resources formElements"></select></td>
<td><span class="resources-units"></span></td>
<td><span class="resources-quantity"></span></td>
<td><input type="text" placeholder="Required Q"></td>
<td align="center"><span class="teamworkIcon delAssig" style="cursor: pointer">d</span></td>
</tr>
And a bit of JS here:
'use strict';
function addResourceFunction(){
let ResourcesJSON = (json) => {
let Resources = json;
console.log(Resources);
let contactsLength = json.length;
let arrayCounter = -1;
let resID;
let resName;
let resUnit;
let resQuantity;
let Option = $('<option />');
let assignedID = $('tr.assigEditRow:last').attr("assigId");
while(arrayCounter <= contactsLength) {
arrayCounter++;
resID = Resources[arrayCounter].ID;
resName = Resources[arrayCounter].name;
resUnit = Resources[arrayCounter].unit;
resQuantity = Resources[arrayCounter].quantity;
$('.assigEditRow').last().find('select').append($('<option>', {
value: resName.toString(),
text: resName.toString(),
resourceID: resID.toString(),
resourceUnit: resUnit.toString(),
resourceQuantity: resQuantity.toString()
}));
}
}
$.getJSON("MY JSON URL IS HERE", function(json) {
ResourcesJSON(json);
});
};
So what's actually going on here: I get my data from the URL (JSON array), trigger the addResourceFunction() on click to create a new table row and to add a new select with options passed from the array. As you see from my HTML markup, the select input is placed in td.get-resources, and all that works good. I get my date set, I populate the select field and all works good. I can add as many rows/select dropdowns as I want.
Also, every option has a few custom attributes (you can see it in my JS code above), and I want to add the values of those attributes to the second and third column of the row (in HTML those are span.resources-units and span.resources-quantity). The thing is, I have no clue how to make it work 1:1, meaning that one select dropdown "alters" only units and quantity of its own row. Below is the code for that:
let idCounter = 1;
$(document).on('change', '.get-resources', function() {
$('.assigEditRow').last().find('.resources-units').attr('id', 'units-' + idCounter);
$('.assigEditRow').last().find('.resources-quantity').attr('id', 'quantity-' + idCounter);
this.resourceUn = $( ".get-resources option:selected" ).attr( "resourceUnit" );
this.resourceQuant = $( ".get-resources option:selected" ).attr( "resourceQuantity" );
$('#units-' + idCounter).append(this.resourceUn);
$('#quantity-' + idCounter).append(this.resourceQuant);
idCounter++;
});
What happens is that if I add one select input, and change options, the thing works. When I add another one and change its options, it gets attributes of the first one. Adding more - same thing. Whatever I change, it takes the attribute value of the first item added.
Try getting the id from the element instead of from the variable, since you always update the element with the id of the counter, instead of the element with the id of the row that was clicked.
Hmm, what does the counter do exactly? The more I look at it, the less I understand. What I do know is that you're not selecting the correct elements by using the idCounter to reference the correct row.
You want to do something like
$(document).on('change', '.get-resources', function() {
//var row = this;
this.find(/* Some path to the second column */).att(/* some att to change */);
this.find(/* Some path to the third column */).att(/* some att to change */);
});
where you always use the row as the root again, instead of finding a certain id, so you only update that row.
Native:
<table>
<tr>
<td>
<select>
<option data-text="resName1" data-resourceID="resID1" data-resourceUnit="resUnit1" data-resourceQuantity="resQuantity1">1</option>
<option data-text="resName2" data-resourceID="resID2" data-resourceUnit="resUnit2" data-resourceQuantity="resQuantity2">2</option>
<option data-text="resName3" data-resourceID="resID3" data-resourceUnit="resUnit3" data-resourceQuantity="resQuantity3">3</option>
</select>
</td>
<td>
<div class="column2"></div>
</td>
<td>
<div class="column3"></div>
</td>
</tr>
</table>
<script>
document.addEventListener('change', function ( event ) {
var select = event.target,
option = select.options[select.selectedIndex],
values = {
'text' : option.getAttribute('data-text'),
'resourceID' : option.getAttribute('data-resourceID'),
'resourceUnit' : option.getAttribute('data-resourceUnit'),
'resourceQuantity' : option.getAttribute('data-resourceQuantity')
},
row = select.parentNode.parentNode,/* depending on how deep the select is nested into the tr element */
column2 = row.querySelector('.column2'),
column3 = row.querySelector('.column3');
column2.textContent = 'some string with the values you want';
column3.textContent = 'some string with the other values you want';
});
</script>
Basically you start with the select that was changed, from there you get the option node that was clicked. Then you get the attributes you need from that option. Then you go up a few nodes to the row parent and find the two columns inside that row. Then you can set the content of these two columns.

editing dynamically generated table

I have a dynamically generated tables the foot of the table contain some text fields when click on save i want to add the value of text fields to the body of that table .
here is the table
<table border="1" class="entity_table">
<tfoot>
<tr>
<td>
<div class="pane1"></div>
<div class="pane2">
<input type="text" id="name"><br>
<select id="data">
<option value="1">int</option>
<option value="2">tinyint</option>
</select>
<br><span id="save">save</span>
</div>
</td>
</tr>
</tfoot>
<tbody class="table-body" id='myid'></tbody>
</table>
i did this but this is id specific ..i want to update that specific table on which it is clicked and edited .
var myName = document.getElementById("name");
var data = document.getElementById("data");
var Mtable = document.getElementById("myid");
var rowCount = Mtable.rows.length;
var mrow = Mtable.insertRow(rowCount);
var mcell = mrow.insertCell(0);
mcell.innerHTML = myName.value;
var mcell1 = mrow.insertCell(1);
mcell1.innerHTML = size.value;
i want to update each dynamically generated table with values that is entered in its table's foot section
You can use below jQuery :
$(function(){
$('#save').click(function(){
$(this).closest('table').find('tbody').append('<tr><td>'+$('#name').val()+' and '+$('#data').val()+'</td></tr>');
});
});
Demo
EDIT - to eliminate input and select box id dependency use below code :
$(function(){
$('#save').click(function(){
var name = $(this).closest('tr').find('input[type=text]').val();
var data = $(this).closest('tr').find('select').val();
$(this).closest('table').find('tbody').append('<tr><td>'+name+' and '+data+'</td></tr>');
});
});
Demo
So if I understood this right, you dont want to use element's ID to select it.
You have some else options if you dont want to work with elements IDs:
1) You can add them some data- attribute, for example: data-id. And based on this you select your element like this:
myElement.querySelector("[data-id='X']") where myElement is some parent element of your tables and X is their ID which you generated before (lets say it will start from 0 and will increment with every next table).
2) If possible, work with objects. When you create your tables, you either create them with raw text with defining html elements or you create new elements with calling createElement("table") on document keyword. If second option is your option, you can save this elements to some array (myTables in this case) and then approach this elements in a standard way - lets say:
myTables[0].getElementsByTagName("input")
Hope it helps your issue. Hope I understood issue you were asking about.

Obtain value from <span> attributes inside <td>

My vb.net program is generating a checkboxlist with several checkboxes. While building the checkboxes, I'm also setting a couple of Attributes. When the user clicks the box, I'm trying to access the values of the attributes in javascript.
The vb.net looks like this:
L = New ListItem
thisROLEn = "A"
L.Value = dr("apr_key")
L.Text = Trim(dr("apr_name"))
L.Attributes("Role" & thisROLEn) = Trim(dr("ROLE_DESC"))
L.Attributes("Title") = AppToolTip
AppList.Items.Add(L)
Here's what a TR section of the generated html looks like:
<tr>
<td>
<span RoleA="User" RoleB="Admin" Title="Approve access">
<input id="MainContent_AppList_0" type="checkbox" name="ctl00$MainContent$AppList$0" value="7" />
<label for="MainContent_AppList_0">Finance</label>
</span>
</td>
I can get the value of the checkbox just fine. My question is how do I access the value of "RoleA" from javascript?
Basic idea based off your comment.
var checkbox= document.getElementById("MainContent_AppList_0");
var span = checkbox.parentNode;
var isChecked = checkbox.checked;
var roleA = span.RoleA;
You can get the attribute value by getAttribute
document.getElementById('MainContent_AppList_0').parentNode.getAttribute("roleA")
As you mentioned you are able to access check box, based on that I have created a sample pseudo code here:
var a=document.getElementsById('ctl00$MainContent$AppList$0');
var b = a.parentNode;
var roleA = b.getAttribute('RoleA');
Hope it helps.

javascript id change onclick function

So I got a 3 x 3 table wich looks like this:
<table align=left width="896px" class="tableCategories">
<tr class="trCategories">
<td class="tdCategories"><input type="button" id="P11" onclick="clickE()" value="E" /></td>
<td class="tdCategories"><input type="button" id="P12" onclick="clickS()" value="S" /></td>
<td class="tdCategories"><input type="button" id="P13" onclick="clickB()" value="B" /></td>
....
I just want that if the user clicks on one of the nine boxes all of the boxes changes their background images and the box which was clicked changes itself to a back button which references to the first 9 images. Like a submenu getting opened if one of the boxes being clicked.
So I tried it in my way but it does not work very well. If I click on one of the boxes it triggers both actions which is connected via id. So my thought was to change the id to, but then I thought maybe there is a smarter way to do that. So I wrote my problem here :D
Edit:
The javascript part is looking like this for every of the functions:
function clickE()
{
document.getElementById("P11").value = "Zurück";
document.getElementById("P11").onclick = clickBack;
document.getElementById("P12").value = "Restaurant";
document.getElementById("P12").onclick =clickCategory("restaurant");
document.getElementById("P13").value = "Imbiss";
document.getElementById("P13").onclick =clickCategory("imbiss");
document.getElementById("P21").value = "Bäckerei";
document.getElementById("P21").onclick =clickCategory("baeckerei");
document.getElementById("P22").value = "Fast Food";
document.getElementById("P22").onclick =clickCategory("fast_food");
document.getElementById("P23").value = "Süßes";
document.getElementById("P23").onclick =clickCategory("suesses");
document.getElementById("P31").value = "Cafe";
document.getElementById("P31").onclick =clickCategory("cafe");
document.getElementById("P32").value = "Bar";
document.getElementById("P32").onclick =clickCategory("bar");
document.getElementById("P33").value = "Kneipe";
document.getElementById("P33").onclick =clickCategory("kneipe");
}
I try it first with the labels because I think it will work with images the same way.
Being unsure of the long term application, I may have a starting point. The initial script provided could get a bit unwieldy if you ever want to extend, so there is now a "defineElements" method that could be used to configure your elements. Please note that argument paramaters have been added to the onclick event in the html as well.
The "defineElements" function returns an associative array object (eg key/value pairs), with each key being named after the html element id. The value of each key is also an associative array that contains the element id (eid), label (lbl) and event (evnt).
Short story long... when you click a button, the label for each button is changed and the appropriate click handler is assigned. If you click the button labeled "Back", the default click handler is reassigned to all.
This would also be an excellent candidate for jQuery if that is available to you.
Hopefully this will get you moving in the right direction:
HTML
<table>
<tr class="trCategories">
<tr class="trCategories">
<td class="tdCategories"><input type="button" id="P11" onclick="clickE(this.id)" value="E" /></td>
<td class="tdCategories"><input type="button" id="P12" onclick="clickS(this.id)" value="S" /></td>
<td class="tdCategories"><input type="button" id="P13" onclick="clickB(this.id)" value="B" /></td>
</tr>
</table>
== And the Javascript
function clickE(id){
var elementDef = defineElements(id);
for(var key in elementDef){
var propObj = elementDef[key];
//console.log(propObj);
document.getElementById(propObj.eid).value = propObj.lbl;
if(id == undefined)
document.getElementById(propObj.eid).onclick = function(){ clickE(this.id);} //--reassign default event
else
document.getElementById(propObj.eid).onclick = propObj.evnt;
}
}
function defineElements(id){
var elementArr = ['P11','P12','P13']; //--add your element id's to this array to extend, and then add a case for each within switch below
var definitionObj = {};
for(var i = 0; i < elementArr.length; i++){
switch(elementArr[i].toUpperCase()){
case 'P11':
definitionObj[elementArr[i].toUpperCase()] = { eid:elementArr[i].toUpperCase(), lbl:'Zuruck', evnt: function(){ clickCategory.call(this, "whatever"); } };
break;
case 'P12':
definitionObj[elementArr[i].toUpperCase()] = { eid:elementArr[i].toUpperCase(), lbl:'Restaurant', evnt: function(){ clickCategory.call(this,"restaurant"); } };
break;
case 'P13':
definitionObj[elementArr[i].toUpperCase()] = { eid:elementArr[i].toUpperCase(), lbl:'Imbiss', evnt: function(){ clickCategory.call(this,"imbiss"); } };
break;
}
}
if(id != undefined){
definitionObj[id]['evnt'] = function(){ clickBack.call(this); } //--assign the clickback function to the selected element based on id paramater
definitionObj[id]['lbl'] = 'Back';
}
return definitionObj;
}
function clickCategory(cat){
alert(cat);
}
function clickBack(){
clickE();
}

Categories