I have a grid array of objects and it has default data from the database , now on the front end the data are being displayed on the table/grid and user can add row and delete row , when I add a row I only want to insert an empty object.
my issue is when I add a row duplicate ids are existing and when I delete only just a single row sometimes multiple rows are being deleted
What seem to be the issue of my implementation ? how do I delete and add rows without compromising the ids ? Thanks.
#grid data - from the database
gridTableData [
{
"id": 0,
"demos": "21 Est. Pop",
},
{
"id": 1,
"demos": "Households",
},
{
"id": 5,
"demos": "Avg HH Inc",
},
]
#add and delete row code
deleteRow(id: number) {
const filteredDemographics = this.gridTableData.filter(s => s.id !== id);
this.gridTableData = [...filteredDemographics];
}
addRow() {
this.gridTableData.push({id:this.gridTableData.length});
console.log("this.gridTableData" , this.gridTableData)
}
#html
<tbody *ngIf="gridTableData.length > 0">
<tr *ngFor="let row of gridTableData;let i = index" [ngClass]="{'row-is-editing':isRowEditing(i)}">
<td>
<button *ngIf="checkIfExistsOnDefaultGrid(row) === false" class="material-icons-outlined" mat-icon-button color="error"
(click)="deleteRow(row.id)">
<mat-icon>delete_outline</mat-icon>
</button>
</td>
</tr>
</tbody>
If you are adding with the row length and removing by index you might get some weird cases.
For example, if you add two entries in your snippet, you will have 2 rows with ID 5. Therefore, when you filter/delete, you would remove two entries.
If you want change the order, always use the index to remove or add.
If you want to use ID, make sure the ID will always be unique.
Here is a possible solution making the ID random
https://stackblitz.com/edit/angular-pebbqb?file=src/app/app.component.ts
Related
I'm working on a web app that lets people schedule lessons and timeslots, and I'm trying to make it so that people can click on the timeslot to accept it. Currently, the table is retrieved from the database and generated with this code:
function updateSchedules(name = "missing", grade = "missing", date = "missing", time = "missing", status = "missing", id) {
let table = document.getElementById('schedules');
let color;
console.log("Output schedule for " + name);
if (status.toLowerCase() === 'pending') {
color = "background-color:rgb(252, 38, 0)";
} else if (status.toLowerCase() === 'confirmed') {
color = 'background-color:rgb(0, 255, 13)';
} else {
color = 'background-color:rgb(35, 64, 153)';
}
table.innerHTML += `
<tr>
<td>${name}</td>
<td>${grade}</td>
<td id=testday2>${date}</td>
<td id=testtime2>${time}</td>
<td style="${color}">${status}</td>
</tr>`;
}
Is there a way to add buttons on each row of the table that refers to each row's unique id without just making buttons and adding event listeners manually for each row?
You have to add one more <td> tag with <button> tag and bind with onlick event and pass unique_id in function parameter like this:
<td><button type="button" onclick="myfunc(${id})"></button></td>
${id} may be string or number it's depend on requirements
Note-:
You have to pass unique id from (backend).
unique_id might be primary key of database row.
Then write script to handle button like-:
<script>
function myfunc(unique_id){
console.log(unique_id);
// do something
}
<script>
More appropriate if you use anchor tag with button like-:
<td><a onclick="myfunc(${id}")><button type="button"></button></a></td>
There are multiple ways to do this same thing. More appropriate if you use this keyword with jquery instead of write pure javascript.
I have a script that generates a dynamic table from values stored in an array.
Though the array has seven properties (equivalent to seven columns), the dynamic table is designed to display only three columns out of seven possible columns. The reason for displaying only three columns out of seven, is due to layout design limitations.
What I would like to do to resolve this issue, is to make one cell in the table a clickable link , that opens a popup bootstrap card that displays a detailed content (with the values from the all the seven properties) of that particular row.
Find below my script which shows the array configuration and the dynamic table script:
var array = [{
TransactionTime: "August 2, 2019 4:34 PM"
amount: "100"
payersNumber: "0705357658"
paymentStatus: "Success! payment received."
statusIcon: "<i class='fas fa-check-circle colorBlue'></i> <a href='#'>"
transactionNumber: "ATPid_40a31c1aad441a3de2f70a17669e651a"
waitersName: "John.P"
},
{
TransactionTime: "August 1, 2019 2:34 AM"
amount: "150"
payersNumber: "0705357658"
paymentStatus: "Failed! payment not received."
statusIcon: "<i class='fas fa-check-circle colorRed'></i> <a href='#'>"
transactionNumber: "ATPid_40a31c1aad441a3de2f70a17669e651a"
waitersName: "Maggie.A"
}];
table = document.getElementById("table");
var currentTransaction;
var keys = ["statusIcon", "amount", "TransactionTime"];
for (var i = 0; i < array.length; i++) {
console.log("Number of transactions: " + array.length);
var newRow = table.insertRow(table.length);
currentTransaction = array[i];
for (var b = 0; b < keys.length; b++) {
var cell = newRow.insertCell(b);
cell.innerText = currentTransaction[keys[b]];
}
}
Find below my HTML code:
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.10.0/css/all.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.10.0/css/v4-shims.css">
<table id="table" border="1">
<tr>
<th>Status</th>
<th>Amount</th>
<th>Time</th>
</tr>
</table>
As you have probably noticed, all the Status cells/columns become links. How do I get each of these links to open up to Bootstrap cards that contain all the relevant row values specific each row?
Find below an example of what the generated table would look like:
Also, the HTML code demonstrating the structure of a table row:
One solution would be to not use anchor tags and instead use JS event listeners.
You would need to assign a unique 'id' to each row in the column.
Add event listeners to either each row and screen for the user clicking the status button/image OR add event listeners to each status button/image and traverse the HTML tree to see which row the button belongs to.
Once you know the row that got called, you can then use JS to extract the information and package it up into an object for your bootstrap modal/card.
UPDATE:
You could do something like this,
// after you insert a new row you get the element node back...
var newRow = table.insertRow(table.length);
// assign a unique id value to it
newRow.id = 'something';
// add an event listener to the row
newRow.addEventListener('click', event => {
// when someone clicks on the ROW, you get a hit, but you're not sure what part of the row they clicked on...
//...so create a check to make sure they clicked on the button
if (event.target === 'some_way_to_indicate_the_status_button') {
let row_number = event.target.id;
// use the row number to grab all data from that row
// package all of that data into an object {} or array, whichever you choose, then do what you want with it
}
});
I have table generated with dynamic ids like this one
<table>
<tbody>
<tr *ngFor="let row of createRange(seats.theatreDimension.rowNum)">
<td id={{row}}_{{seat}} *ngFor="let seat of createRange(seats.theatreDimension.seatNumInRow)">
</td>
</tr>
</tbody>
</table>
I want to access table td element from angular 2 .ts file. Here are functions:
ngOnInit() {
this.getSeats();
}
getSeats() {
this.cinemaService.getAllSeats(this.repertoire.id).subscribe(
data => {
this.seats = data.json();
this.setReservedSeats();
}
)
}
setReservedSeats() {
this.seats.reservedSeats.forEach(seat => {
let cssSeatId = seat.rowNumReserved + "_" + seat.seatNumInRowResereved;
document.getElementById(cssSeatId).className += "reserved";
}
)
}
and after that I want dynamically to set class of every td, but I am getting this console error in my browser:
ERROR TypeError: Cannot read property 'className' of null
Just to note once again that I generate td ids dynamically. They are in form rowNum_cellNum.
I am getting this object from api.
{
"theatreDimension": {
"rowNum": 17,
"seatNumInRow": 20
},
"reservedSeats": [
{
"rowNumReserved": 9,
"seatNumInRowResereved": 19
},
{
"rowNumReserved": 2,
"seatNumInRowResereved": 4
},
{
"rowNumReserved": 15,
"seatNumInRowResereved": 15
}
]
}
I am using theatreDimension to make table. Then I try to make reserved seats from reservedSeats array with red background (reserved)
How I can access td elements from angular 2 and solve this issue?
Instead of accessing the DOM directly, you should try using the ngClass directive to set the class:
<td [ngClass]="{'reserved': isReserved(row, seat)}" id={{row}}_{{seat}} *ngFor="let seat of createRange(seats.theatreDimension.seatNumInRow)">
</td>
You can then implement the isReserved(row, seat) function, and if it returns true, it will apply the reserved class.
isReserved(rowNum: number, seatNum: number) {
return this.seats.reservedSeats.some((r) => r.rowNumReserved === rowNum && r.seatNumInRowResereved === seatNum);
}
To directly answer your question, in order to get the element by ID, you need to do so after the page has been renedered. Use the function ngAfterViewInit(). You will need to remove the call to setReservedSeats() from getSeats().
ngOnInit() {
this.getSeats();
}
ngAfterViewInit(){
this.setReservedSeats();
}
However, I would suggest going a different route. You can set the style of the element based on whether or not the seat has been reserved. Assuming you have some sort of "reserved" flag on the seat object you can do something like this:
<td id={{row}}_{{seat}}
[ng-class]="{'reserved' : seat.researved}"
*ngFor="let seat of createRange(seats.theatreDimension.seatNumInRow)">
</td>
I have never used filters extensively in angularjs and I just saw a problem with one of my projects..
The filter that I apply to a table is filtering all columns except for the one that multiplies the values of two items in the repeater.
1. Is this meant to work this way?
2. Is there a way to filter the column that multiplies numbers or adds strings together?
Here is the plunker with a working (not working) example
http://plnkr.co/edit/pmCjQL39BWWowIAgj9hP?p=preview
I have a filter that I apply to ng-repeater...
<input type="text" ng-model="tableSearch">
And then in table..
<tr ng-repeat="items in tableData | filter:tableSearch">
<td> {{ items.qty }} </td>
<td> {{ items.price }} </td>
// hm.....
<td> {{ items.qty*items.price }} </td>
</tr>
Update
Thanks to guidance from #Chandermani below I resolved the issue writing a custom filter. Here is the updated plunk: http://plnkr.co/edit/pmCjQL39BWWowIAgj9hP?p=preview
$scope.tableFilter = function(item) {
for (key in item) {
var m = item[key].toString();
var s = (item.qty * item.price).toString();
if(
s.indexOf($scope.tableSearch) != -1 ||
m.indexOf($scope.tableSearch) != -1 ||
$scope.tableSearch == undefined
){
return true;
};
}
return false;
}
Then filter is used like this:
<tr ng-repeat="items in tableData | filter:tableFilter">
Filter work on model data, in your case this data
{
"id": 1,
"name": "Apples",
"price": 19.99,
"qty": 1
},
{
"id": 2,
"name": "Oranges",
"price": 29.99,
"qty": 1
},
The last column has been generated in html. You would have to write a custom filter function for supporting that or add the last column value also to your model.
For custom filter function you can do something like
$scope.tableFilter=function(item) {
//should return true for the item to show up.
}
see documentation for filter http://docs.angularjs.org/api/ng/filter/filter
I am trying to make a table containing several rows, each with a button in the last cell that creates a copy of the row.
All the other cells contains an input (text).
The content (value) of the inputs that are added must be the same as the one above (the one they are copies of).
The copies cannot be copied however!
The inputs must have a unique name something like this:
1-1-name
1-1-age
1-1-country
1-1-email
and if this row is copied, the copied inputs must have names like this
1-2-name
1-2-age
1-2-country
1-2-email
The next one with 3 instead of 2, and so on.
The problem with this, I guess, is that I must do this without JQuery. I can only use Javascript. Is this even possible?
Take a look at this fiddle. Here is a pure js (no-jQuery) way to duplicate a table row and increment it's ID:
var idInit;
var table = document.getElementById('theTable');
table.addEventListener('click', duplicateRow); // Make the table listen to "Click" events
function duplicateRow(e){
if(e.target.type == "button"){ // "If a button was clicked"
var row = e.target.parentElement.parentElement; // Get the row
var newRow = row.cloneNode(true); // Clone the row
incrementId(newRow); // Increment the row's ID
var cells = newRow.cells;
for(var i = 0; i < cells.length; i++){
incrementId(cells[i]); // Increment the cells' IDs
}
insertAfter(row, newRow); // Insert the row at the right position
idInit++;
}
}
function incrementId(elem){
idParts = elem.id.split('-'); // Cut up the element's ID to get the second part.
idInit ? idParts[1] = idInit + 1 : idInit = idParts[1]++; // Increment the ID, and set a temp variable to keep track of the id's.
elem.id = idParts.join('-'); // Set the new id to the element.
}
function insertAfter(after, newNode){
after.parentNode.insertBefore(newNode, after.nextSibling);
}
<table id="theTable">
<tr id="1-1">
<td id="1-1-name"><input type="text"/></td>
<td id="1-1-age"><input type="text"/></td>
<td id="1-1-country"><input type="text"/></td>
<td id="1-1-email"><input type="text"/></td>
<td id="1-1-button"><input type="button" value="Copy"/></td>
</tr>
</table>
Edit: Updated to insert the new row after the clicked one. Now with buttons and inputs!
Yes this is possible,
you should create a new table row ,
then set its innerHTML to the innerHTML of the row above.
jQuery is a JavaScript library, which means it is built with JavaScript functions.
So everything you can do with jQuery, you can do with JavaScript too.
Léon