$(function () {
var people = [];
var ctCells = [], questionCells = [], userCells = [];
var $tBody = $("#userdata tbody");
$.getJSON('https://api.myjson.com/bins/18g7fm', function (data) {
$.each(data.ct_info, function (i, f) {
ctCells.push(`<td id=${f.id}>${f.id}</td><td>${f.name}</td>`);
var users = []
var question = []
f.Qid_info.forEach((x) => {
x.user_info.forEach((y) => {
//avoid duplicates
var foundUser = users.find((user) => {
return user.id === y.id
})
if (!foundUser) {
users.push(y)
}
})
})
f.Qid_info.forEach((x) => {
var foundQuestion = question.find((questions) => {
return questions.id === x.id
})
if (!foundQuestion) {
question.push(x)
}
})
$.each(question, function (i, question) {
ctCells.push(`<td colspan="2"> </td>`)
questionCells.push(`<td id=${question.id}>${question.id}</td><td>${question.isActive}</td><td>${question["is complex"]}</td><td>${question["is breakdown"]}</td>`);
})
$.each(users, function (i, user) {
var a = user.data.map(function (key) {
return key
})
// ctCells.push(`<td colspan="2"> </td>`)
// questionCells.push(`<td colspan="${lent+1}"> </td>`)
userCells.push(`<td><div style="display:flex; flex-direction:row">
${
users.forEach(val => {
`<div style="display:flex; flex-direction:column">
<div>${val.id}${val.name}</div>
<div>${val.updatedAt}</div>
<div style="display:flex; flex-direction:column">
${user.data.forEach(IN => {
`
<div style="display:flex; flex-direction:row">
<div><p>${console.log(IN.git_ids)}</p></div>
</div>
`
})}
</div>
</div>`
})
}
</div></td>`)
// userCells.push(`<td id=${user.id}>UserId--- ${user.id} UserName---- ${user.name}${a.map(value => {
// return `
// <div id="${user.id}" >
// <td><input type="checkbox" style="display: inline;"> </td>
// <td> <span id="text">${Object.keys(value)[0]}</span></td>
// <td> <textarea type="text" class="gitplc" placeholder="GitId">${ value.git_ids}</textarea> </td> j
// </div>
// `
// })
// }</td><td>${user.updatedAt}</td>`);
})
});
console.log(userCells)
$.each(ctCells, function (i) {
console.log(userCells)
$tBody.append(`<tr>${ctCells[i]}${questionCells[i]}${userCells[i]}</tr>`)
})
});
});
#scrlSec {
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
}
/* .checkSec { width: 60%; } */
.checkSec .tbl {
padding-top: 20px;
}
.checkSec td {
padding-left: 10px;
}
.checkSec .btnGreen {
color: white;
background-color: #4CAF50;
border: none;
padding: 15px 32px;
width: 100%;
text-decoration: none;
}
.checkSec .gitplc {
width: 80%;
}
.checkSec #text {
font-size: 14px;
}
.checkSec .tbl .colOne {
width: 50%;
float: left;
}
.checkSec .tbl .colTwo {
width: 50%;
float: right;
}
#user {
overflow: auto;
}
.flex-container {
display: flex;
}
th,
td {
font-weight: normal;
padding: 5px;
text-align: center;
width: 120px;
vertical-align: top;
}
th {
background: #00B0F0;
}
tr+tr th,
tbody th {
background: #DAEEF3;
}
tr+tr,
tbody {
text-align: left
}
table,
th,
td {
border: solid 1px;
border-collapse: collapse;
table-layout: fixed;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id='userdata'>
<thead>
<tr>
<th colspan="2" id="ct">CT INFO</th>
<th colspan="4" id="que">Question</th>
<th id="user">User Info</th>
</tr>
<tr>
<th>CT ID</th>
<th>CT</th>
<th>Id</th>
<th>isActive</th>
<th>is Complex</th>
<th>is Breakdown</th>
<th>USER</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
I am printing json data in table format in html. But in users column i am getting undefined but when i print those variable in console that is printing correct value.
This is my json api . When i print value in foreach loop of user i am getting undefined but on console.log i have proper value printing in console.
https://api.myjson.com/bins/18g7fm
This is happening because you are using forEach() inside a template literal. forEach() returns undefined by default. Use map() function instead.
userCells.push(`<td><div style="display:flex; flex-direction:row">
${
users.map(val => {
return `<div style="display:flex; flex-direction:column">
<div>${val.id}${val.name}</div>
<div>${val.updatedAt}</div>
<div style="display:flex; flex-direction:column">
${user.data.map(IN => {
return `
<div style="display:flex; flex-direction:row">
<div><p>${console.log(IN.git_ids)}</p></div>
</div>
`
})}
</div>
</div>`
})
}
</div></td>`)
Related
I am currently trying to add a feature where when I click the name of a user in the table it will display all of the posts made by that User however I am completely stumped. The url for the posts is https://jsonplaceholder.typicode.com/posts
I currently have the table set up and will attach images of the table and code below.
Here is the problem I am trying to solve for further context
Using jQuery or vanilla JS you will display each USER in a table. When the user selects a USER in the table, it will display all of the 'POSTS' that were created by that USER.
Make use of event delegation together with Element.closest and the data-* attribute respectively its HTMLElement.dataset counterpart.
document
.querySelector('tbody')
.addEventListener('click', ({ target }) => {
const currentRow = target.closest('tr');
const userId = currentRow.dataset.id;
console.clear();
console.log('userId: ', userId);
});
body {
margin: 0 0 0 2px;
}
table {
border-collapse: collapse;
}
thead th {
color: #fff;
font-weight: bolder;
background-color: #009577;
}
th, td {
padding: 4px 10px 6px 10px;
color: #0a0a0a;
}
tbody tr:nth-child(even) {
background-color: #eee;
}
tbody tr:hover {
outline: 2px dotted orange;
}
tbody tr {
cursor: pointer;
}
tbody tr:nth-child(even) { background-color: #eee; }
tbody tr:hover { outline: 2px dotted orange; }
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Username</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr data-id='1'>
<td>1</td>
<td>Leanne Graham</td>
<td>Bret</td>
<td>Sincere#april.biz</td>
</tr>
<tr data-id='2'>
<td>2</td>
<td>Ervin Howell</td>
<td>Antonette</td>
<td>Shanna#melissa.tv</td>
</tr>
<tr data-id='3'>
<td>3</td>
<td>Clementine Bauch</td>
<td>Samantha</td>
<td>Nathan#yesenia.net</td>
</tr>
<tr data-id='4'>
<td>4</td>
<td>Patricia Lebsack</td>
<td>Karianne</td>
<td>Julianne.OConner#kory.org</td>
</tr>
<tr data-id='5'>
<td>5</td>
<td>Chelsey Dietrich</td>
<td>Kamren</td>
<td>Lucio_Hettinger#annie.ca</td>
</tr>
</tbody>
</table>
A vanilla based implementation of a component-like approach which is partially configurable via data-* attributes then possibly might look like the next provided example code ...
function emptyElementNode(node) {
[...node.childNodes].forEach(child => child.remove());
}
function clearTableContent(root) {
[
...root.querySelectorAll('thead'),
...root.querySelectorAll('tbody'),
].forEach(child => child.remove());
}
function createTableHead(headerContentList) {
const elmThead = document.createElement('thead');
const elmTr = headerContentList
.reduce((root, content) => {
const elmTh = document.createElement('th');
elmTh.textContent = content;
root.appendChild(elmTh);
return root;
}, document.createElement('tr'));
elmThead.appendChild(elmTr);
return elmThead;
}
function createTableBody(rowContentKeyList, userList) {
return userList
.reduce((elmTbody, userItem) => {
const elmTr = rowContentKeyList
.reduce((root, key) => {
const elmTd = document.createElement('td');
elmTd.textContent = userItem[key];
root.appendChild(elmTd);
return root;
}, document.createElement('tr'));
elmTr.dataset.id = userItem.id;
elmTbody.appendChild(elmTr);
return elmTbody;
}, document.createElement('tbody'));
}
function createAndRenderPostItem(root, { title, body }) {
const elmDt = document.createElement('dt');
const elmDd = document.createElement('dd');
elmDt.textContent = title;
elmDd.textContent = body;
root.appendChild(elmDt);
root.appendChild(elmDd);
return root;
}
function updateSelectedStates(selectedRow) {
[...selectedRow.parentNode.children]
.forEach(rowNode =>
rowNode.classList.remove('selected')
);
selectedRow.classList.add('selected');
}
function handleUserPostsRequestFromBoundData({ target }) {
const { postsRoot, requestUrl, placeholder } = this;
const currentRow = target.closest('tr');
const userId = currentRow?.dataset?.id;
if (userId) {
createListOfUserPosts({
postsRoot,
url: requestUrl.replace(placeholder, userId)
});
updateSelectedStates(currentRow);
}
}
async function createListOfUserPosts({ postsRoot, url }) {
emptyElementNode(postsRoot);
if (postsRoot && url) {
const response = await fetch(url);
const postList = await response.json();
postList.reduce(createAndRenderPostItem, postsRoot);
}
}
async function createListOfUsers({ usersRoot, postsRoot }) {
const usersRequestUrl = usersRoot.dataset.request;
const userPostsRequestUrl = postsRoot.dataset.request;
const userPostsPlaceholder = postsRoot.dataset.placeholder;
const response = await fetch(usersRequestUrl);
const userList = await response.json();
if (userList.length >= 1) {
const displayConfig = JSON.parse(
usersRoot.dataset.display ?? '{}'
);
const headerContentList = Object.values(displayConfig);
const rowContentKeyList = Object.keys(displayConfig);
emptyElementNode(postsRoot);
clearTableContent(usersRoot);
usersRoot.appendChild(
createTableHead(headerContentList)
);
usersRoot.appendChild(
createTableBody(rowContentKeyList, userList)
);
usersRoot.addEventListener(
'click',
handleUserPostsRequestFromBoundData
.bind({
postsRoot,
requestUrl: userPostsRequestUrl,
placeholder: userPostsPlaceholder,
})
);
}
}
function initializeUserPostsComponent(root) {
const usersRoot = root.querySelector('[data-users]');
const postsRoot = root.querySelector('[data-posts]');
createListOfUsers({ usersRoot, postsRoot });
}
document
.querySelectorAll('[data-user-posts]')
.forEach(initializeUserPostsComponent);
body { margin: 0 0 0 2px; font-size: .8em; }
table { border-collapse: collapse; }
table caption { text-align: left; padding: 3px; }
thead th { color: #fff; font-weight: bolder; background-color: #009577; }
th, td { padding: 3px 5px; color: #0a0a0a; }
tbody tr:nth-child(even) { background-color: #eee; }
tbody tr:hover { outline: 2px dotted orange; }
tbody tr.selected { background-color: rgb(255 204 0 / 18%); }
tbody tr { cursor: pointer; }
[data-user-posts]::after { clear: both; display: inline-block; content: ''; }
.user-overview { float: left; width: 63%; }
.user-posts { float: right; width: 36%; }
.user-posts h3 { margin: 0; padding: 4px 8px; font-size: inherit; font-weight: normal; }
<article data-user-posts>
<table
data-users
data-request="https://jsonplaceholder.typicode.com/users"
data-display='{"id":"Id","name":"Name","username":"Username","email":"Email"}'
class="user-overview"
>
<caption>Select a user to view posts</caption>
</table>
<div class="user-posts">
<h3>Posts:</h3>
<dl
data-posts
data-request="https://jsonplaceholder.typicode.com/users/:userId/posts"
data-placeholder=":userId"
>
</dl>
</div>
<article>
To add a click event handler to a table row with JavaScript, we can loop through each row and set the onclick property of each row to an event handler.
For instance, we add a table by writing:
<table>
<tbody>
<tr>
<td>foo</td>
<td>bar</td>
</tr>
<tr>
<td>baz</td>
<td>qux</td>
</tr>
</tbody>
</table>
Then we write:
const createClickHandler = (row) => {
return () => {
const [cell] = row.getElementsByTagName("td");
const id = cell.innerHTML;
console.log(id);
};
};
const table = document.querySelector("table");
for (const currentRow of table.rows) {
currentRow.onclick = createClickHandler(currentRow);
}
to add event handlers to each row.
Reference : https://thewebdev.info/2021/09/12/how-to-add-an-click-event-handler-to-a-table-row-with-javascript/#:~:text=Row%20with%20JavaScript-,To%20add%20an%20click%20event%20handler%20to%20a%20table%20row,row%20to%20an%20event%20handler.&text=to%20add%20event%20handlers%20toe,table%20row%20as%20a%20parameter.
I have currently figured out a way to store value of 6am plan in a content editable box in my local storage key
item 2
How do I make this happen for all the other hours like 1
work plan for every hour of the day 6 am - 11pm
storing input in one key
using this code snippet below
javascript -
var content = document.getElementById('content'),
address = document.getElementById('address'),
saveButton = document.getElementById('save'),
loadButton = document.getElementById('load'),
clearButton = document.getElementById('clear'),
resetButton = document.getElementById('reset');
var localStore = {
saveLocalStorage: function() {
localStorage.setItem('item', content.innerHTML);
},
loadLocalStorage: function() {
var contentStored = localStorage.getItem('item');
if ( contentStored ) {
content.innerHTML = contentStored;
}
},
clearLocalStorage: function() {
localStorage.removeItem('item');
}
};
saveButton.addEventListener('click', function() {
localStore.saveLocalStorage();
}, false);
<r class="notion-table-row">
<td
style="color: inherit; fill: inherit; border: 1px solid gb(233, 233, 231); position: relative; vertical-align: top; min-width: 122px; max-width: 122px; min-height: 32px;">
<div class="notion-table-cell">
<div class="notion-table-cell-text"
spellcheck="true" placeholder=" "
data-content-editable-leaf="true"
style="max-width: 100%; width: 100%; white-space: pre-wrap; word-break: break-word; caret-colour: gb(55, 53, 47); padding: 7px 9px; background-colour: transparent; font-size: 14px; line-height: 20px;"
content editable="false">11 PM</div>
</div>
</td>
<td
style="color: inherit; fill: inherit; border: 1px solid gb(233, 233, 231); position: relative; vertical-align: top; min-width: 200px; max-width: 200px; min-height: 32px;">
<div class="notion-table-cell">
<div class="notion-table-cell-text"
spellcheck="true" placeholder=" "
data-content-editable-leaf="true"
style="max-width: 100%; width: 100%; white-space: pre-wrap; word-break: break-word; caret-colour: gb (55, 53, 47); padding: 7px 9px; background - colour: transparent; font-size: 14px; line-height: 20px;"
<section id="11pm_input" content editable="true"></div>
Store all of the data in an array. Keep in mind, localStorage stores only strings so anything not a string (ex. array, object, number, etc.), must be converted into a string when saved to localStorage:
localStorage.setItem("String", JSON.stringify([...Array]))
and when it is retrieved from localStorge it needs to be parsed into it's original type:
const data = JSON.parse(localStorage.getItem("String"));
In the example below, the data is gathered from all .cell which comprise of the following HTML elements from the table head and body (in ex. 4 x time, 6 x data):
<time class='cell' datetime="23:00">11 PM</time>
<data class='cell' value="0ne Zer0">Zer0 0ne</data>
time.datetime = "23:00";
time.textContent = "11 PM";
data.value = "0ne Zer0";
data.textContent = "Zer0 0ne";
lStorage = [
["23:00", "11 PM"],
["0ne Zer0", "Zer0 0ne"],
["00:00", "12 AM"],
...[N, N]
];
The event handler manageData(event) delegated all click events triggered on the table head, body, and foot.
Variables:
key is whatever the user typed in .input, the value will be assigned to data being saved to localStorage.
data is declared for data coming from and to localStorage.
cells is an array of all time.cell and data.cell tags within the table head and body.
node is determined by event.target property which always refers to the tag the user actually clicked. This reference will be delegated to react according to the .matches() method and a series of flow control statements (if, if else, and else).
The process for loading data from localStorage involves loadData(key) and iteration of the array of arrays saved in localStorage and the array of .cells:
if (node.matches('.load')) {...
...
data = loadData(key);
cells.forEach((n, i) => {
n.textContent = data[i][1];
if (n.matches('time')) {
n.datetime = data[i][0];
} else {
n.value = data[i][0];
}
});
The rest of the code is of simular fashion -- here's a brief description of what it can do:
Load data from key in localStorage and popualte a <table> with it.
Save data from a <table> to a key in localStorage.
Reset data the user typed in the fields (if given a selector, it can reset it as well).
Clear data by key and all data in <table> (including static data).
All .cells are editable (including headers). Double click any .cell within the table head or body to toggle it in/out of edit mode.
Due to SO security policy, WebStorage API will not function, go to: Plunker
The link doesn't work for me when clicked but
I managed to use the url by copy & paste
to the address bar:
https://run.plnkr.co/preview/ckz3pfkfe0007386nlancnzqk/
If the links above don't work, you can copy & paste the code in the Snippet with a text editor and save the file with a *.html extension.
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
:root {
font: 1ch/1.5 'Segoe UI';
}
body {
font-size: 2ch;
}
table {
table-layout: fixed;
width: 70vw;
margin: 20px auto;
}
th {
width: 50%;
font-size: 2.25rem;
}
td {
vertical-align: center;
}
.box {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
min-height: 32px;
padding: 8px 8px 5px;
}
.cell {
display: block;
max-width: 100%;
width: 100%;
min-height: 25px;
white-space: pre-wrap;
word-break: break-word;
font-size: 2rem;
border: 2px solid #000;
background: transparent;
text-align: center;
}
.head {
width: 97.5%;
border-color: transparent;
}
.edit {
border-color: blue;
}
button {
display: block;
font: inherit;
font-size: 2rem;
background: transparent;
border-radius: 6px;
cursor: pointer;
}
button:hover {
border-color: blue;
color: blue;
background: #ddd;
}
.ctrl {
position: relative;
flex-flow: row nowrap;
justify-content: space-between;
min-width: 92%;
height: 30px;
margin-top: 40px;
}
.input {
position: absolute;
top: -40px;
left: -0.5vw;
width: 99%;
height: 25px;
}
.noKey {
color: tomato;
font-weight: 900;
border-color: tomato;
}
.noKey::before {
content: 'Enter the key to data';
}
.done {
color: blue;
border-color: blue;
}
.done::before {
content: 'Data is saved under key: "';
}
.done::after {
content: '"';
}
</style>
</head>
<body>
<main>
<section>
<table>
<thead>
<tr>
<th>
<data class="cell head" value="Hour">Hour</data>
</th>
<th>
<data class="cell head" value="Employee">Employee</data>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<fieldset class="box">
<time class="cell">8 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">9 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">10 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
<tr>
<td>
<fieldset class="box">
<time class="cell">11 PM</time>
</fieldset>
</td>
<td>
<fieldset class="box">
<data class="cell edit" contenteditable></data>
</fieldset>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2">
<fieldset class="ctrl box">
<data class="cell input edit" contenteditable></data>
<button class="load" title="Load data">
Load</button>
<button class="save" title="Save data">
Save</button>
<button class="reset" title="Reset fields">
Reset</button>
<button class="clear" title="Clear saved data and Reset fields">
Clear</button>
</fieldset>
</td>
</tr>
</tfoot>
</table>
</section>
</main>
<script>
const tab = document.querySelector('table');
const hdr = tab.tHead;
const mid = tab.tBodies[0];
const ftr = tab.tFoot;
const cls = ['noKey', 'done'];
const inp = document.querySelector('.input');
const formatTime = time => {
const T = time.split(' ');
return T[1] === 'PM' ? `${+T[0]+12}:00` : `${T[0]}:00`;
};
const lastFirst = name => name.split(' ').reverse().join(', ');
const loadData = key => JSON.parse(localStorage.getItem(key));
const saveData = (key, data) => localStorage.setItem(key, JSON.stringify(data));
const resetData = selector => {
let nodes = selector === undefined ? '.edit' : selector;
[...document.querySelectorAll(nodes)].forEach(n => {
n.textContent = '';
if (n.matches('time')) {
n.datetime = '';
} else {
n.value = '';
}
});
inp.classList.remove(...cls);
};
const clearData = key => {
resetData('.cell');
inp.classList.remove(...cls);
localStorage.removeItem(key);
};
const manageData = e => {
let key = inp.textContent;
let data;
const cells = [...document.querySelectorAll('.cell')];
const node = e.target;
if (node.matches('.load')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
data = loadData(key);
cells.forEach((n, i) => {
n.textContent = data[i][1];
if (n.matches('time')) {
n.datetime = data[i][0];
} else {
n.value = data[i][0];
}
});
}
} else if (node.matches('.save')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
data = cells.flatMap(n => {
if (n.matches('time')) {
n.datetime = formatTime(n.textContent);
return [
[n.datetime, n.textContent]
];
} else {
n.value = lastFirst(n.textContent);
return [
[n.value, n.textContent]
];
}
});
inp.classList.add('done');
saveData(key, data);
}
} else if (node.matches('.reset')) {
resetData();
} else if (node.matches('.clear')) {
if (key.length < 1) {
inp.classList.add('noKey');
} else {
clearData(key);
}
} else if (node.matches('.input')) {
node.textContent = '';
node.classList.remove(...cls);
} else {
return;
}
};
ftr.onclick = manageData;
const toggleEdit = e => {
const node = e.target;
if (node.matches('.cell')) {
node.classList.toggle('edit');
node.toggleAttribute('contenteditable');
}
};
hdr.ondblclick = toggleEdit;
mid.ondblclick = toggleEdit;
</script>
</body>
</html>
I have this fiddle:
https://jsfiddle.net/n9epsy5x/2/
I've got drag and drop working for the column headers, but I'd like a header's whole column to move when dragging and dropping the header. How can I pull this off in plain Javascript (VanillaJS)?
I tried adding classes to the table headers and accompanying column elements to be able to target the column elements to get them to move, but I couldn't get that to work.
Any help would be greatly appreciated. Thanks!
var source;
function isbefore(a, b) {
if (a.parentNode == b.parentNode) {
for (var cur = a; cur; cur = cur.previousSibling) {
if (cur === b) {
return true;
}
}
}
return false;
}
function dragenter(e) {
var targetelem = e.target;
if (targetelem.nodeName == "TD") {
targetelem = targetelem.parentNode;
}
if (isbefore(source, targetelem)) {
targetelem.parentNode.insertBefore(source, targetelem);
} else {
targetelem.parentNode.insertBefore(source, targetelem.nextSibling);
}
}
function dragstart(e) {
source = e.target;
e.dataTransfer.effectAllowed = 'move';
}
[draggable] {
user-select: none;
}
body {
background-color: #fff;
color: #303;
font-family: sans-serif;
text-align: center;
}
li {
border: 2px solid #000;
list-style-type: none;
margin: 2px;
padding: 5px;
}
ul {
margin: auto;
text-align: center;
width: 25%;
}
<table>
<thead>
<tr>
<th ondragstart="dragstart(event)" ondragenter="dragenter(event)" draggable="true">
Column 1
</th>
<th ondragstart="dragstart(event)" ondragenter="dragenter(event)" draggable="true">
Column 2
</th>
<th ondragstart="dragstart(event)" ondragenter="dragenter(event)" draggable="true">
Column 3
</th>
</tr>
</thead>
<tbody>
<tr>
<td>hhgr</td>
<td>ffrr</td>
<td>qedf</td>
</tr>
<tr>
<td>wdfe</td>
<td>cjnb</td>
<td>cdke</td>
</tr>
<tr>
<td>awjb</td>
<td>cdjk</td>
<td>ijfe</td>
</tr>
</tbody>
</table>
I'm new to angualr, and developing some application on angular.
I have a table where the rows will be added dynamically, and pagination is used to traverse through the table. I have used 5 rows per page and selection of these rows per page can be selected by a dropdown.
Problem Facing: When i click on add button, each time it adds the row. After adding 5 rows if i click on add button then 2nd page pagination will be displayed.
But the rows in the second page contains the data of first page only.
If I select the dropdown to show to items the 6th item will be empty.
The 6th text box under pagination of page 2 selects the value of 1st input of first page.
Please help me to solve this pagination issue.
TypeScript code:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormArray, FormGroup } from '#angular/forms';
import { SelectItem } from 'primeng/api';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
createOrderForm: FormGroup;
isAllPortTnsSelected: boolean;
tablePaginationDropDowns: SelectItem[];
tableRowsToBeDisplayed: number;
totalTns: number;
constructor(private formBuilder: FormBuilder) {
this.tableRowsToBeDisplayed = 5;
this.totalTns = 0;
}
ngOnInit() {
this.loadTablePaginationDropDowns();
//load the form with all form controls.
this.createOrderForm = this.formBuilder.group({
tnList: this.formBuilder.array([]),
tnListDropDown: ['']
});
// any change in the porting tn table pagination dropdown, it should reflect the value
this.createOrderForm.get('tnListDropDown').valueChanges.subscribe(
data => {
this.changePortingTnDropDownValue(data);
}
)
}
loadTablePaginationDropDowns() {
this.tablePaginationDropDowns = [
{ label: "5", value: "five" },
{ label: "10", value: "ten" },
{ label: "15", value: "fifteen" },
{ label: "20", value: "twenty" }
]
}
addTnGroup(): FormGroup {
return this.formBuilder.group({
tnCheckBox: [''],
fromTn: [''],
toTn: ['']
});
}
addTnRow() {
//get the reference for the portingTnList and add one more set of tnGroup
// before adding the tn group we need to typecast the control to FormArray
(<FormArray>this.createOrderForm.get('tnList')).push(this.addTnGroup());
this.totalTns = (<FormArray>this.createOrderForm.get('tnList')).length;
}
removeTnRow(group: FormArray = <FormArray>this.createOrderForm.get('tnList')) {
const tempArray: FormArray = new FormArray([]);
Object.keys(group.controls).forEach (
(key: string) => {
const abstractControl = group.get(key);
if(abstractControl instanceof FormGroup) {
if (!this.removeTnRowIfSelected(abstractControl)) {
tempArray.push(abstractControl);
}
}
}
);
while(group.length != 0) {
group.removeAt(0);
}
while(tempArray.length != 0) {
group.push(tempArray.at(0));
tempArray.removeAt(0);
}
this.totalTns = group.length;
}
removeTnRowIfSelected(group: FormGroup): boolean{
if (group.get('tnCheckBox') && group.get('tnCheckBox').value !== '') {
return true;
} else {
return false;
}
}
selectAllTns() {
this.selectAndDisSelectAllTns();
this.isAllPortTnsSelected = !this.isAllPortTnsSelected;
}
selectAndDisSelectAllTns(group: FormArray = <FormArray>this.createOrderForm.get('tnList')) {
Object.keys(group.controls).forEach(
(key: string) => {
const abstractControl = group.get(key);
if (abstractControl instanceof FormGroup) {
this.selectAndDisSelectTnsInGroup(abstractControl);
}
}
)
}
selectAndDisSelectTnsInGroup(group: FormGroup) {
if (!this.isAllPortTnsSelected) {
group.get('tnCheckBox').setValue('select');
} else {
group.get('tnCheckBox').setValue('');
}
}
AddBulkTns() {
console.log(this.createOrderForm.get('tnList'));
}
/**
* Method changeDropDownValue() used to reflect the changes done for the dropdown values which are used to
* decide the number of rows to be displayed in the table
*
* #param data : Accepts the string value of dropdown
*/
changePortingTnDropDownValue(data: string) {
if (data === 'ten') {
this.tableRowsToBeDisplayed = 10;
} else if (data === 'fifteen') {
this.tableRowsToBeDisplayed = 15;
} else if (data === 'twenty') {
this.tableRowsToBeDisplayed = 20;
} else {
this.tableRowsToBeDisplayed = 5;
}
}
}
HTML Code:
<h3>Dynamic</h3>
<form [formGroup]="createOrderForm">
<div class="div-grid">
<div style="width: 100%;">
<div class="top-div-checkbox">
<input class="form-check-input position-static" type="checkbox" style="margin-left: 30%; width: auto"
(click)="selectAllTns()">
</div>
<div class="top-div-label1">
<label>From TN</label>
</div>
<div class="top-div-label2">
<label>To TN</label>
</div>
</div>
<div id="gridDiv">
<table class="test">
<tbody formArrayName="tnList">
<tr [formGroupName]="j"
*ngFor="let tnGroup of createOrderForm.get('tnList').controls |
paginate:{itemsPerPage: tableRowsToBeDisplayed, currentPage: page}; let j = index">
<td class="td-checkbox">
<input type="checkbox" formControlName="tnCheckBox" [id]="'tnCheckBox'+j">
</td>
<td class="td-input1">
<input type="text" formControlName="fromTn" [id]="'fromTn'+j">
</td>
<td class="td-input2">
<input type="text" formControlName="toTn" [id]="'toTn'+j">
</td>
</tr>
</tbody>
</table>
</div>
<div class="nav-div">
<div class="pagination-div">
<pagination-controls previousLabel="" nextLabel="" (pageChange)="page = $event"></pagination-controls>
</div>
<div class="page-dropdown-div">
<select name="tnListPageDropDown" id="tnListPageDropDown" class="custom-select mr-sm-2"
formControlName="tnListDropDown" style="width: 60px">
<option value="" disabled selected>5</option>
<option *ngFor="let dropDown of tablePaginationDropDowns" [value]="dropDown.value">{{dropDown.label}}</option>
</select>
<label> items per Page</label>
</div>
<div class="total-items-div">
Total {{totalTns}} items
</div>
</div>
<div>
<button type="button" class="btn btn-info list-btns" (click)="addTnRow()">Add</button>
<button type="button" class="btn btn-info list-btns" (click)="removeTnRow()">Remove</button>
<button type="button" class="btn btn-info list-btns" (click)="AddBulkTns()">Bulk Add</button>
</div>
</div>
</form>
StyleSheet
.test {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
table-layout:fixed;
border: 1px solid #ddd;
}
div {
border: 1px solid #ddd;
}
.test th, .test td {
border: 1px solid #ddd;
padding: 8px;
}
.test tr:nth-child(even) {
background-color: #f2f2f2;
}
#gridDiv {
width: 100%;
overflow-y:auto;
height: 200px;
border: 1px solid black;
}
.div-grid {
border: 1px solid black;
width: 50%;
}
td input {
width: 100%;
}
.td-checkbox {
width: 7%;
}
.td-input1{
width: 60%;
}
.td-input2{
width: 33%;
}
.top-div-checkbox {
width: 7%;
border: 1px solid #ddd;
float: left;
}
.top-div-label1 {
width: 60%;
border: 1px solid #ddd;
float: left;
}
.top-div-label2 {
width: 33%;
border: 1px solid #ddd;
float: left;
}
.nav-div {
border: 1px solid #ddd;
height: 48px;
}
.pagination-div {
width: 50%;
float: left;
}
.page-dropdown-div {
width: 30%;
float: left;
}
.total-items-div {
width: 20%;
float: left;
}
.list-btns {
padding-left: 5%;
padding-right: 5%;
margin-right: 10%;
margin-left: 2%;
}
I am implemented successfully the recursive table. Then I added the (click) method in the parent tr in order to call the child method to fetch data from database to display in the child tr.the problem is i am able to get the data in the child. but the child tr immediately appear and disappear.Could you please anyone help me on this to display the child tr when i click the parent tr.
sampletree() {
var = ele < HTMLTableElement > document.getElementById("tableid");
ele.addEventListener("click", (ev: MouseEvent) => {
var element = ev.target as HTMLElement;
if (element.tagName !== "A")
return false;
ev.preventDefault();
var row = element.closest("tr");
var cls = row.classList[0];
var lvl = +(cls.slice(-1)) + 1;
cls = cls.slice(0, -1) + lvl;
while ((row = nextTr(row)) && (row.className.includes(cls) || row.className.includes("open")))
row.classList.toggle("open");
});
function nextTr(row) {
while ((row = row.nextSibling) && row.nodeType != 1);
return row;
}
}
getTableParent1(): void {
this.service.getTableParent1()
.subscribe(TableParent1 => this.TableParent1 = TableParent1;
}
getChild(): void {
this.service.getTablechild1()
.subscribe(Tablechild1 => this.Tablechild1 = Tablechild11;
}
ngOnInit() {
this.TableParent1();
this.sampletree();
}
tbody>tr {
display: none;
}
/* Simplified */
tr.level0,
tr.open {
display: table-row;
}
/* Added colors for better visibility */
/* Added some more styling after comment */
tr td {
padding: 0.2em 0.4em;
}
tr td:first-of-type {
position: relative;
padding: 0.2em 1em;
}
tr td a {
color: #0e879a;
text-decoration: inherit;
position: relative;
left: -0.73em;
font-size: 19px;
}
tr.level1 td:first-of-type {
padding-left: 1.5em;
}
tr.level2 td:first-of-type {
padding-left: 2em;
}
<table class="table table-bordered table-hover" id="tableid">
<thead>
<tr>
<th>test1</th>
<th>test2</th>
</tr>
</thead>
<tbody>
<tr class="level0" *ngFor="let tavles of tables">
<td>+{{tavles.test2}}</td>
<td>{{tavles.test1}}</td>
</tr>
<tr class="level1" *ngFor="let child of tablechild">
<td>+{{child.test1}}</td>
<td>+{{child.test2}} </td>
</tr>