Menu
I have noticed that the “hen and egg” problem will occur now and then using Javascript. If I load a stored menu by fetch, I often cannot use this menu until it is “finished”. And **async
My intention is to fetch a menu from database and then serve it **dynamically
Is it possible to preload data ONCE and use this stored data in a predictable way?
then
</C>, <C>
async
</C> and <C>
await
</C> are not necessary and, the other way round, when using <C>
async
</C> and <C>
await
</C>, <C>
then
</C> is not necessary.
The main thing that has been adhered to is, that the response from the server is available only in the <C>
then
</C> callback or inside the <C>
async
</C> function.<br/>
That's the reason why your code is not working reliably.<br/>
This should:
<CODE>
`<i>
</i>fetch(url)
.then(response => response.json())
.then(data => {
localStorage.setItem('main', data);
// do anything you like with data here
});
// In this place data being stored in local storage
// will not be available reliably<i>
</i>
``>@Sempervivum#1649027 Ajax is predictable when handled properly.
>// In this place data being stored in local storage
// will not be available reliably
>@Sempervivum#1649033 // In this place data being stored in local storage
> // will not be available reliably
then
`>@Sempervivum#1649035 You will have to perform any action that references the response from the server inside the then callback. When doing so, the data will be available reliably
then
``<i>
</i>function get_menu() {
let url = "https://api3.go4webdev.org/main/std";
fetch(url)
.then(response => response.json())
.then(data => store(data));
}
get_menu()
function store(menu) {
localStorage.setItem('main', data)
}<i>
</i>
``
then
</C> callback, it will not be available reliably.
One option is storing the promise in a variable, this will be available anywhere in your code but will require <C>
then
```<i>
</i> function fetchIt() {
return fetch('thread1042-promise-sibert.php')
.then(response => response.text());
}
prom = fetchIt();
// The variable prom is containing a promise now:
console.log(prom);
// You can place the following code anywhere
// but will need "then" in order to access the data:
prom.then(txt => {
console.log(txt);
// Perform any actions with txt here
});<i>
</i>
``
>@Sempervivum#1649039 One option is storing the promise in a variable, this will be available anywhere in your code but will require then to access the data.
``<i>
</i>if (window.location.href.split("/").pop() === "") {
window.location.href = "home"
}
const load_menu = async () => {
try {
const response = await fetch('https://api3.go4webdev.org/menu/all')
const data = await response.json()
sessionStorage.setItem('main', JSON.stringify(data));
} catch (err) {
console.error(err)
}
}
const get_menu = async () => {
if (sessionStorage.main == null) {
await load_menu()
}
let data = JSON.parse(sessionStorage.main)
let main = document.body.dataset.main
fillmain(data.filter(item => item.menu_main == ['min']))
fillsub(data.filter(item => item.menu_main == [main]))
}
get_menu()
//let fragment = new DocumentFragment() <---does not work
//create menus from database.
function fillmain(data) {
let mainlist = document.getElementById("mainlist");
data.forEach(function(item) {
let li = document.createElement('li')
li.setAttribute('id', item.menu_id)
li.innerHTML =
<a href=" + item.menu_lnk +
"><svg class="icn56"><use xlink:href=" + item.menu_icn +
"/></svg><span> + item.menu_txt +
</span></a>
mainlist.appendChild(li)
})
// mainlist.appendChild(fragment) //does not work...
}
function fillsub(data) {
let sublist = document.getElementById("sublist");
data.forEach(function(item) {
sublist.setAttribute('data-main', item.menu_main)
let li = document.createElement('li')
li.setAttribute('id', item.menu_id)
li.innerHTML =
<a href=" + item.menu_lnk +
"> + item.menu_txt +
</a>
sublist.appendChild(li)
})
// mainlist.appendChild(fragment)
}
// the last piece is to select the current selection in menu based on window.location.href
let mainli = document.querySelectorAll("#mainlist li")
let subli = document.querySelectorAll("#sublist li");
let main = document.body.dataset.main
let localhref = window.location.href.split("/").pop()
select()
function select() {
subli.forEach(function list(item) {
let href = item.firstElementChild.href.split("/").pop()
if (href === localhref) {
item.classList.add("selected")
}
});
mainli.forEach(item => {
let sub = item.id
if (sub === main) {
item.classList.add("selected");
}
});
}<i>
</i>
``
then
</C> callback or the async function:
<CODE>
`<i>
</i>const get_menu = async () => {
if (sessionStorage.main == null) {
await load_menu()
}
let data = JSON.parse(sessionStorage.main)
let main = document.body.dataset.main
fillmain(data.filter(item => item.menu_main == ['min']))
fillsub(data.filter(item => item.menu_main == [main]))
// The menu items are ready here
// Highlight the current item:
select();
}<i>
</i>
`</CODE>
The following code is located outside the async function, therefore the menu items are not available reliably here, as they are created only when the response of the server has arrived:
<CODE>
`<i>
</i>let mainli = document.querySelectorAll("#mainlist li")
let subli = document.querySelectorAll("#sublist li");
let main = document.body.dataset.main
let localhref = window.location.href.split("/").pop()
// select()<i>
</i>
``>@Sempervivum#1649159 It's always the same issue:
``<i>
</i>function fillmain(data) {
let mainlist = document.getElementById("mainlist");
let main = document.body.dataset.main
data.forEach(function(item) {
let li = document.createElement('li')
li.setAttribute('id', item.menu_id)
if (item.menu_id === main) {
li.classList.add("selected") <----- select when loading :-)
}
li.innerHTML =
<a href=" + item.menu_lnk +
"><svg class="icn56"><use xlink:href=" + item.menu_icn +
"/></svg><span> + item.menu_txt +
</span></a>
mainlist.appendChild(li)
})
}<i>
</i>
``
``<i>
</i> async function makeMenu() {
const response = await fetch('https://api3.go4webdev.org/menu/all');
const data = await response.json();
const main = document.body.dataset.main;
fillmain(data.filter(item => item.menu_main == ['min']));
fillsub(data.filter(item => item.menu_main == [main]));
select(); // This may get obsolete by your latest proposal
}
makeMenu()<i>
</i>
``
>@Sempervivum#1649162 No storage needed
>@Sempervivum#1649164 The browser will save the result of the fetch in it's cache
0.1.9 — BETA 4.23