javascript, nested navbar from json - javascript

My aim is to replicate this structure automatically from a json file.
<ul class="sidebar-menu">
<li class="treeview">
Mammals
<ul class="treeview-menu">
<li>Goat</li>
<li> Sheep
<ul class="treeview-menu">
<li>Bone</li>
<li>Bone
<ul class="treeview-menu">
<li>Variant 1</li>
<li> Variant 2</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
JSON
[
{"datasetID":"5fd4124900827","higherClassification":"Eukarya","kingdom":"Animalia","phylum":"Chordata","class":"Mammalia","order":"Artiodactyla","family":"Bovidae","genus":"Capra","subgenus":"None","vernacularName":"goat","commonName":"None","elementName":"Calcaneus","commonElementName":"None"},
{"datasetID":"5fd4058e5c8d2","higherClassification":"Eukarya","kingdom":"Animalia","phylum":"Chordata","class":"Mammalia","order":"Artiodactyla","family":"Bovidae","genus":"Capra","subgenus":"None","vernacularName":"goat","commonName":"goat","elementName":"Femur","commonElementName":"None"}
]
The relevant parts are:
"datasetID":"5fd4124900827"
"class":"Mammalia",
"order":"Artiodactyla",
"family":"Bovidae",
"genus":"Capra",
"subgenus":"None",
"vernacularName":"goat",
"elementName":"Calcaneus"},
So the class is on the top level of the hierarchy, it could be mammal, bird, fish...
Taking class: Mammalia as an example, under this is order under that family under that genus
then if there is a subgenus that is on the next level also.
Under that is the vernacularName then elementName.
Each record has a unique id datasetID there may be multiple "elementName": "Calcaneus" for a goat, these need an integer added (i.e. Calcaneus 1, then Calcaneus 2, then Calcaneus 3 etc.
>Mammalia
>order
>family
>genus
>subgenus (if exists)
>vernacularName
>elementName (if more than one append 1,2,3...)
So, my mega question, how to do this in javascript?
My attempt so far:
Php gets the json, yes this could be done in javascript.
<?php
$data = json_decode(file_get_contents("bonify" . $version . "/app/json/data.json"), True);
?>
Javascript picks up the json:
<script type="text/javascript">
const version = "<?php echo $version; ?>";
$.getJSON('bonify'+ version +'/app/json/data2.json', function(json) {
console.log(json); // this will show the info it in firebug console
obj = json
This lists all the json data:
function printValues(obj) {
for(var k in obj) {
if(obj[k] instanceof Object) {
printValues(obj[k]);
} else {
document.write(obj[k] + "<br>");
};
}
};
closing code:
});
</script>
I'm not convinced document.write is the best way to do this.
I have this code for my search and it seems like I should adapt that but with out the filter capability.
$('#txt-search').keyup(function(){
var searchField = $(this).val();
if(searchField === '') {
$('#filter-records').html('');
return;
}
var regex = new RegExp(searchField, "i");
var output = '<div class="col-12 p-0"><hr />';
var count = 1;
$.each(data, function(key, val){
if ((val.datasetID.search(regex) != -1) || (val.ownerInstitutionCode.search(regex) != -1)|| (val.vernacularName.search(regex) != -1)|| (val.elementName.search(regex) != -1)) {
output += '<ul class="sidebar-menu">';
output += '<li><i class="fas fa-bone" data-fa-transform="rotate-45"></i> <span>' + val.vernacularName + ': ' + val.elementName + '</span></li>';
output += '</ul>';
if(count%2 == 0){
output += '</div>'
}
count++;
}
});
$('#filter-records').html(output);
});
});
});
I'm assuming several nested foreach loops is the way to go? I've put the whole aim for clarity. I am trying to learn and I have a learning disability so please be patient with me, thanks for your help. I've tried to included as much info as possible to avoid having my question closed as too broad.

You have a repeated pattern. If we assume that you have built a hierarchical data structure, then we can use a function using template literals, like:
function buildChildren(children) {
var tvms = [];
for (let child of children) {
tvms.push(myTreeViewMenu(child));
}
return tvms;
}
function myTreeViewMenu(treeViewMenu) {
tvms = buildChildren(treeViewMenu.children);
return `
<ul class="treeview-menu">
<li>${treeViewMenu.name} ${tvms.join("")}</li>
</ul>
`;
}
function myTree(tree) {
tvms = buildChildren(tree.children);
return `
<ul class="sidebar-menu">
<li class="treeview">
${tree.name}
${tvms.join("")}
</li>
</ul>
`;
}
(NOT TESTED)
This logic can be a starting point for you, basically you nest your pattern into itself. You need to make sure that from your raw JSON you build an object tree whose nodes have a string called name and an array for the subtree called children. Also, make sure there are no cycles in the tree.

Related

how to generate different html if the given object has no results

I am talking to WP REST API and if I do have results in data object I generate HTML code to frontend, and that is working fine, but what to do, and how to generate different HTML output (for example "There is no search result") if there is not data in data object.
I try with if statement but no luck with that. I am new to coding so forgive me if this is trivial problem...
var searchVal = this.inputField.val();
$.getJSON('http://localhost:8888/wptest/wp-json/wp/v2/posts?search=' + searchVal,
data => {
this.createHtml.html(`
<h2 class="search-overlay__section-title">Posts Info</h2>
<ul class="link-list min-list">
<li>${data[0].title.rendered}</li>
</ul>
`);
});
Your code should work if its as below:
var searchVal = this.inputField.val();
$.getJSON('http://localhost:8888/wptest/wp-json/wp/v2/posts?search=' + searchVal,
data => {
if (data.length == 0) {
// no results html
} else {
this.createHtml.html(`
<h2 class="search-overlay__section-title">Posts Info</h2>
<ul class="link-list min-list">
<li>${data[0].title.rendered}</li>
</ul>
`);
}
});
Hope this helps !!
Hello guys I did a little research and found a more concise solution, Ternary Operator... sorry if I didn't say it was ES6 syntax and I used a compiler to run regular javascript in the browser
var searchVal = this.inputField.val();
$.getJSON('http://localhost:8888/wptest/wp-json/wp/v2/posts?search=' + searchVal,
data => {
this.createHtml.html(`
<h2 class="search-overlay__section-title">Posts Info</h2>
${data.length == 0 ? '<p>No Results</p>' : '<ul class="link-list min-list">'}
${data.map(item => `<li>${item.title.rendered}</li>`).join('')}
${data.length == 0 ? '</ul>' : ''}
`);
});

How to Import a Text File to a List in HTML5 / JavaScript even if it is not JSON or CSV?

I am new here, please be kind. I am trying to import a text file into a HTML nested list. The HTML output I am trying to achieve would look like this:
<ul>
<li>FAVORITE SITES
<ul>
<li>eBay</li>
<li>Google</li>
</ul>
</li>
<li>AVOIDED SITES
<ul>
<li>Yahoo</li>
<li>CraigsList</li>
</ul>
</li>
<li>OTHER SITES
<ul>
<li>Alexa</li>
<li>Amazon</li>
<li>Jet</li>
</ul>
</li>
</ul>
The text file I am loading using <input type="file"> is not formatted in a usual way - it is not JSON or CSV, it is formatted like below:
FAVORITE SITES {
!Variable=eBay
!Variable=Google
}
AVOIDED SITES {
!Variable=Yahoo
!Variable=CraigsList
}
OTHER SITES {
!Variable=Alexa
!Variable=Amazon
!Variable=Jet
}
Is there any way to load parse and input the values of the text file and load them into the list even though the file is in this non-standard format or will I need to change the format of the text file? Any help will be appreciated, thank you.
let s = `FAVORITE SITES {
!Variable=eBay
!Variable=Google
}
AVOIDED SITES {
!Variable=Yahoo
!Variable=CraigsList
}
OTHER SITES {
!Variable=Alexa
!Variable=Amazon
!Variable=Jet
}`;
var ul = document.createElement('ul');
ul.innerHTML = s.replace(/(\})/gim, "</ul>\n</li>")
.replace(/\{/gim, "\n<ul>")
.replace(/!Variable=(\w+)\s*\n/gim, "<li>$1</li>\n")
.split(/\s*\n\s*\n/)
.map(v => v = '\n<li>' + v)
.join('');
document.body.appendChild(ul);
Please reference this code snippet;
// assign the file content to s
let s = `FAVORITE SITES {
!Variable=eBay
!Variable=Google
}
AVOIDED SITES {
!Variable=Yahoo
!Variable=CraigsList
}
OTHER SITES {
!Variable=Alexa
!Variable=Amazon
!Variable=Jet
}`;
let a = s.replace(/[\n\r]/g, '').split(/(.+?) \{(.+?)\}/g);
for (let i = 0; i < a.length - 1; i += 3) {
// a[i + 1] is the outer li text
let a2 = a[i + 2].split(/!Variable=([^!]+)/g);
// every 2nd item of a2 is the inner li text
// or you can filter out '' items
// you can apply the same trick to a as well, but need to modify for-loop accordingly
let a3 = a2.filter(v => v !== '');
// now all items are a3 can be used directly as inner li text
}

Serialize from ul li with child using jqueryui sortable

I would like to know how to create a JSON or serialize (both is fine) from a ul including
<ul class="menu send ui-sortable">
<li id="pageid_1" class="ui-sortable-handle">Inscription
<ul class="menu send ui-sortable">
<li id="pageid_2" class="ui-sortable-handle">Joueurs en ligne
<ul class="menu send ui-sortable"></ul>
</li>
</ul>
</li>
I cannot find how to create something like this:
pageid[]=1&pageid[1]=2 OR [{"pageid":1,"children":[{"pageid":2}]}]
Meaning, including parent ID in [].
Thank you for your help!
This code will produce the output required
var result = [].map.call(document.querySelectorAll('ul.menu.send.ui-sortable li.ui-sortable-handle'), function(li) {
var parent = '';
if (li.parentNode && li.parentNode.parentNode && li.parentNode.parentNode.nodeName == 'LI' && li.parentNode.parentNode.matches('li.ui-sortable-handle')) {
parent = li.parentNode.parentNode.id.replace(/\D+/g, '');
}
return "pageid[" + parent + "]=" + li.id.replace(/\D+/g, '');
}).join('&');
console.log(result); // pageid[]=1&pageid[1]=2
I haven't thought about how to do the second format, because the first format is easier to produce

Framework7: Delete items of an array with swipeout

I tried app developing with Framework7.
I print my array (list) in this way:
if (list != null){
for (var i=0; i<list.length; i++){
output = output + '<li class="swipeout"><div class="item-content swipeout-content"><div class="item-inner"><div class="item-title-row"><div class="item-title">' + list[i].name + '</div></div><div class="item-subtitle">' + new Date(list[i].fDate).toLocaleDateString() + '</div></div></div><div class="swipeout-actions-right">Delete</div></li>';
}
}
$$('#liste').html(output);
When I swipeout an entry, the entry will disappear but he is still in the array.
This is to handle the remove-event:
$$('.swipeout').on('deleted', function () {
myApp.alert('Item removed');
});
How can I get the index of the element to remove it also from the array?
Alternatively, is there an other way to solve this problem?
Thank you!
Markus
If I were you, I'd rather use Framework7's view engine to render the swipeout items and take advantage of the #index helper. Click here for further information.
In your markup, you'd have something similiar to this:
<div class="list-block">
<ul>
{{#each item in list}}
<li class="swipeout">
<!-- Usual list element wrapped with "swipeout-content" -->
<div class="swipeout-content">
<!-- Your list element here -->
<div class="item-content">
<div class="item-media">...</div>
<div class="item-inner">...</div>
</div>
</div>
<!-- Swipeout actions right -->
<div class="swipeout-actions-right">
<!-- Swipeout actions links/buttons -->
<a href="#" data-index={{#index}}>Action 1</a>
<a class="swipeout-close" href="#" data-index={{#index}}>Action 2</a>
</div>
</li>
{{/each}}
Notice that I'm using the "each" helper along with "#index" to render the items and put an attribute on them with the id. But you can still achieve the same objective by using the "i" variable inside the for loop:
if (list != null){
for (var i=0; i<list.length; i++){
output = output + '<li class="swipeout"><div class="item-content swipeout-content"><div class="item-inner"><div class="item-title-row"><div class="item-title">' + list[i].name + '</div></div><div class="item-subtitle">' + new Date(list[i].fDate).toLocaleDateString() + '</div></div></div><div class="swipeout-actions-right"><a href="#" class="swipeout-delete" data-index='+i+'>Delete</a></div></li>';
}
}
When the event is fired:
$$('.swipeout').on('deleted', function () {
var $thisAction = $(this);
// Here you delete the item
delete list[$thisAction.data('index')];
myApp.alert('Item removed');
});
In this case you can't use Array.slice because if you delete item 2 from the markup, the element with the index 3 will replace item 2. The problem with the above approach is that you have to take care of the "holes" in your array. A much better approach would be to use a two-way binding framework, such as VueJs.

How do I reference a dynamic variable inside angular expression braces?

I've looked high and low for an answer to this, but nothing that directly addresses this. The issue is this -- I have a function that gets a list of calendars from an endpoint. I have another function that counts the number of events for each calendar. I have it creating a dynamic variable every time it's called in the ng-repeat list, attaching the calendar ID to the word "count" as the variable name.
function getEventsCount(calendarId) {
if(calendarId !== '') {
calendarService.getCalendarEvents(calendarId, fromDate, toDate).then(function (result) {
if(isSuccessResponse(result)) {
$scope['count' + calendarId] = result.events.length;
} else {
$scope.errorMessage = errorText + result.errorMessage;
}
});
}
}
In the HTML I want to display the numerical count of events for each calendar, but since we're iterating through an ng-repeat list which will return any given number of calendar IDs, I don't know what the variable name for count will be, so I need angular to somehow parse the variable name inside of those braces for the value.
<ul class="list-group">
<li class="list-group-item" data-ng-repeat="calendar in calendarList.calendars" data-ng-init="getEventsCount(calendar.id)">
<a roll="button" class="btn-link" data-ng-click="showCalendar(calendar.id, calendar.summary)">{{calendar.summary}} ({{count + calendar.id}})</a>
</li>
</ul>
Forgive me if the question is muddy, I'll be happy to clarify if needed.
What about just putting the counts inside an object on the scope, keyed by their id? Then you can index into the object in the expression using the id:
$scope.counts = {};
function getEventsCount(calendarId) {
if(calendarId !== '') {
calendarService.getCalendarEvents(calendarId, fromDate, toDate).then(function (result) {
if(isSuccessResponse(result)) {
$scope.counts[calendarId] = result.events.length;
} else {
$scope.errorMessage = errorText + result.errorMessage;
}
});
}
}
Template:
<ul class="list-group">
<li class="list-group-item" data-ng-repeat="calendar in calendarList.calendars" data-ng-init="getEventsCount(calendar.id)">
<a roll="button" class="btn-link" data-ng-click="showCalendar(calendar.id, calendar.summary)">{{calendar.summary}} ({{counts[calendar.id]}})</a>
</li>
</ul>

Categories

Resources