html table is duplicating defaultValue's for no apparent reason - javascript

I have some data that is displayed in a table. When I add a table data cell (td) with JS the defaultValue and innerText of the previous table data cell (td) is being copied in the new table data (td). I don't understand why this happens? I just add a new html element which, in essence, has nothing to do with the already existing element I would say.
const [dataSet, setDataSet] = useState([
{
City: "Amsterdam",
Provence: "North-Holland"
},
{
City: "Rotterdam",
Provence: "South-Holland"
}
]);
const newCity = () => {
const city = [
{
City: "Groningen",
Provence: "Groningen"
}
];
setDataSet(city);
};
<button onClick={newCity}>Add city</button>
<table>
<tr>
<th>City</th>
<th>Provence</th>
</tr>
{dataSet.map(city => (
<tr>
<td>
<input defaultValue={city.City}/>
</td>
<td>
<input defaultValue={city.Provence}/>
</td>
</tr>
)}
</table>
So when I click on the 'newCity button' a new table row (tr) is added, but instead of populating the table data cells (td) with the correct data (City: 'Groningen', Provence: 'Groningen') it populates the td with the data from the previous table row (tr) (City: 'Amsterdam', Provence: 'North-Holland'). This seems really odd to me, since I only add a new html element that has no relation to the table row (tr) before it exempt for it being in the same table.
Here is a codebox link to see the behavior in action:
https://codesandbox.io/s/dry-water-rss2m8?file=/src/App.js
Why does it behave like this?

There are two important things to take into consideration here.
First, your program is outputting a warning:
Warning: Each child in a list should have a unique "key" prop.
Always deal with warnings. They are there for a reason!
Second, you are dealing with a default (as opposed to current) value.
When you replace the array with a new array, React generates new JSX and compares it to the existing DOM.
The first row has the same (lack of) key so it changes the default values of the existing row. (The second row no longer exists so it removes it).
Changing the default values doesn't have any effect through: The inputs already have values, which they continue to display.
You could give the generated rows keys:
<tr key={something}>
… but you will need to figure out what the something is.
You might use the city name, but it seems like that is a piece of data which might change — so changing the city name would make it look like a new row and not an update to an existing row.
You might consider using the uuid module to generate a unique ID when the object is created.
You could also make the inputs controlled by passing them values instead of default values. This would require that you update the value you pass to them as they are edited.

You need to set a key on the item
{dataSet.map((city) => (
<tr key={city.City}>
...
etc

I guess it is because you are working with uncontrolled components.
If you want your input values to reflect state changes you need to use the value and not the defaultValue, and you also probably want to have an onChange handler.
<input value={city.City} />

First, the native Input element doesn't have defaultValue attribute. Look at the attributes list in MDN. It has only value and placeholder.
Secondly, if you want to add new city to the dataSet, you must merge the city-object to the array, not replace it.
setDataSet(currentDataSet => {
return [...currentDataSet, city]
})

Related

How do I access a row object of this grid?

I am trying to access an individual row from a grid in Kendo UI so that I can run operations on the selected entries in the row. However, my code is not correctly grabbing the row itself and I do not know how to resolve this.
I've tried binding the grid to an event that, when changed, will fire my method in order to grab whichever row was toggled.
const row = arg.sender.element.closest("tr")
const grid = $("#ECUs").getKendoGrid()
const dataItem = grid.dataItem(row)
Results:
I.fn.init [prevObject: I.fn.init(1)]
length: 0
prevObject: I.fn.init [div#ECUs.k-grid.k-widget.k-display-block]
__proto__: w
(Sorry, I apparently don't have enough reputation to post images)
Ideally, I would expect to get a tr object back in the first method, but I'm getting absolutely nothing instead. Does anybody know how to correct this to access the row?
If you have a click event on one of the columns, you can access the table row using some jquery.
function onClickListener(e) {
e.preventDefault();
var row = this.dataItem($(e.currentTarget).closest("tr"));
}
Option 1:
You can use the edit event of a grid to get the currently selected row model.
edit: function(e) { console.log(e.model); }
Here, e.model contains the row data and you can access the particular column value by e.model.columnName.
Option 2: You can get the row model data like below from the other functions.
https://stackoverflow.com/a/56478061/8733214

insertBefore not updating rowIndex/nextSibling properties

It may sound stupid or even trivial for most experienced users, but I just landed a few hours ago on front-end javascript and I must say I am a bit puzzled with the behavior of the insertBefore javascript function.
My intention here is plain and simple: I have a table with its rows and cells, and in each row I have a cell with a button with the only purpose of duplicating that cell (with all its contents) and place the new duplicated cell right next to the original one.
I have a javascript function for it such like this one:
// id -> the id of the table I want the row to be added
// caller -> the object of the element that called the function
function duplicateRow(id, caller)
{
const table = document.getElementById(id);
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
table.insertBefore(clone, row.nextElementSibling);
}
This function is called like this (from an extract of my HTML):
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( 'competencies-table', this )"></button>
</td>
</tr>
So, what I would expect from it is that, at each click on the duplicate row button, it would create an exact copy of the row where the button is being clicked and add it right after that row.
My problem here is not with the duplicating (that is done just right and smooth as one would expect) but with where the new row is placed:
The first time, when there is only one row, it is placed at the end (since nextSibling is null).
The second time clicking the button on the first row (despite now having a sibling right after it), the new row is again placed at the end of the table (as if nextSibling for the first row was still null).
And so on (even strager placements happen when mixing duplications with the newly added rows).
Shouldn't the nextSibling and/or rowIndex properties be updated when adding a new node to the DOM? Is there a way of forcing them to update? What is it that I have wrong? My code, my understanding of how it should work?
I am surely open to any possible explanation/solution/alternative to achieve what I need, and thank you all in advance!
The problem is that initial table row is wrapped in a tbody element (for which you can omit both start and end tag), which is required according to the content model of tables. However, when you programmatically add more rows, they are inserted outside the tbody and your initial row is the only child of that implicit tbody, so the DOM tree looks like this:
<table>
<tbody>
<tr></tr>
</tbody>
<tr></tr>
<tr></tr>
</table>
To solve it I suggest to add a clone to cloned row's parent:
function duplicateRow(caller){
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
row.parentNode.insertBefore(clone, row.nextElementSibling);
}
<table id="competencies-table">
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( this )">Duplicate</button>
</td>
</tr>
</table>

React Input Element Not Updating When Updating Component

I'm trying to make a React version of this.
You can see what I have so far on this Codepen.
The issue here is that when you type into a text field (under the "Suspected Player" column) and then click the adjacent name (under the "Role" column) to remove that row, the text persists inside the input, and is then adjacent to a different name.
The expected behavior is that you'd click the name to remove the row and both the elements (the name and text input) would disappear.
All the items in the table are stored in an array called checklist in the state of the RoleList class.
The component for each list item in the table is this:
class RoleCheckpoint extends React.Component {
constructor() {
super();
this.state = {
text: ""
};
}
deleteThis() {
this.props.removeRole(this.props.role.id);
}
onChange(e) {
this.setState({
text: e.target.value
});
}
render() {
console.log(this.props);
return (
<tr>
<td className="vertAlign remove noselect" onClick={this.deleteThis.bind(this)}>
{this.props.role.el}
</td>
<td>
<input type="text" className="form-control" spellCheck="false" onChange={this.onChange.bind(this)} value={this.state.text} />
</td>
</tr>
);
}
}
When you click on the name it invokes the components deleteThis function, which in turn invokes RoleList's removeRole function (that goes through the array of list items, and removes the one with the matching ID from the components state), which is being passed as a prop to RoleCheckpoint.
I've tried using this.forceUpdate() in the deleteThis function, and as you can see I've tried binding the input field's value to the components state (called text), yet still the input doesn't update when removing the component from the DOM.
Any help is appreciated, if you need more clarification I'll be happy to provide it.
A "key" is a special string attribute you need to include when creating lists of elements.
<RoleCheckpoint role={role} key={role.id} removeRole={this.removeRole.bind(this)} />
The behaviour is typical when you have a list of components and you have not set the key of the objects correctly. It need to be something that identifies the object.
Probably you can use the name field:
<RoleCheckpoint key={role.name} .../>
See https://facebook.github.io/react/docs/lists-and-keys.html

Delete multiple ng-repeat elements on click of a single button

I have a table showing project names as shown in below image ,
On delete button click, I am passing the selected check-boxes data as array of objects to my controller
[{id:1,name:'Name 8'},{id:2,name:'Name 7'}]
Then deleting the names from the table at the server side. This all works fine but how do i remove the rows from DOM after deleting them?? I went through this post which says how to remove ng-repeat elemets from DOM but in that case the elements are removed one at a time, by passing the $index to the splice() function
In my case i need to remove multiple rows. If ill have to use the splice function in my controller how do i get the index from the selected rows object? Or is there any better way of doing it.
Hope my question is clear!
Update: jsFiddle
Solution: Well i had to modify #wickY26 answer a bit to suite my scenario. Here is my update jsFiddle
What i did was , in the delete() change code to
angular.forEach($scope.projects, function (row, index) {
if($scope.projects[index].checked) {
$scope.projects.splice(index,1);
}
});
You can keep selected rows on an object via binding checkbox with ng-model,
so example table html should be like this
HTML
<table class="table table-bordered">
<tbody>
<tr ng-repeat="row in data" ng-class="{'success' : tableSelection[$index]}">
<td>
<input type="checkbox" ng-model="tableSelection[$index]" />
</td>
<td ng-repeat="cell in row">
{{cell}}
</td>
</tr>
</tbody>
</table>
and if you define a function in your controller which travel through your data and splice array depends on tableSelection object boolean values...
UPDATE
after your comment I debug my code and see I cannot remove multiple rows same time, so I look at my code and change some part of it...
at my example you cannot delete multiple rows same time because everytime you splice an element from data array you shift indexes of array for rest, so right way to do it starting from last index,
Here new CONTROLLER
$scope.removeSelectedRows = function() {
//start from last index because starting from first index cause shifting
//in the array because of array.splice()
for (var i = $scope.data.length - 1; i >= 0; i--) {
if ($scope.tableSelection[i]) {
//delete row from data
$scope.data.splice(i, 1);
//delete rowSelection property
delete $scope.tableSelection[i];
}
}
};
I update my PLUNKER added comments and some other functionallty as well...

dataTable fnUpdate row with new value

I am using datatables and have this tr element in table
<tr class="gradeA even row_selected" id="3692">
<td class=" sorting_1">3692</td>
<td class="">koza</td>
<td class="" title="10:12:30">2013-12-31</td>
<td class="">2014-02-06</td>
<td class="">FULL packet</td>
<td class="">NONE</td>
<td class="">Name</td>
</tr>
I would like to update 1st and 4th td element using fnUpdate function. I have tried to update for only one td but it does not update.
In Chrome, console log I am getting this error:
Uncaught TypeError: Cannot set property '_aData' of undefined
Here is what I have tried:
// dynamically update row
$('#example').dataTable().fnUpdate( ['Zebra'], parseInt('3692'));
3692 is the id of the td element to know which row I need to update, and the zebra is the value to change. I know that I have not included which cell to update but I don't know how to do that. On datatables api, following example is given:
oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], 1 ); // Row
Please review the docs here http://datatables.net/api
Your question is not complete, as you need to specify what column(td) you want to modify, but here's what I would try (assuming you want to update the second column).
$('#example').dataTable().fnUpdate('Zebra' , $('tr#3692')[0], 1 );
The second parameter will be the row, and the third is the column.
Note that I passed in a string.
I prefer this:
var myDataTable= $('#myDataTableId').DataTable();
var row = myDataTable.row( '#idRow');
myDataTable.cell(row, 2).data("New Text").draw();
Note:
2 is column, cell inside modified row.
This works for me
var tableRow = $(this).closest('tr').index(); // GET TABLE ROW NUMBER
$('#table').dataTable().fnUpdate('Zebra', [tableRow], 1, false)
You don't need to specify the column. Your issue is that you are using row ID when the doc states that 2nd argument can be the aoData index or the element.
Make sure your number of columns is correct, but you should be able to do it like so:
$('#example').dataTable().fnUpdate( ['Zebra'], $('#example tr#3692')[0]);
In my case I wanted to update an entire row with data coming from backend as an object. Referring to:
$('#example').dataTable().fnUpdate('Zebra' , $('tr#3692')[0], 1);
I modified it a bit and used this lines of code to update an entire row without having to draw entire data table:
// get an selected row/row under edit action
let ele = $("#DTTableID")..dataTable().find('tr.row_selected');
$("#DTTableID").dataTable().fnUpdate(obj, ele);
Here obj is data object and ele is selected row.
Try:
$('#datatable').dataTable().fnUpdate(result, $('[data-id=' + idvalue + ']'), 8 );
This is best way to update column value without error and add table row with data-id="row id value" ....it works fine.

Categories