I have an invoice and I want to add many items. I have a row with a select and two input fields. Select takes data from DB(it takes all items that are on the database) and input fields are for price and quantity.
I have a button Add Item that I want to add another row with specific inputs. My problem is with select because I do not know how to get data from the database to appear in options.
I am displaying the first line with Laravel while at the click of a button I am trying to implement it with JS. I also want to show the price of the selected item depending on the quantity.
<table id="items_table">...</table>
<button id="add_item">Add Item</button>
<script>
let counter = 1;
const items = {!! json_encode($items->toArray()) !!}
$('#add_item').click(function(event){
event.preventDefault();
counter++;
const select = document.createElement('select')
for(let i = 0; i< items.length; i++){
const opt = items[i]
const el = document.createElement('option');
el.textContent = opt.name;
el.value = opt.id;
select.appendChild(el);
}
const newRow = jQuery(
select +
counter + '"/></td><td><input type="text" name="last_name' +
counter + '"/></td></tr>');
$('#items_table').append(newRow);
});
</script>
It looks like you are getting your data types mixed up. You are creating the select element with document.createElement('select') - that returns a HTMLSelectElement and then you are trying to use that as if it was a String. Here is what your issue really is:
const select = document.createElement('select')
console.log(select + "<br>");
// This prints out "[object HTMLSelectElement]<br>"
To solve the issue, you must convert the select to a string of HTML, try something like this
const select = document.createElement('select')
select.innerText = "Test";
const selectAsHTML = select.outerHTML;
console.log(selectAsHTML + "<br>");
Related
I have a simple method for adding input boxes after a button is clicked. The goal of this method is to generate a set of input boxes with a newline inserted after each div.
In the screenshot above you can see that the divs are spaced properly. However, when the add_more button is clicked the generated inputs do not come out properly.
Expected:
The code should generate new input boxes like so:
<div>
Key Term 2: <input id="el2" type="text" value=""> <br>
</div>
<br>
Actual:
function add_more() {
// we've added more inputs.
addMore = true;
// set html generated to false, because new inputs have been added.
htmlGenerated = false;
// increment the number of inputs.
numberOfInputs++;
//fetch the input boxes.
inputs = document.getElementById("inputBoxes");
// create newline
br_key = document.createElement("br");
// create newline
br_description = document.createElement("br");
//create a new row for a key term.
row = document.createElement("div");
// set the key term text.
row.innerHTML = "Key Term ";
row.innerHTML += numberOfInputs;
row.innerHTML += " :";
// create the input for the key.
key = document.createElement("input");
key.setAttribute("id", "el" + numberOfInputs);
//add the key to the row.
row.appendChild(key);
row.after(br_key);
//create a row for the new description.
row2 = document.createElement("div");
// set the description text.
row2.innerHTML = "Description "
row2.innerHTML += numberOfInputs;
row2.innerHTML += " :";
// create the description input
description = document.createElement("input");
description.setAttribute("id", "dl" + numberOfInputs);
// add the description to the row.
row2.appendChild(description);
row2.after(br_description);
// add the rows for the key and the description to the inputBoxes.
inputs.appendChild(row);
inputs.appendChild(row2);
}
<div>Key Term 5 :<input id="el5"></div>
Any help figuring out this issue would be greatly appreciated. Thanks.
Your issue here is essentially incorrect HTML, CSS. I'd implement your inputs, etc. like this:
.full-width-label {
display:block;
}
<label class="full-width-label" for="1">Label</label>
<input type="text" id="1"/>
There are multiple ways to achieve the above, this is just one of your options but now you no longer need to embed the look into the HTML and the format of your HTML (line breaks) is independent of your look.
You might want to look into an off the shelf solution for these kinds of things, like Bootstrap or Tailwind
You can use make it in a simple way
HTML
add this jquery cdn
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<button type="button">Click Here</button>
<div class="appendDiv">
</div>
Js
$(document).ready(function (){
var button = document.getElementsByTagName('button');
var appendDiv = document.getElementsByClassName('appendDiv');
var key = 1;
var descKey = 1;
$('button').click(function(event){
event.preventDefault();
$(appendDiv).append('<div class="child"><div class="grand-child"><label>Key Form :'+ ' '+(++key)+'</label><input type="text" value=""/></div><div class="grand-child"></div><label>Description :'+ ' '+(++descKey )+'</label><input type="text" value=""/></div>');
})
})
I have appended data and I print each data price in hidden input. Now I want to sum these prices with input numbers as quantity in order to get the total amount but nothing prints.
Code
javascript
success:function(data) {
//append data to my view
data.forEach(function(row) {
var $meto = '';
$meto += '<div class="col-md-3 mt-2">'+
'<div class="card">'+
'<div class="card-body">'+
'<img src="{{url("images")}}/'+row['photo']+'" class="menuimg img-fluid" alt="image">'+
'<p>'+row['name']+'</p>'+
'<input type="number" name="qty[]" class="qty form-control">'+
'<input type="hidden" class="price" value="'+row['price']+'" />'+
'</div>'+
'</div>'+
'</div>';
$('.here').append($meto);
//sum prices
$('.qty').on('keyup',function(){
var qty = $('.qty').val();
var price = $('.price').val();
$("#total").html(qty*price);
});
});
}
html
<div class="col-md-10">
<div class="row here"></div>
</div>
<p>Total: <span id="total"></span></p>
Explanation
I have filled 2 of my items with quantities 1 & 3 there is hidden input under each of them that holds their prices (as this sample they are 5000 & 16000)
Basically I should have something like this in my total div:
1*5000 = 5000
3*16000 = 48000
Total = 53000
However, it only gets my first input and ignores the rest. Any ideas?
You are not adding all items prices. You need to loop through all items and calculate total.
Note: keep this after your forEach statement.
Try this:
$('.qty').on('keyup',function() {
var quantities = $('.qty');
var prices = $('.price');
var total = 0;
$.each(quantities, (index, qty) => {
total += parseInt($(qty).val() || 0) * parseFloat($(prices[index]).val() || 0)
});
$("#total").html(total);
});
I think the problem is, that you cant get the value from multiple elements with the function ".val()". You only can get multiple values from one element.
You need another function to do want you want to achieve.
You should have a look here: Stackoverflow Question
Anway - you should not save prices into hidden html elements in production environments.
Iterate over the parent element to find the exact price and quantity for each element.
$('.qty').on('keyup',function(){
var total = 0;
$('.card-body').each(function(){
var qty = parseFloat($(this).find('.qty').val());
if (!qty) return;
var price = parseFloat($(this).find('.price').val());
total += (qty * price);
});
$("#total").html(total);
});
Attaching a Fiddle
I don't know jQuery, though I can help you with vanilla JS.
The problem here is:
$('.qty').on('keyup',function(){
var qty = $('.qty').val();
var price = $('.price').val();
$("#total").html(qty*price);
});
This is setting an event listener only for the first .qty, even though there are many of them. So, you should select and iterate over them all, just like you did with the data:
let qtyNodes = document.querySelectorAll('.qty');
let priceNodes = document.querySelectorAll('.price');
let outputNode = document.querySelector('#total');
let sum = 0;
qtyNodes.forEach(function(qtyNode) { // Set event listener to every single qty node
qtyNode.addEventListener('keyup', function() {
qtyNodes.forEach((qtyNode, index) => { // Calculate final price, using each meal price per quantity value.
sum = qtyNode.value * priceNodes[index].value;
})
});
})
outputNode.innerHTML = `${sum}`;
It is important that you keep the arrays-like (qtyNodes and priceNodes) outside the given loop, as it will be accessing the DOM at each iteration and will demand too much performance. Placing it outside, you will access the DOM only one time.
Also, note that: you should be using "input" event, as opposed to "keyup". Input events will fire as soon as the user insert any data, just like a combination of "keyup" and "keydown".
Currently I am working on a project in C# MVC5 that will have a list of musicians. This list of musicians in my view has the name of the input in my view as musician[i]. When i go to delete a row in the middle of my table it will remove an integer that causes discontinuity in the array. So when my i submit my table to my controller that has musicians 1 through 5 but musician 4 has been deleted it will also skip 5.
In short I am trying to figure out how to delete a row then change the index numbers of all objects to be flush... so if i have musicians[0], musicians[1], musicians[2] and i delete index 1 then it will shift musician of index to change to index 1 since there is nothing there.
I have attached my Javascript that i am using. Not really anything else than just html at this point and Javascript in my opinion but i could be missing something too?
Thanks in advance for any advice and help!
<script type="text/javascript">
$(document).ready(function () {
$(document).on("click",
".classAddMusician",
function () {
var mrowCount = $('#musicianTableBody').children().length;
var musicianObject = '<tr><td><input class="form-control" type="text" name="track.Musicians[' + mrowCount + '].FirstName" id="track.Musicians[' + mrowCount + '].FirstName" placeholder ="First Name" value=""></td>' +
'<td><input class="form-control" type="text" name="track.Musicians[' + mrowCount + '].LastName" id="track.Musicians[' + mrowCount + '].LastName" placeholder="Last Name" value=""></td>'+
'<td><button type="button" id="btnAddInstrument' + mrowCount + '" class="btn btn-xs btn-primary classAddMusician">Add More</button>' +
'<button type="button" id="btnDelete' + mrowCount + '" class="delete btn btn btn-danger btn-xs">Remove</button></td></tr>';
$('#musicianTableBody').append(musicianObject); // Adding these controls to Main table class
});
$(document).on("click",
".delete",
function () {
$(this).closest("tr")
.remove(); // closest used to remove the respective 'tr' in which I have my controls
});
});
</script>
Controller (POST)
public async Task<ActionResult> Edit(Track track)
{
var instruments = track.Instruments.ToList();
track.Instruments.Clear();
var musicianList = track.Musicians.ToList();
track.Musicians.Clear();
var composerList = track.Composers.ToList();
track.Composers.Clear();
if (!HelperMethods.HelperMethods.IsNullOrEmpty(musicianList))
{
for(int i = 0; i < musicianList.Count(); i++)
{
var tempFirstName = musicianList[i].FirstName;
var tempLastName = musicianList[i].LastName;
if(db.Musicians.Any(x => x.FirstName.Equals(tempFirstName, StringComparison.InvariantCultureIgnoreCase) && x.LastName.Equals(tempLastName, StringComparison.InvariantCultureIgnoreCase)))
{
musicianList[i] = db.Musicians.First(f => f.FirstName.Equals(tempFirstName, StringComparison.InvariantCultureIgnoreCase) && f.LastName.Equals(tempLastName, StringComparison.InvariantCultureIgnoreCase));
}
else
{
musicianList.RemoveAt(i);
}
}
foreach(var x in musicianList)
{
track.Musicians.Add(x);
}
}
if (!HelperMethods.HelperMethods.IsNullOrEmpty(instruments))
{
for(int i = 0; i< instruments.Count(); i++)
{
var tempInstrumentName = instruments[i].Name;
if(db.Instruments.Any(x => x.Name.Equals(tempInstrumentName, StringComparison.InvariantCultureIgnoreCase)))
{
instruments[i] = db.Instruments.First(f => f.Name.Equals(tempInstrumentName, StringComparison.InvariantCultureIgnoreCase));
}
else
{
instruments.RemoveAt(i);
}
}
foreach(var x in instruments)
{
Instrument inst = new Instrument();
inst = x;
track.Instruments.Add(inst);
}
}
if (!HelperMethods.HelperMethods.IsNullOrEmpty(composerList))
{
for (int i = 0; i < musicianList.Count(); i++)
{
var tempFirstName = composerList[i].FirstName;
var tempLastName = composerList[i].LastName;
if (db.Musicians.Any(x => x.FirstName.Equals(tempFirstName, StringComparison.InvariantCultureIgnoreCase) && x.LastName.Equals(tempLastName, StringComparison.InvariantCultureIgnoreCase)))
{
composerList[i] = db.Composers.First(f => f.FirstName.Equals(tempFirstName, StringComparison.InvariantCultureIgnoreCase) && f.LastName.Equals(tempLastName, StringComparison.InvariantCultureIgnoreCase));
}
else
{
composerList.RemoveAt(i);
}
}
foreach (var x in musicianList)
{
track.Musicians.Add(x);
}
}
if (ModelState.IsValid)
{
db.Entry(track).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(track);
}
You appear to be appending musicians one row at a time.
Incrementing the mrowCount variable as the index for each one.
You are then posting back the inputs with their names; track.Musicians[0].FirstName, track.Musicians[1].FirstName, track.Musicians[2].FirstName etc.
You haven't supplied your controller's code, but I'll assume you are iterating over all of the track.Musician[?] items with an index.
If you delete one of these rows on the client, then the inputs you receive at the server will be missing one of these, i.e. track.Musicians[0], track.Musicians[2].
If you are iterating through these items you do not know how many were added or deleted. You may have had 100 musicians added, then 95 of them deleted; but which five were kept and what was the index used on each of them?
There are many ways to handle this, but here are a few of suggestions:
After removing the table row, rename all of the track.Musicians in
the remaining table rows starting from 0 to the last one (length -
1).
Capture the onSubmit() and iterate through all of the remaining
controls who's name starts with 'track.Musicians[' and re-name them
all starting from 0.
Use an extra hidden control that stores the state of the musician
(existing, added, or deleted). Then on a delete, only make the table row hidden - don't remove it. This way, all rows will be submitted
back to the server and you can decide what needs to be done with each
musician.
Use an array to store the current musicians and push to it on an add,
remove (track.Musicians.splice(index, 1);) on a delete. Bind the
table row controls to the values in the array using a framework such
as knockout.js, or redraw the table yourself if it is small. This way
the table will update automatically when you add or remove musicians
from the array.
When you submit your form, you will now have correctly indexed controls according to how many exist.
Of these I would recommend the third option out of personal choice. The HTML code would be smaller and cleaner.
Saying all this though, if you are editing an existing list of musicians, you would have to delete all existing musicians (for the applicable track) in the database and add all those submitted because you cannot match the indexes to those that have been saved.
You could add a hidden input that holds that particular row's musician's id, then compare them on the server for either updating (for existing), adding (if new), and deleting if marked as such.
$ctr = 0;
foreach($authspons as $authspons_list){
$ctr++;
echo "<input type='checkbox' id='id_list'".$ctr."'"." name='authspons[]'"." class='list_check' value='".$authspons_list->authspons_position." ".$authspons_list->authspons_lname.",".$authspons_list->authspons_fname." ".$authspons_list->authspons_mname." ".$authspons_list->authspons_cmpny.'; '."'".">" .$authspons_list->authspons_position." ".$authspons_list->authspons_lname.",".$authspons_list->authspons_fname." ".$authspons_list->authspons_mname." ".$authspons_list->authspons_cmpny."</input> <br />";
echo "<input type='text' class='text_list' id='tbox".$ctr."'"."name='authsponsid[]' value='".$authspons_list->authspons_id."; "."'"." >";
}
I have a generated list coming from database, the checkboxes contains the personal details, while the textboxes contains the "ID" of each items in the checkboxes.
This is my jquery code:
$('#list').click(function(){
var final = '';
var final2='';
$('.list_check:checked').each(function(){
var values = $(this).val();
final += values;
var val_id = $('.text_list').val();
final2 +=val_id;
});
$("#coauth").val(final);
$("#coauthid").val(final2);
$("#coauthspons").modal("hide");
});
Getting value from checkboxes is okay. My problem is: the output from the textbox is always the last value of the number of checkboxes that is checked.
Like this:
1st checkbox = 1st textbox(value="1");
2nd checkbox = 2nd textbox(value="2");
when i check the two options, the textbox only results to:
1st checkbox = 1st textbox(value="2");
2nd checkbox = 2nd textbox(value="2");
How do I get the result of each of the expected checkbox and its textbox value?
here var val_id = $('.text_list').val();
you are giving the same value for all the elements irrespective of their index
try like this
var val_id = $(this).next().val(); //this gives value of next i.e .text_list element
$('#list').click(function(){
var final = '';
var final2='';
$('.list_check:checked').each(function(){
var raw_value = $(this).val();
var id_value = raw_value.split("||");
var values = id_value[1];
final += values;
var val_id = id_value[0];
final2 +=val_id;
});
$("#coauth").val(final);
$("#coauthid").val(final2);
//alert(final2);
$("#coauthspons").modal("hide");
});
Thanks #Bhadra for your answer. it gives me an idea but I found a better solution to my own problem. I put all the values in the checkbox then I use split function to separate the "ID" from the original checkbox values(fname,mname,lname).
I have a form which is largely populated by checkboxes. The checkboxes each have an ID "value" that corresponds to an item within a javascript array. The array items hold some text that will populate a textarea.
I would like to include some dropdown boxes to clean up the site; however, I cannot seem to assign an array ID to the dropdown options? Can this be done in a selectbox option? Is there a workaround to simulate a selectbox without using the tab?
My html is basically:
<div>
<input type=checkbox id=array1 name=textArray></input>
<input type=checkbox id=array1 name=textArray></input>
<input type=checkbox id=array1 name=textArray></input>
...
<select><option 1><option 2>...</select>
</div>
<div>
<form>
<textarea id=outPut></textarea>
</form>
</div>
And my js is:
var textArray = {
array1: 'some text here',
array2: 'some more text',
array3: 'some other text',
...
array90: 'the last text'
};
// variable assigned to chosen item
var selectedInputs = document.getElementsByName("textArray");
for (var i = 0; i < selectedInputs.length; i++) {
selectedInputs[i].onchange = function() {
chosenItem = this;
printText();
};
}
// Script to add items to the Comments section text area
var mytextbox = document.getElementById('outPut');
var chosenItem = null;
function printText(){
if(chosenItem !== null){
mytextbox.value += textArray[chosenItem.id] + "";
// resets the radio box values after output is displayed
chosenItem.checked = false;
// resets these variables to the null state
chosenItem = null;
}
}
How can I associate an item in my js array with one of the selectbox choices?
I found it very difficult to understand what you're asking but I threw this together and hopefully it'll be helpful.
Important bit is
var selectNode = document.getElementById('select'); // <select id="select">
selectNode.onchange = function () {
if (selectNode.selectedIndex !== 0) {
chosenItem = selectNode.options[selectNode.selectedIndex];
selectNode.selectedIndex = 0;
printText();
}
}
and not to use the id attribute for what you're doing (I used data-i).
I'd also like to say that if you're cleaning up code this would be a good time to strongly reconsider how you're passing variables between functions; setting a value in the global namespace and relying on it in the next invocation is just asking for trouble (race conditions, conflicts with other bits of code, etc).
<option value="whatever">1</option> This has been part of HTML from the beginning.