Loop through an array of objects and display in HTML - javascript

I have an array as below:
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}];
Now, i want to loop through the array and display them int the below dom:
<div id="locations-grid">
<div id="di-locations">
<div id="plot"></div>
<div id="address"></div>
<div id="city"></div>
<div id="pin"></div>
<div id="phone"></div>
</div>
</div>
Each object in the array corresponds to one location. I want to display all the locations as different columns in a css grid.
I tried
locations.map((item)=>{
plot.innerHTML = item.plot;
address.innerHTML = item.address;
city.innerHTML = item.city;
pin.innerHTML = item.pin;
phone.innerHTML = item.phone;
});
But this displays only the last object in the array. This doesn't get me the 3 objects into 3 different columns of the grid.

Putting your structure in DIVS is not the right way use a TABLE instead of it (here with a header-row). You had to dynamical build the rows and cell of it because you don't know how many rows will be needed and it's much more structured.
First get the handler from your table with document.getElementById Than create for every row a new table-row TR and then iterate through your data. For each property of it create a new cell TD and add to it's innerHTML the value. Append the TD to your row TR with appendChild because the elemnts is till now not in the DOM. After you have done this for every property append the TR to the table. Now the tablerow will be presented in the table.
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}
];
let table = document.getElementById('di-locations');
locations.forEach(location => {
let tr = document.createElement('tr');
Object.entries(location).forEach(value => {
let td = document.createElement('td');
td.innerHTML= value;
tr.appendChild(td);
});
table.appendChild(tr);
});
td, th { border: 1px solid black; }
<div id="locations-grid">
<table id="di-locations">
<tr>
<th>plot</th>
<th>address</th>
<th>city</th>
<th>pin</th>
<th>phone</th>
</tr>
</table>
</div>

You can iterate over these location objects and append new grid items containing the location property data as you go. From your question it's a little unclear how exactly the result should look like, but this snippet gives you something to build upon...
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}];
var container = document.getElementById("di-locations");
// iterate locations
for (loc of locations) {
// iterate location properties
for (var prop in loc) {
if (Object.prototype.hasOwnProperty.call(loc, prop)) {
//create and append grid item
var item = document.createElement("DIV");
item.classList.add(loc[prop]);
item.innerHTML = loc[prop];
container.appendChild(item);
}
}
}
#di-locations {
display: grid;
grid-template-columns: auto auto auto auto auto;
font-family: sans-serif;
}
#di-locations>* {
padding: .8rem;
border-bottom: 1px solid #ddd;
}
<div id="locations-grid">
<div id="di-locations"></div>
</div>

This should help, shouldn't be too hard to turn into a grid:
HTML:
<div id="i"></div>
JavaScript:
var locations = [{
plot: "24-17",
address: "XYZ",
city: "Something",
pin: "24399",
phone: "041678993"
}, {
plot: "24-18",
address: "ABC",
city: "Something",
pin: "24398",
phone: "041678995"
}, {
plot: "24-19",
address: "DEF",
city: "Something",
pin: "24397",
phone: "041678994"
}];
txt = "<p>"; // Define txt so it can be accessed inside of functions
locations.forEach(foo); // Run foo for each item in the array
function foo(value,index,array) {
txt = txt + "Plot: " + value["plot"] + "<br>"; // Add Plot: _____ to the end of txt
txt = txt + "Address: " + value["address"] + "<br>"; // Similar to above
txt = txt + "City: " + value["city"] + "<br><br>"; // Similar to above
}
document.getElementById("i").innerHTML = txt + "</p>"; // Set the inner html of i to txt

Related

insertAdjacentHTML not working with created element

I want to display a table after an h1 with an id of pageHeading.
The HTML for the h1 is hardcoded: <h1 id="pageHeading">Table</h1>
const pageHeading = document.querySelector("#pageHeading")
The table is created with Javascript:
const table = document.createElement("table")
table.setAttribute("id", "table")
I tried the following:
document.body.appendChild(table)
This prints the table but after the last HTML element on the page.
Then I tried:
tableHeading.appendChild(table)
This prints the table but INSIDE the h1.
Finally, I tried:
pageHeading.insertAdjacentHTML(
"afterend",
table
)
This doesn't print the table at all. Instead I get (after the h1):
[object HTMLTableElement]
Could this be because I'm using .insertAdjacentHTML on the table contents (see full code below)?
const tableHeaders = [{
titleOne: "Name",
titleTwo: "Age",
titleThree: "Nationality",
}, ]
const persons = [{
name: "James",
age: "23",
nationality: "English",
},
{
name: "Isabella",
age: "21",
nationality: "Italian",
},
{
name: "Hank",
age: "25",
nationality: "American",
},
{
name: "Manon",
age: "27",
nationality: "French",
},
]
const pageHeading = document.querySelector("#pageHeading")
const table = document.createElement("table")
table.setAttribute("id", "table")
/* document.body.appendChild(table) this puts table AFTER the last item in the body <h2>Test</h2> */
/* tableHeading.appendChild(table) this puts table INSIDE <h1 id="tableHeading">Table</h1> */
pageHeading.insertAdjacentHTML(
"afterend",
table
) /* this returns: [object HTMLTableElement] */
const headers = tableHeaders.map(header => {
let ths = `<tr><th>${header.titleOne}</th><th>${header.titleTwo}</th><th>${header.titleThree}</th></tr>`
table.insertAdjacentHTML("afterbegin", ths)
})
const personDetails = persons.map(person => {
let tds = `<tr><td>${person.name}</td><td>${person.age}</td><td>${person.nationality}</td></tr>`
table.insertAdjacentHTML("beforeEnd", tds)
})
<h1 id="pageHeading">Table</h1>
<h2>Test</h2>
Instead of use insertAdjacentHTML you need insertAdjacentElement because is a element not an html string like:
const tableHeaders = [{
titleOne: "Name",
titleTwo: "Age",
titleThree: "Nationality",
}, ]
const persons = [{
name: "James",
age: "23",
nationality: "English",
},
{
name: "Isabella",
age: "21",
nationality: "Italian",
},
{
name: "Hank",
age: "25",
nationality: "American",
},
{
name: "Manon",
age: "27",
nationality: "French",
},
]
const pageHeading = document.querySelector("#pageHeading")
const table = document.createElement("table")
table.setAttribute("id", "table")
/* document.body.appendChild(table) this puts table AFTER the last item in the body <h2>Test</h2> */
/* tableHeading.appendChild(table) this puts table INSIDE <h1 id="tableHeading">Table</h1> */
pageHeading.insertAdjacentElement(
"afterend",
table
) /* this returns: [object HTMLTableElement] */
const headers = tableHeaders.map(header => {
let ths = `<tr><th>${header.titleOne}</th><th>${header.titleTwo}</th><th>${header.titleThree}</th></tr>`
table.insertAdjacentHTML("afterbegin", ths)
})
const personDetails = persons.map(person => {
let tds = `<tr><td>${person.name}</td><td>${person.age}</td><td>${person.nationality}</td></tr>`
table.insertAdjacentHTML("beforeEnd", tds)
})
<h1 id="pageHeading">Table</h1>
<h2>Test</h2>
Reference:
insertAdjacentHTML
insertAdjacentElement

Trying to map elements from object of nested subarrays to a cell in an HTML table, unsure how to reach deepest sub arrays

I have a React/TypeScript component I'm building that features an HTML table for contact details.
I'm trying to map the API response to cells in the table and dynamically populate rows for each contact and their details. There are two arrays for phone numbers and addresses that are nested deep within the object and I can't figure out how to iterate over them along with the rest of the data all in one go.
I initially tried nested for loops but I hit a wall when I got to those two elements because of their position in the data object.
I then tried to use .map() in the middle of the for loops, but I hit a TypeScript error stating the element I'm trying to map over could possibly be null.
I thought about iterating over phone number and address arrays separately and then inserting them into the appropriate cells per contact but I can't figure out how to do when I'm using separate for loops to populate the other cells.
Expected Output:
Name | Member | Telephone | Email | Addresses
Ben B| Friend | 610-535-1234 | ben#gmail.com | 123 Fiction Drive,Denver
215-674-6789 234 Dreary Ln,Seattle
Alice | Family| 267-333-1234 | ally#aim.com | 437 Chance St, Pitts.
I made a CodeSandbox and dropped the current component and example data structure below. For the CodeSandbox it currently loads but as soon as you uncomment these lines you'll see the error
<td>{contacts.contactGroups[i].contacts[j].phoneNumbers}</td>
<td>{contacts.contactGroups[i].contacts[j].addresses}</td>
Current Component
import React from "react";
import { Contacts } from "./contact-types";
type Props = {
contacts: Contacts;
};
export const ContactsGrid = (props: Props) => {
const { contacts } = props;
const rows = [];
for (let i = 0; i < contacts.contactGroups.length; i++) {
rows.push(
<tr>
<td>{contacts.contactGroups[i].contactGroup}</td>
</tr>
);
for (let j = 0; j < contacts.contactGroups[i].contacts.length; j++) {
rows.push(
<tr>
<td>{contacts.contactGroups[i].contacts[j].fullName}</td>
<td>{contacts.contactGroups[i].contacts[j].member}</td>
{/* <td>{contacts.contactGroups[i].contacts[j].phoneNumbers}</td> */}
<td>{contacts.contactGroups[i].contacts[j].email}</td>
{/* <td>{contacts.contactGroups[i].contacts[j].addresses}</td> */}
</tr>
);
}
}
return (
<table>
<thead>
<tr>
<td>Name</td>
<td>Member Type</td>
<td>Telephone</td>
<td>Email</td>
<td>Address</td>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
};
Current Data Structure
export default {
count: 1,
contactGroups: [
{
contactGroup: "Family",
count: 1,
contacts: [
{
member: "Uncle",
fullName: "BENJAMIN BILLIARDS",
lastName: "BILLIARDS",
firstName: "BENJAMIN",
email: "shark#billiards.com",
phoneNumbers: [
{
telephoneNumber: "123-456-7899",
type: "mobile"
},
{
telephoneNumber: "610-555-7625",
type: "work"
}
],
addresses: [
{
addressLine1: "123 FAMILY ST",
addressLine2: "APT 1208",
city: "ATLANTA",
state: "GEORGIA",
zipCode: "12345"
},
{
addressLine1: "456 WORKING BLVD",
addressLine2: "",
city: "ATLANTA",
state: "GEORGIA",
zipCode: "12345"
}
]
}
]
},
{
contactGroup: "Friends",
count: 1,
contacts: [
{
member: "School Friend",
fullName: "HANS ZIMMER",
lastName: "ZIMMER",
firstName: "HANS",
email: "hans#pirates.com",
phoneNumbers: [
{
telephoneNumber: "267-455-1234",
type: "mobile"
}
],
addresses: [
{
addressLine1: "789 FRIEND ST",
addressLine2: "",
city: "SAN DIEGO",
state: "CALIFORNIA",
zipCode: "67890"
},
{
addressLine1: "234 CANARY ST",
addressLine2: "",
city: "SEATTLE",
state: "WASHINGTON",
zipCode: "67890"
}
]
}
]
}
]
};
Use a nested map:
const rows = contacts.contactGroups.map(group => <tr>
<td>{group.contactGroup}</td>
<td>
<table>
{group.contacts.map(contact => <tr>
<td>{contact.fullName}
</tr>}
</table>
</td>
</tr>;

Filter a part of String

I have an array and its data shows on a table. Live Code
Filter by date or by name are working well.
I write some more code to show "No Data Found" if users enter a name, which is not on the list, but somehow it doesn't work.
Is there any way to write a code which will show a result if users enter only last name or first name, that matches last names or first names on the list?
Please give a hand. Thanks!
HTML
<p>From: <input class="datepicker" id="dateFrom" type="text"> To: <input class="datepicker" id="dateTo" type="text"><button class="buttApply">APPLY</button></p>
Search by Name<input type="text" id="searchByName"><button type="button" id="byNamebutton">SEARCH</button><span id="errmsg"></span>
<div class="text"></div>
<table id="myTable" border="1" width="300" cellpadding="5">
</table>
JS
$( ".datepicker" ).datepicker();
var dateList =[
{
name: "Mike Jenson",
email: "mike_j#yesware.com",
phone: "9433550193",
joined: "05/23/2014",
},
{
name: "Jim Stevens",
email: "jim_s#yesware.com",
phone: "1299331944",
joined: "05/22/2014"
},
{
name: "Paul Smith",
email: "paul_s#yesware.com",
phone: "4351289654",
joined: "04/14/2014"
},
{
name: "Sarah Andrews",
email: "sarah_a#yesware.com",
phone: "1299332944",
joined: "03/15/2014"
},
{
name: "Edward O'Brien",
email: "edward_ob#yesware.com",
phone: "4782456897",
joined: "03/27/2014"
},
{
name: "Nicole Plano",
email: "nicole_p#yesware.com",
phone: "6657831564",
joined: "03/30/2013"
},
{
name: "Peter Min",
email: "peter_m#yesware.com",
phone: "8893923938",
joined: "01/07/2013"
},
{
name: "Aaron Jackson",
email: "aaron_j#yesware.com",
phone: "6174896315",
joined: "04/11/2014"
}
];
$('#byNamebutton').click(
function()
{
var Namefilter = dateList.filter(function(NameItem)
{
if(NameItem.name == $('#searchByName').val())
{
return NameItem.name == $('#searchByName').val();
}
else
{
$('#mytable').append('No data found!');
}
});
refreshTable(Namefilter);
}
);
$('.buttApply').click(
function()
{
var filtered = dateList.filter(function(item){
return item.joined >= $('#dateFrom').val() && item.joined <= $('#dateTo').val();
});
refreshTable(filtered);
}
);
function refreshTable(list){
$("#myTable").html("");
for (var i=0; i< list.length; i++)
{
var tr="<tr>";
var td1 = "<td>" + list[i]["name"] + "</td>";
var td2 = "<td>" + list[i]["email"] + "</td>";
var td3 = "<td>" + list[i]["phone"] + "</td>";
var td4 = "<td>" + list[i]["joined"] + "</td></tr>";
$('#myTable').append(tr+td1+td2+td3+td4);
}
}
refreshTable(dateList);
If you want your search by name to work by containing phrase and ignore case sensitive:
return NameItem.name.toLowerCase().indexOf($('#searchByName').val().toLowerCase()) != -1;
As for the no data found, you just need to include this in the end of your reFreshTable function:
if(list.length==0){
$('#myTable').html("No Data Found");
}
JSFiddle: https://jsfiddle.net/juvian/WWscZ/9/
I would also recommend If your data is not very large to change your $('#byNamebutton').click( for a $('#searchByName').keyup( to make it more responsive, as it filters as you type
For #1 add at line 91,
if ($('#myTable').html() == "") {
var tr="<tr>";
var td1 = "<td colspan=4>No Results Found</td></tr>";
$('#myTable').append(tr+td1);
}
This addresses your #2 question:
indexOf returns the position of the string in the other string. If not found, it will return -1:
On line 59, use this instead:
return NameItem.name.indexOf($('#searchByName').val()) > -1;
It will search for partial instead of entire matches. So if you search for "Mike" or "Jensen" it will return "Mike Jensen".

Adding more than one value to a single td in DataTables?

I am trying to add information to Datatables via text input. Normally, you add to DataTables with fnAddData, like so:
$('.home').dataTable().fnAddData([
$("#fName").val(),
$("#lName").val()
]);
but the issue is that I need the values from both of these inputs in the same td, not different tds. Something along the lines of:
$('td:first').text($("#fName").val() + ' ' + $("#lName").val());
...I just don't know how to go about it properly to append to DataTables. You can see that I have the information adding to the table, but this does not append the pagination and isn't correctly adding to DataTables. Any ideas? Thanks in advance.
http://jsfiddle.net/Z85QC/3/
You can join the values of the first and last name as the first array item passed to the fnAddData method. Each array item represents a column in the table.
$('#example').dataTable().fnAddData([
$("#fName").val() + $("#lName").val(),
$("#email").val(),
$("#company").val() + '<br>' + $('#address').val()
]);
See working version here: http://jsfiddle.net/Z85QC/4/
May I recommend a different approach using javascript arrays?
From your sample, the HTML table becomes:
<table cellpadding="0" cellspacing="0" border="0" class="display" id="example"></table>
You need some css rules
td.address
{
white-space: pre
}
And the code:
var thedata = [
{
name: "John Smith",
email: "jsmith#company.com",
address: "Company\n123 Seseme Street\nHouston, TX 77388 USA"
},
{
name: "Jane Doe",
email: "jdoe#company.com",
address: "Company\n123 Seseme Street\nHouston, TX 77388 USA"
}
];
var thetable = $('#example').dataTable({
aaData: thedata,
aoColumns: [
{ sTitle: "Name", mData: "name" },
{ sTitle: "Email", mData: "email" },
{ sTitle: "Address", mData: "address", sClass: "address"}
]
});
$('#addRow').click(function () {
thetable.fnAddData({
name: $("#fName").val() + ' ' + $("#lName").val(),
email: $("#email").val(),
address: $("#company").val() + '\n' + $("#address").val()
});
});
Note that you could create a different object and use a render function for each column mergin some data, but for an answer, I think this is enough.

jquery mobile multipage with javascript template, does not always "refresh"

I have a multi-page jQuery mobile page.
When I go from Page 1 to Page 2 I see my template that I dynamically create using handlebars.
The template:
<script id="history-template" type="text/x-handlebars-template">
<div data-role="collapsible" id="share_history" >
<h3>{{share_title}}</h3>
{{#each historyItem}}
<h2>Shared with {{shared_with}}</h2>
{{#list people}}{{firstName}} {{lastName}}, {{role}}{{/list}}
{{/each}}
</div>
</script>
The javascript:
var context = {
share_title: "View Share History",
historyItem: [
{
shared_with: "with a group",
people: [
{firstName: "Bob", lastName: "Wong", role: "Dad" },
{firstName: "Tina", lastName: "Turner", role: "Guardian" },
{firstName: "Modest", lastName: "Mouse", role: "Dad" }
]
},
{
shared_with: "with 3 people",
people: [
{firstName: "Baily", lastName: "Wong", role: "Dad" },
{firstName: "Gina", lastName: "Turner", role: "Guardian" },
{firstName: "Modest", lastName: "Mouse", role: "Dad" }
]
}
]
};
var source = $("#history-template").html();
var template = Handlebars.compile(source);
Handlebars.registerHelper('list', function(people, options) {
var out = "<ul class=>";
for(var i=0, l=people.length; i<l; i++) {
out = out + "<li>" + options.fn(people[i]) + "</li>";
}
return out + "</ul>";
});
var html = template(context);
$('#share').html(html);
$.mobile.changePage('#add-edit');
When I go from Page 1 to Page 2 (in my multipage layout) it works (good).
But if I click the back button, and then go back to Page 2, I see my content...minus the additional markup jQuery mobile adds (i.e. I see content but not my jQuery mobile appearance/theme).
Edit
For my example, I had to do the following:
$('#share').html(html).trigger( "create" );
You will need to trigger the create event on the html element, e.g
el.trigger('create');

Categories