removing dynamically generated table rows - javascript

I have a dynamically created table named "jobTable" based on some text inputs and a button and also save the data into an array at the same time. Each row of the table contains a button which is used to remove the entire row and update the array. Whenever a user tries to delete a row, the row will be correctly deleted but the array will be completely flushed and I lose all of the data. I guess it must be because of that all of the dynamically generated buttons share the same 'id' so the button's selector's function will be invoked more than one time, but it works fine on the table itself! Why is this happening?
var workExperience = [];
$("#jobTable").on('click', '#DeleteRow', function () {
$removeditem = $(this).closest('tr').parent().index();
$(this).closest('tr').remove();
delData($removeditem);
});
function delData($x) {
workExperience.splice($x - 1, 1);
console.log(workExperience); //the array will be flushed in a few steps!
}
$("#save").click( function () {
//Save the data into an array inorder to send it to the server later.
workExperience.push($("#companyName").val());
//Append data into some table in order to make the user able to see or remove each row he/she enters
$("#jobtable").append(
"<tbody>" +
"<tr>" +
"<td>" +
"<button type='button' id='DeleteRow'>"+
"</td>" +
"<td>" +
$("#companyName").val() +
"</td>" +
"</tr>" +
"</tbody>");
});

I lack the reputation to make this a comment, but could you please provide some more information on your HTML?
Alternatively if you don't mind adding a library. Knockout.js could handle this. See this tutorial.

I am not sure why you would like to store data at two places and try to maintain on delete button click.
Creating delete button with same ID is not good idea either.
I took a liberty and put together example for storing data into data- attribute for each company. I have stored just string into "data-workExp", but you can pretty much add any object like array. Here is the entire code for you:
$(function () {
$("#jobtable").on('click', '.DeleteRow', function () {
$(this).parent().remove();
});
$('#toServer').click(function () {
$('#serverData').html("");
$('.CompanyRow').each(function () {
$('#serverData').append("<div>" + $(this).data("workExp") + "</div>");
});
});
$("#save").click(function () {
$("#jobtable").append(
$('<div>').addClass('CompanyRow').data('workExp', $("#companyName").val())
.html($("#companyName").val() + "<input type='button' class='DeleteRow' value='Delete Me'>")
);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<div>
<input id="companyName" type="text"><br />
<input type="button" id="save" value="Save">
<input type="button" id="toServer" value="Save to server!"/>
</div>
<div id="jobtable" >
</div>
<h1>Data goes to server</h1>
<div id="serverData"></div>

Related

How to pass a value from a Javascript generated button to a controller?

My code generates a table with a button at the end of each row. When the user clicks a button how can I pass a property u.userEmail to the controller via the button? Will the value being sent to the controller be a string?
My (non-working) attempt:
<script>
$(document.body).append("waiting on async table to load<br>");
$(document).ready(function () {
$.getJSON("/Account/LoadClaimsTable", function (crewResponse) {
//returns a List<UserClaims>
$(document.body).append("<table>")
crewResponse.forEach(function (u) {
var s = "";
s+="<tr><td>" + u.userEmail + "</td>";
u.userClaims.forEach(function (k) {
console.log("added claim"+k.value);
s += ("<td>" + k.type + "</td><td>" + k.value + "</td><td>" +
"<input type=\"hidden\" name=\"userEmail\" value=\"`${u.userEmail}`\" />"+
"<input type=\"button\" value=\"Create\" onclick=\"location.href='#Url.Action("EditClaims", "Account")'" />
+"</td>");
});
s += "</tr>";
$(document.body).append(s);
s = "";
});
$(document.body).append("</table>")
});
});
</script>
AccountController.cs contains:
public ActionResult EditClaims(string userEmail)
{
return View("StringView", userEmail);
}
You have to pass it on the url of the action. Not sure if you want to pass u.userEmail, but it could looks like this:
crewResponse.forEach(function (u) {
var s = "<tr><td>" + u.userEmail + "</td>";
u.userClaims.forEach(function (k) {
console.log("added claim"+k.value);
s += ("<td>" + k.type + "</td><td>" + k.value + "</td><td>" +
"<input type=\"hidden\" name=\"userEmail\" value=\"`${u.userEmail}`\" />"+
"<input type=\"button\" value=\"Create\" onclick=\"location.href='#Url.Action("EditClaims", "Account")?userEmail=" + u.userEmail + "'\"/></td>");
});
s += "</tr>";
$(document.body).append(s);
});
There are multiple ways to do it. One is mentioned in the answer above by Felipe. Here is another alternate approach using unobtrusive js
Add the email as html5 data attributes to the button along with another attribute which we will use bind the click behavior.
u.userClaims.forEach(function (k) {
// Add quotes as needed if you want multiline ( i just removed those)
s += "<td>" + k.type + "</td><td>" + k.value + "</td><td>
<input type='button'
clickablebutton data-email='" + u.email + "' value='Create'/></td>";
});
Now, in your document ready, bind a click event handler to those elements (with our custom attribute) and read the data attribute and build the url you need.
$(document).on("click", "input[clickablebutton]", function (e){
var url = '#Url.Action("EditClaims", "Accounts")?useremail=' + $(this).data("email");
window.location.href = url;
});
Some other suggestions
Use the appropriate element. Button is better than input (Consider accessibility)
If it is for navigation, Use an anchor tag instead of a button.
Inline javascript is not great. Let the browser parses your markup without any interruptions and you can add the behavior scripts later (that is the whole point of uobutrisive js approach)
The approach you appear to be taking would be Ajax, response, render a template. With that being said, you may want to rethink your approach.
Step 1.
Build a template
<template id="...">
<button type="button" value="[action]" onclick="[url]">[text]</button>
</template>
Step 2.
Create your request.
axios.get('...').then((response) => {
// Retrieve template.
// Replace bracket with response object model data.
html += template.replace('[action]', response.action);
});
Step 3.
Have the JavaScript render your template.
The above can create a clear concise codebase that is easier to maintain and scale as the scope changes, rather than an individual request performing a change with embedded markup. This approach has worked quite well for me, also I feel it'll make you troubleshooting and definition easier, as the controller is handing an object back to your JavaScript instead of a markup / view data. Which will be a better finite control for the frontend and clear modifications in future.

<pattern> & required doesn't work on dynamically created form

I'm trying to implement client-side input validation for a datatables-editor that I'm rewriting. The form is created dynamically and then added to a bootstrap-modal.
I have encountered a problem where adding <pattern> and/or required doesn't result in any added functionality at all. The form just accepts the input and submits, and I'm quite confused as to why that is.
EDIT:
I have added the relevant code to a plunkr
I have now added the full project. Specifically the issue is connected to the _openEditModal function and _openAddModal function, where i generate the forms dynamically and add the pattern='patternVariable'.
The pattern for this example (however it doesn't work no matter what pattern I use):
^[a-zA-Z0-9\.]+$
Creating the form:
var data = "";
data += "<form name='altEditor-form' role='form'>";
for(var j = 0; j < columnDefs.length; j++){
data += "<div class='form-group'><div class='col-sm-3 col-md-3 col-lg-3 text-right' style='padding-top:7px;'><label for='" + columnDefs[j].title + "'>" + columnDefs[j].title + ":</label></div><div class='col-sm-9 col-md-9 col-lg-9'>";
if(columnTypes[j].type.includes("text")){
data += "<input type='" + columnTypes[j].type + "' id='" + columnDefs[j].title + "' pattern='" + columnPattern[j].pattern + "' title='" + patternErrMsg[j].msg + "' required name='" + columnDefs[j].title + "' placeholder='" + columnDefs[j].title + "' style='overflow:hidden' class='form-control form-control-sm' value='" + adata.data()[0][newaData[j].name] + "'>";
}
if(...){...}
data +="</div><div style='clear:both;'></div></div>";
}
data += "</form>";
As you can see I add the tags like so:
pattern='" + columnPattern[j].pattern + "' title='" + patternErrMsg[j].msg + "' required ...
The modal:
$('#altEditor-modal').on('show.bs.modal', function() {
$('#altEditor-modal').find('.modal-title').html('Edit Record');
$('#altEditor-modal').find('.modal-body').html(data);
$('#altEditor-modal').find('.modal-footer').html("<button type='button' data-content='remove' class='btn btn-default' data-dismiss='modal'>Close</button>\
<input type='submit' data-content='remove' class='btn btn-primary' id='editRowBtn'>Save Changes</input>");
I made sure that the button has type='submit' as I've read that this is what triggers the pattern-check.
editRowBtn code:
$(document).on('click', '#editRowBtn', function(e)
{
e.preventDefault();
e.stopPropagation();
that._editRowData();
});
To make sure that my code is actually adding the attributes to the input i checked the console:
Any help or advice is greatly appreciated as I'm kinda stuck here.
It's a little hard to read your examples (a plunkr would be nice :) ), but from what I can see, you've put your submit button outside your form.
That won't work, since the button won't know what it's submitting.
Try putting the submit button inside the form.
Alternatively, try using the form attribute on the submit button, which should reference the form ID. I've never used this myself, but according to MDN, it's part of the HTML5 spec.
Form attribute description from MDN:
The form element that the input element is associated with (its form owner). The value of the attribute must be an id of a element in the same document. If this attribute is not specified, this element must be a descendant of a element. This attribute enables you to place elements anywhere within a document, not just as descendants of their form elements. An input can only be associated with one form.

How can I retrieve input values from elements added after page load?

I'm working with Zend Framework 1.12 and I've need to be able to dynamically add and delete fields from a sub-form, in this case we're associating hyperlinks to a parent "promotion".
I haven't found a way to accomplish dynamically adding and removing elements via Zend, and the rare tutorial I've found that claimed to do this are half a decade old and aren't working when I attempt them.
So what I am doing is storing the links I need to work with in a Zend Hidden input field and then dealing with the JSON data after I submit. Not very efficient, but it's the only thing I've gotten to work so far.
Below is the section of the code I'm working with:
Assume a form like:
<form action="/promos/edit/promo_id/15" method="POST" id="form_edit">
<!-- input is Zend_Form_Element_Hidden -->
<input type="hidden" id="link_array" value="{ contains the JSON string }"/>
<button id="add_link">Add Link</button>
</form>
The purpose is that every time the Add Link button is pressed, the form adds fields to allow the user to input new hyperlinks that will be associated with the specific items.
Here's the function:
// add links
$('#add_link').click(
function(e) {
e.preventDefault();
link = '<div class="p_link new_link">' +
'<div class="element_wrap">' +
'<label for="link_name" class="form_label optional">Text: </label>' +
'<input type="text" id="new_link_name" name="link_name"/>' +
'</div>' +
'<div class="element_wrap">' +
'<label for="link_http" class="form_label optional">http://</label>' +
'<input type="text" id="new_link_http" name="link_http"/>' +
'</div>' +
'<div class="element_wrap">' +
'<button class="submit delete_link">Delete</button>' +
'</div>' +
'</div>';
$('#add_link').prev().after(link);
}
);
Now, what I need to do is on submit, for every new_link class element, to take the links name and http reference and place it in a json object. Here's the code as I have it so far (I know I don't have both input fields represented at this point):
$('#submit').click(
function(e) {
e.preventDefault();
var link_array = [];
var new_links = document.getElementsByClassName('new_link');
$.each(new_links, function() {
console.log(this);
var n = $(this).children('#new_link_name').text();
console.log(n);
link_array.push({'link_name':n}); //'link_http':h
});
console.log(JSON.stringify(link_array));
}
);
My problem is that: var new_links = document.getElementsByClassName('new_link'); will collect all the newly added new_link elements, but it does not pull in any value that has been input into the text fields.
I need to know how I can apparently bind any input I make to the input field's value attribute, because right now anything I type into these new elements are tossed out and the field appears empty when it's anything but.
$('#submit').click(
function(e) {
e.preventDefault();
var link_array = [];
var new_links = $('.new_link');
$.each(new_links, function() {
console.log(this);
var n = $(this).find('input').val(); // you need input values! This line //is changed...
console.log(n);
link_array.push({'link_name':n}); //'link_http':h
});
console.log(JSON.stringify(link_array));
}
);
JSFIDDLE: http://jsfiddle.net/DZuLJ/
EDit: You can't have multiple IDS (make class for each input, and target class, if you want link names and http's)

Edit dynamically created row in a form

I have difficulties to edit in form dynamically added rows. For example if some of the cell in the row have error - clicking on it will feed the form with selected row. Hope I'm clear.
Here is a code:
(document).ready(function(){
$("#inputtitle").append("<input type='text' name='type1' placeholder='Title'/><br>");
$("#inputremarks").append("<input type='text' name='type2' placeholder='Remarks'/><br>");
$("#inputdate").append("<input type='text' name='type3' placeholder='Date'/><br>");
$("#inputoption").append("<input type='text' name='type4' placeholder='Option'/><br>");
});
$("form").submit(function(e){
e.preventDefault();
var newName = $('form').find('input[name="type1"]').val();
var newName1 = $('form').find('input[name="type2"]').val();
var newName2 = $('form').find('input[name="type3"]').val();
var newName3 = $('form').find('input[name="type4"]').val();
$('table').append('<tr><td>' + newName + '</td><td>' + newName1 + '</td><td>' + newName2 + '</td><td>' + newName3 + '</td></tr>' );
});
Sorry for being not clear. My question was how to feed back the form when click to one of the new created row. Below is updated JSFiddle with approximate solution, but not correct, because probably need another button with name UPDATE and may be need another column with ID. DEMO.
I desided to go by different way. So I created additional button to run a script for editing cell of created table. The link to the code you can find [here] Using jQuery to edit individual table cells

Sorting consistency of dynamically created IDs on DOM Elements

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.

Categories

Resources