How to get UL and LI value dynamically in jquery - javascript

I have a simple ul and li tags which contains state and capitals, here I need to get all the data from json dynamically and to append into main div of class "details",Here I just hardcoded but actually it should come from json given below in the same way.Here is the code below
HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="details">
<ul>
<li>state</li>
<li>
<ul>state1</ul>
<ul>state2</ul>
</li>
<li>capital</li>
<li>
<ul>capital1</ul>
<ul>capital2</ul>
</li>
</ul>
</div>
jquery
$(document).ready(function () {
$.ajax({
type: "GET",
url: "sample.json",
success: function(result)
{
console.log(result);
}
});
});
json
{
"state": [{
"name": "state1"
},
{
"name": "state2"
}
],
"capital": [{
"name": "capital1"
},
{
"name": "capital2"
}
]
}

Word of warning: your proposed markup isn't valid (Error: Text not allowed in element ul in this context.). Even so, it sounds like it's the appropriate output for this task.
I'll assume your Ajax request is working and returns a valid JSON structure as the result variable.
Having handled set up, here's the code to generate your structure:
const result = {
"state": [
{"name": "state1"},
{"name": "state2"}
],
"capital": [
{"name": "capital1"},
{"name": "capital2"}
]
};
const root = document.createElement("ul");
document.querySelector(".details").appendChild(root);
for (const key in result) {
const title = document.createElement("li");
title.innerText = key;
root.appendChild(title);
const list = document.createElement("li");
root.appendChild(list);
for (const o of result[key]) {
const ul = document.createElement("ul");
ul.innerText = o.name;
list.appendChild(ul);
}
}
<div class="details"></div>

Related

Filter through checkboxes that were pulled from database and match results associated with the checkboxes

My project is a project idea generator page where a user is given a list of coding languages. I am using handlebars and javascript to render the page. Whichever languages they select, a list of projects that use those languages should show up on the page. I used sequelize and seeds to connect the language database and project database using a many to many relationship.
When I call on the database in my javascript, I am shown a list of languages and their associated project names.
I have no idea how to create this filtering checkbox system using the json data. This is what I have so far:
// This is the data from my model seed. I am just including it here for reference:
[
{
"id": 1,
"language_name": "HTML/CSS",
"project_id": null,
"projects": [
{
"project_name": "Job Search Board",
"id": 1
},
{
"project_name": "Blank Project",
"id": 2
},
{
"project_name": "Project 3",
"id": 3
},
{
"project_name": "Example Project 4",
"id": 4
}
]
},
{
"id": 2,
"language_name": "Javascript",
"project_id": null,
"projects": [
{
"project_name": "Job Search Board",
"id": 1
},
{
"project_name": "Project 3",
"id": 3
},
{
"project_name": "Example Project 4",
"id": 4
}
]
},
{
"id": 3,
"language_name": "Express",
"project_id": null,
"projects": [
{
"project_name": "Project 3",
"id": 3
}
]
},
{
"id": 4,
"language_name": "React",
"project_id": null,
"projects": []
}
]
const filt=document.getElementById("filt"),
list=document.querySelector("#list tbody");
let cols={}, trs; // arrays for columns sequence in data and all table records
// set up filtering (needs be done only once, before the AJAX call):
filt.addEventListener("change",ev=>{let cb=ev.target;
let tst=Object.entries([...filt.querySelectorAll(":checked")].reduce((a,c)=>((a[c.name]=a[c.name]||[]).push(c.value),a), {}));
trs.forEach(tr=>
tr.style.display=tst.length==0 || tst.every(([n,arr])=>arr.includes(tr.children[cols[n]].textContent))
?"":"none"
)
});
$.ajax({
url: "/api/languages",
method: "GET",
dataType: 'json',
data: 'projects',
success: function(data) {
console.log(data);
if (data.length) { // only if data array is not empty
// columns sequence:
Object.keys(data[0]).forEach((c,i)=>cols[c]=i);
// build the filter-menu structure:
const menu={language_name:{}};
data.forEach(f=>Object.entries(menu).forEach(([k,o])=>o[f[k]]=1));
// translate the filter-menu structure into HTML:
filt.innerHTML=Object.entries(menu).map(([k,o])=>k+':<br>'+
Object.keys(o).map(c=>'<label><input type="checkbox" value="'+c+'" name="'+k+'">'+c+'</label>').join('<br>')
).join('<br>\n');
// fill the main project list with data entries:
list.innerHTML=data.map(f=>'<tr><td>'+Object.values(f).join('</td><td>')+'</td></tr>').join('\n');
trs=[...list.children];
}
},
error: function() {
console.log(data);
}
});
<div id="filt"></div>
<div id="list"><table><thead><tr><th>Project</th></thead>
<tbody></tbody></table</tdiv>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="/js/projecttwo.js"></script>
You can fix the filtering problem by creating two functions to build the GUI. The first function showFilters() would build the list of languages which does not change. The second function showProjects() would build the list of projects. Then you just need to modify your change event handler to filter and update the projects section.
You can build a simple filter by getting a list of all the checked inputs and concatenating their values. Then you only need to use String.includes to filter the array using the lanuage_name field.
The filter
filt.addEventListener("change", ev => {
let keywords = [...filt.querySelectorAll("input[type=checkbox]:checked")].map(p => p.value).join(",")
let subset = data.filter(item => keywords.includes(item.language_name));
showProjects(subset);
});
keep in mind that there are many ways to build a filter and this is the most simple
HTML Templates
Though it wasn't part of the question, the html isn't being generated correctly. This is why you see [object object] in the display. You may want to learn about JavaScript template literals which can make this job easier.
Update
The snippet was updated to display the projects in a different format per OP's comments.
Snippet
Review and run the code snippet to understand how it works.
const data = [{"id":1,"language_name":"HTML/CSS","project_id":null,"projects":[{"project_name":"Job Search Board","id":1},{"project_name":"Blank Project","id":2},{"project_name":"Project 3","id":3},{"project_name":"Example Project 4","id":4}]},{"id":2,"language_name":"Javascript","project_id":null,"projects":[{"project_name":"Job Search Board","id":1},{"project_name":"Project 3","id":3},{"project_name":"Example Project 4","id":4}]},{"id":3,"language_name":"Express","project_id":null,"projects":[{"project_name":"Project 3","id":3}]},{"id":4,"language_name":"React","project_id":null,"projects":[]}];
const filt = document.getElementById("filt"),
list = document.querySelector("#list tbody");
let cols = {},
trs;
function showFilter(data) {
if (!data.length) return;
Object.keys(data[0]).forEach((c, i) => cols[c] = i);
const menu = {
language_name: {}
};
data.forEach(f => Object.entries(menu).forEach(([k, o]) => o[f[k]] = 1));
filt.innerHTML = Object.entries(menu).map(([k, o]) => k + ':<br>' +
Object.keys(o).map(c =>
'<label><input type="checkbox" value="' + c + '" name="' + k + '">' + c + '</label>').join(''));
showProjects(data);
}
function showProjects(data) {
// create an array of all the projects
let projects = data.reduce((a,b) => a.concat(b.projects), [])
// sort the array by project id
.sort((a,b) => a.id - b.id)
// remove duplicates projects by comparing current and previous id
.filter((item, index, array) => !index || item.id !== array[index-1].id);
// update table
list.innerHTML = projects.length > 0
? projects.map(item => `<tr><td>${item.id}</td><td>${item.project_name}</td></tr>`).join("\n")
: `<tr><td colspan="10">No matching projects found</td</tr>`;
trs = [...list.children];
}
filt.addEventListener("change", ev => {
let keywords = [...filt.querySelectorAll("input[type=checkbox]:checked")].map(p => p.value).join(",")
let subset = data.filter(item => keywords.includes(item.language_name));
showProjects(subset);
});
showFilter(data);
body {
font-family: sans-serif;
}
label {
margin-right: 2rem;
}
table {
margin-top: 1em;
border-collapse: collapse;
}
table td {
padding: 0.2rem;
border: 1px solid lightgray;
}
table th {
background-color: steelblue;
color: white;
}
<div id="filt"></div>
<div id="list">
<table>
<thead>
<tr><th>ID</th><th>Projects</th></tr>
</thead>
<tbody></tbody>
</table>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

for json Element id = "1" , append ul 'chapter_count' times

I am fairly new to web dev and This is my first question on StackOverflow. Apologies if I didn't frame it properly.
Here is my json.
{
"books":
[
{
"_id": "1",
"book_cat": "OLD",
"book_eng_name": "Book1",
"chapter_count": "50"
},
{
"_id": "2",
"book_cat": "OLD",
"book_eng_name": "Book2",
"chapter_count": "40""
}
]
I want to use this json with jquery to append to . First I need to filter __id and then append a statement to the <ul> "Chapter_count" times.
For instance, if I select I am searching for _id 1 I want 50 item list that says
Chapter 1
Chapter 2
...
...
...
Chapter 50
I want to append to ul in the following html:
<html>
<title>Chapters</title>
<body>
<ul></ul>
<script type="text/javascript"
src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/
jquery.min.js"></script>
<script type = "text/javascript" src='files/js/Chapter_script.js'></script>
<script type = "text/javascript" src='files/js/books.json'></script>
</body>
</html>
I wrote something like this but didn't work :(
(document).ready(function(){
var search_id = window.location.search;
var chapter_no = search_id.match(/\d+/g);
$.getJSON("files/js/books.json",function(data){
$.each(data.books, function(){
if (this['_id'] == chapter_no ){
for(var i = 0; i <= this['chapter_count']; i++; ){
$("ul").append("<li>Chapter Number"+i+"</li></br>")
}
};
})
});
});
Where chapter_no is extracted from url with window.location.search
Sample url: http://localhost:90/chapters.html?book_id=1
Thanks in advance.
Copy the below code in $.getJSON("files/js/books.json",function(data) function.
var selectedElement = $.grep(data.books, function( element, index ) {
if(chapter_no == element._id)
return element
});
for(var i =0 ; i<=selectedElement[0].chapter_count;i++)
{
$("ul").append("<li>Chapter Number"+i+"</li></br>")
}
first your json is not valid and have syntax error i think the true one is
json ={ "books": [ { "_id": "1", "book_cat": "OLD", "book_eng_name": "Book1",
"chapter_count": "50" }, { "_id": "2", "book_cat": "OLD",
"book_eng_name": "Book2", "chapter_count": "40" } ]}
its sample js code
json.books.forEach(function (b){
if(b._identer code here == "1")
{
for(var i =0 ; i<=b.caphter_count;i++)
{
//put your code here
}
}
})

Displaying related content UNDER the CURRENT li in Angular

I currently have a repeated list repeating category names from my JSON data. Each li is it's own repeated list of people related to that category (see CodePen) What I want to do is open a div under the current nested li displaying data regarding the individual clicked. My demo has it working except it opens under EACH li instead of the current one. I know there is probably a solution similar to the select(i) i have in the demo but can't quite get it to work. Any help would be appreciated.
My HMTL:
<div data-ng-app="app" data-ng-controller="starWarsCtrl">
<ul>
<li data-ng-repeat="cat in getCategories()">
<h2>{{cat}}</h2>
<div ng-click="select(i)" ng-repeat="i in data | filter:{cat: cat}">
<p>{{i.name}}</p>
</div>
</li>
</ul>
</div>
My AngularJS:
angular.module('app', ['ngAnimate'])
.controller('starWarsCtrl', function ($scope) {
$scope.data = [
{"name": "Obi-Wan Kenobi",
"index":88,
"cat": "jedi"},
{"name": "Yoda",
"index":69,
"cat":"jedi"},
{"name": "Lando",
"index":31,
"cat": "smuggler"},
{"name": "Han Solo",
"index":90,
"cat": "smuggler"},
{"name": "Darth Vader",
"index":98,
"cat": "sith"},
{"name": "Jar-Jar Binks",
"index":80,
"cat": "alien"},
{"name": "Mace Windu",
"index":45,
"cat": "jedi"},
{"name": "Chewy",
"index":76,
"cat": "smuggler"}
];
$scope.select = function (item) {
$scope.selectedItem = item;
}
$scope.getCategories = function() {
var categories = [];
angular.forEach($scope.data, function(item) {
//item.cat is a string
if (categories.indexOf(item.cat) == -1) {
categories.push(item.cat);
}
});
return categories;
}
})
The quick answer is adding
&& cat === selectedItem.cat
to your data-ng-if. This checks whether the cat & selectedItem are matching appropriately.

Mapping and binding nested objects and arrays

I have an object and within this object I have items and one of the items is an array which also contains objects. A sample of the data is shown below.
I am using knockout to bind this data to the view so I think I need to implement a double loop for returning the objects and the objects within the child array to be able to bind them in the view.
Sample data:
"singers": {
"ijiyt6ih": {
"id": ObjectId('ijiyt6ih'),
"name": "John",
"songs": [
{
"id": ObjectId('okoiu8yi'),
"songName": "Hello There",
"year": "1980"
},
{
"id": ObjectId('sewfd323'),
"songName": "No More",
"year": "1983"
}
]
},
"98usd96w": {
"id": ObjectId('98usd96w'),
"name": "Jack",
"songs": [
{
"id": ObjectId('iew342o3'),
"songName": "Hurry Up",
"year": "1985"
}
]
}
}
I need to find a way to appropriately loop through this so that I can modify the returned data to bind it to the viewModel using knockout.
Here is how my viewModel looks like:
singersViewModel = function(data) {
var self = {
singerId: ko.observable(data.id),
singerName: ko.observable(data.name),
songName: ko.observable(...),
songYear: ko.observable(...)
};
I am not sure if I will have to return two different sets of data or not.
As for the looping. I was able to loop and return the list of singers to display on the page but I am not able to get the list of songs displayed within each singer.
Here is my loop so far:
var self = {},
singer,
tempSingers = [];
self.singers = ko.observableArray([]);
for (singer in singers) {
if (singers.hasOwnProperty(singer)) {
tempSingers.push(new singersViewModel(singers[singer]));
}
}
self.singers(tempSingers);
I tried to duplicate the same type of loop for songs within this loop but i would get an error using hasOwnProperty because songs is an array.
In the included snippet you can see how you can map the original data to a viewmodel that can be bound to a view.
I've left the ids as regular properties, and converted the names into observables, so thatthey can be edited. At the bottom you can see the current viewmodel state.
There is also a sample view which iterates the list of singers, and also the list of song within each singer.
As you can see I'm implementing the solution using mapping. For mapping you need to implement a callback that receives each original object and returns a new one with a new structure. For example this part of the code
_.map(_singers, function(singer) {
return {
id: singer.id,
name: ko.observable(singer.name),
// ... songs:
})
iterates over each singer (the sample data in the question), and for each one creates a new object with the id, an observable which includes the name (and the mapping of songs, which I don't show in this fragment).
NOTE: I'm using lodash, but many browsers support map natively as an array function
var ObjectId = function (id) { return id; }
var singers = {
"ijiyt6ih": {
"id": ObjectId('ijiyt6ih'),
"name": "John",
"songs": [
{
"id": ObjectId('okoiu8yi'),
"songName": "Hello There",
"year": "1980"
},
{
"id": ObjectId('sewfd323'),
"songName": "No More",
"year": "1983"
}
]
},
"98usd96w": {
"id": ObjectId('98usd96w'),
"name": "Jack",
"songs": [
{
"id": ObjectId('iew342o3'),
"songName": "Hurry Up",
"year": "1985"
}
]
}
};
var SingersVm = function(_singers) {
var self = this;
self.singers = _.map(_singers, function(singer) {
return {
id: singer.id,
name: ko.observable(singer.name),
songs: _.map(singer.songs, function(song) {
return {
name: ko.observable(song.songName),
id: song.id
};
})
};
});
return self;
};
var vm = new SingersVm(singers);
//console.log(vm);
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-bind="foreach: singers">
<div>
<input data-bind="value: name"/> (<span data-bind="text: id"></span>)
<ul data-bind="foreach:songs">
<li>
<input data-bind="value: name"/> (<span data-bind="text: id"></span>)
</li>
</ul>
</div>
</div>
<pre data-bind="html: ko.toJSON($root,null,2)">
</pre>

how to use for each with mustache javascript?

i have some json objects and some of them have some other objects inside them.
if i leave only the json obj that don't have other obj inside them and then apply the template, everything goes well, i get, in this case 3 li elements.
but if i grab the original json obj the results are a bit wired. I believe i need to do a each statement to iterate through each sub json obj from inside each main one
maybe i am a bit confuse so here is some code.
i have some json data like this:
{
"msg_id":"134",
"message":"Nick",
"comment":[
{
"com_id":"9",
"comment":"test",
},
{
"com_id":"10",
"comment":"testtt",
},
{
"com_id":"11",
"comment":"testtttt",
}]
},
{
"msg_id":"134",
"message":"Nick",
},
{
"msg_id":"134",
"message":"Nick",
}
and i am trying to arive at something like this:
Nick
test
testtt
testtttt
Nick
Nick
i've created a template like this:
function messagesTamplate(data)
{
$.each(data, function(index, obj)
{
msg += template.replace( /{{message}}/ig , obj.message );
if(obj.comment) {
$.each(obj.comment, function(key, val)
{
msg += template.replace( /{{comment}}/ig , val.comment );
});
}
});
return msg;
}
then i just append this to the main ul.
thanks
data needs to be an array (see the enclosing [])
var data = [{
"msg_id": "134",
"message": "Nick",
"comment": [{
"com_id": "9",
"comment": "test",
}, {
"com_id": "10",
"comment": "testtt",
}, {
"com_id": "11",
"comment": "testtttt",
}]
}, {
"msg_id": "134",
"message": "Nick",
}, {
"msg_id": "134",
"message": "Nick",
}]
is just this in mustache templates:
{{#data}} //loop through all data
{{message}} //pick out the "message" per iteration
{{#comment}} //loop through all comments in an iterated item
{{comment}} //pick out the comment
{{/comment}}
{{/data}}

Categories

Resources