LocalStorage data appearing in console but not in the DOM - javascript

I am currently building a to-do list app and I have already saved the data using the localStorage API.
The desired data, however, appears in the console whenever I console.log it but it still doesn't save in the DOM.
I also added the getItem() function and logged it into the console and I can still view it in the console, find here:
getItem content in the console
But it just doesn't store in the browser
Seeing this, it is in your inclination that it should have stored in the DOM and the content still remains after reloading but that just isn't the case here.
This function below adds a new item to the list, it also deletes and crosses out completed items too:
let id = 0;
function addTaskFunc() {
const aTask = `
<div class="task" id="task-${id}">
<button class="done__btn">
<i class="far fa-check-square"></i>
</button>
<p>${box.value}</p>
<button class="priority">Make priority</button>
<button class="cancel__btn">
<i class="far fa-times-circle"></i>
</button>
</div>
`;
const x = box.value;
if (x) {
taskList.insertAdjacentHTML('afterbegin', aTask);
box.value = '';
;
let cid = id;
const cancelBtn = document.querySelector(`#task-${id} .cancel__btn`);
cancelBtn.addEventListener('click', () => {
deleteItem(cid);
});
let cid2 = id;
// code for marking out an item as done
let cid3 = id;
// code for appending an item to the top of DOM
let cid4 = id;
persistData(cid4);
readData(cid4);
id++;
}
}
newTask.addEventListener('click', addTaskFunc); // Button to activate the function
persistData = id => {
const el = document.querySelector(`#task-${id}`);
localStorage.setItem('addedTasks222', el.innerHTML);
};
readData = id => {
const el = document.querySelector(`#task-${id}`);
const saved = localStorage.getItem('addedTasks222');
if (saved) el.innerHTML = saved;
console.log(el.innerHTML); // This line of code appears in the console
}
I also tried doing it this way inside of the addTaskFunc:
const r = document.querySelector(`#task-${id} .task`);
r.addEventListener('load', () => {
persistData(cid4);
readData(cid4);
});
When I try it with the method above I get the error code in the console:
Cannot read property of addEventListener of null.
I feel there is something wrong somewhere but I just cannot seem to find out where I am missing it.
One last thing, the localStorage only seems to store only one item into the key. Do I need a loop to sort that out?

you can store an array in the local storage like this
localStorage.setItem('array', JSON.stringify(YOURARRAY))
and then you can load that with
var restoredArray = JSON.parse(localStorage.getItem('array'));

sorry for the time..
So I was looking in to your code, and appears that you have some problems..
1- The first one I saw, was in the persistData() function. Accidentally you are just selecting one task to store with document.querySelector(.task);
to select all you need to use document.querySelectorAll(.task), this will return you an array. Because of that you only store one task.
2- Secondly is that you are trying to store html. with .innerHtml, and the innerHtml of your (".class") is buttons and etc.. You should store values.
3- At the end, when you are trying to print the data, you do document.querySelector(.task), and as you can see its returning you undefined, that's because you haven't a .task div yet.
So How you can appropriately make your app work.
1- The first thing you need to do is creating an array variable up on your js file.
let tasks = [];
2-That array will be used to store the values of your tasks with something like this
function addItem(){
let value = item.value; //you need to define the item some way
tasks.push(value);
print() //a function to print the values;
persistData() // store the array
}
3-To print the tasks
function print(){
tasks.forEach(task => {
let div = document.createElement("div");
div.innerHTML = `copy your html task to here, use ${task} to print the task value`
})
}
4-Store the tasks
function persistData(){
localStorage.setItem('addedTasks', tasks); //store the array
};
function readData(){
if (typeof(Storage) !== "undefined") { //check if the browser has localStorage
tasks = localStorage.getItem('addedTasks'); //update your array
print();
} else {
//No Web Storage message
}
};
5- Just run readData() when you load your document with
document.addEventListener("DOMContentLoaded", function(event) {
readData();
});
I hope that I can help

Related

Take selected text, send it over to Scryfall API, then take the link and put it in the selected text

I've been able to sort out the middle bit (the API seems to be called to just fine) along with the submenu displaying. Originally I thought that just the end part wasn't working but I'm now thinking that the selection part isn't either.
What am I doing wrong with the getSelection() and what do I need to do to insert a link into said selection? (to clarify, not to replace the text with a link, but to insert a link into the text)
//Open trigger to get menu
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Scry', 'serumVisions')
.addToUi();
}
//Installation trigger
function onInstall(e) {
onOpen(e);
}
//I'm not sure if I need to do this but in case; declare var elements first
var elements
// Get selected text (not working)
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var elements = selection.getRangeElements();
Logger.log(elements);
} else {
var elements = "Lack of selection"
Logger.log("Lack of selection");
}
}
//Test run
// insert here
// Search Function
function searchFunction(nameTag) {
// API call + inserted Value
let URL = "https://api.scryfall.com/cards/named?exact=" + nameTag;
// Grabbing response
let response = UrlFetchApp.fetch(URL, {muteHttpExceptions: true});
let json = response.getContentText();
// Translation
let data = JSON.parse(json);
// Jackpot
let link = data.scryfall_uri;
// Output
Logger.log(link);
}
// Test run
searchFunction("Lightning Bolt");
//Let's hope this works how I think it works
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
// Unsure what class I'm supposed to use, this doesn't
const insertLink = DocumentApp.getActiveDocument().getSelection().newRichTextValue()
.setLinkUrl(linkage);
Logger.log(linkage);
}
For the first part, I tried the getSelection() and getCursor() examples from the Google documentation but they don't seem to work, they all just keep returning null.
For the inserting link bit, I read all those classes from the Spreadsheet section of the documentation, at the time I was unaware but now knowing, I haven't been able to find a version of the same task for Google Docs. Maybe it works but I'm writing it wrong as well, idk.
Modification points:
In your script, the functions of getSelectedText() and searchFunction(nameTag) return no values. I think that this might be the reason for your current issue of they all just keep returning null..
elements of var elements = selection.getRangeElements(); is not text data.
DocumentApp.getActiveDocument().getSelection() has no method of newRichTextValue().
In the case of searchFunction("Lightning Bolt");, when the script is run, this function is always run. Please be careful about this.
When these points are reflected in your script, how about the following modification?
Modified script:
Please remove searchFunction("Lightning Bolt");. And, in this case, var elements is not used. Please be careful about this.
From your script, I guessed that in your situation, you might have wanted to run serumVisions(). And also, I thought that you might have wanted to run the individual function. So, I modified your script as follows.
function getSelectedText() {
const selection = DocumentApp.getActiveDocument().getSelection();
var text = "";
if (selection) {
text = selection.getRangeElements()[0].getElement().asText().getText().trim();
Logger.log(text);
} else {
text = "Lack of selection"
Logger.log("Lack of selection");
}
return text;
}
function searchFunction(nameTag) {
let URL = "https://api.scryfall.com/cards/named?exact=" + encodeURIComponent(nameTag);
let response = UrlFetchApp.fetch(URL, { muteHttpExceptions: true });
let json = response.getContentText();
let data = JSON.parse(json);
let link = data.scryfall_uri;
Logger.log(link);
return link;
}
// Please run this function.
function serumVisions() {
const hostText = getSelectedText();
const linkage = searchFunction(hostText);
if (linkage) {
Logger.log(linkage);
DocumentApp.getActiveDocument().getSelection().getRangeElements()[0].getElement().asText().editAsText().setLinkUrl(linkage);
}
}
When you select the text of "Lightning Bolt" in the Google Document and run the function serumVisions(), the text of Lightning Bolt is retrieved, and the URL like https://scryfall.com/card/2x2/117/lightning-bolt?utm_source=api is retrieved. And, this link is set to the selected text of "Lightning Bolt".
Reference:
getSelection()

Show the element saved in localStorage when reload the page

I'm learning js.
I want to show the saved li with localStorage created with appendChild when I reload the page.
<ul id="gfg">
<li>Computer Network</li>
<li>Data Structures</li>
</ul>
<button onclick="test()">Submit</button>
function test() {
var node = document.createElement('li');
node.textContent = 'Hi';
abc = document.getElementById('gfg').appendChild(node);
//localStorage
localStorage.setItem('abc', 'abc');
let a = localStorage.getItem('abc');
console.log(abc);
}
We can use a pair of functions to work with your items in the storage:
function getStorageItems() {
var data = localStorage.getItem('items');
return data ? JSON.parse(data) : null;
}
function saveStorageItem(text) {
var array = getStorageItems() || [];
array.push(text);
localStorage.setItem('items', JSON.stringify(array));
}
With this functions you can save and load an array of text (the text of each li). In the storage, we save a text, this functions also convert/parse your array to a JSON text.
We can create another function to add one li item to your page:
function addItem(text, save) {
var node = document.createElement('li');
node.textContent = text;
document.getElementById('gfg').appendChild(node);
if (save !== false)
saveStorageItem(text);
}
By default, we save in the storage the text. Only if we pass save=false, we omit the save action. In this form, we can add and save an item and also only add (without saving).
Your test function, in this form, add and save the item:
function test() {
addItem("Hi");
}
And a last function to load items from storage:
function loadFromStorage() {
var array = getStorageItems();
if (array) {
for (var i = 0; i < array.length; i++)
addItem(array[i], false);
}
}
Here we use false in addItem because only want add the item, not saving again. You can invoke loadFromStorage to load your saved items.
NOTE: here I only save the text because is the only data required to create your HTML. If you need more data, I suggest you use an object and save/load this serialized object in the same way or the HTML.

How to connect loop data to pdfgeneratorapi with wix corvid?

I'm generating PDF by using https://pdfgeneratorapi.com/.
Now I can show data one by one using this code.Can any one give me suggestion how can show all data with loop or any other way?
This below photos showing my template from pdfgenerator .
This is the code I'm using to generate PDF
let communicationWay1=[
{0:"dim"},
{1:"kal"}
];
let cstomerExpence1=[
{0:"dim"},
{1:"kal"}
];
let title="test";
let names="test";
let phone="test";
let email="test";
let maritalStatus="test";
let city="test";
let other="test";
const result = await wixData.query(collection)
.eq('main_user_email', $w('#mainE').text)
.find()
.then( (results) => {
if (results.totalCount>0) {
count=1;
// title=results.items[1].title;
names=results.items[0].names;
email=results.items[0].emial;
phone=results.items[0].phone;
maritalStatus=results.items[0].maritalStatus;
city=results.items[0].city;
other=results.items[0].cousterExpenses_other;
title=results.items[0].title;
communicationWay=results.items[0].communicationWay;
cstomerExpence=results.items[0].cstomerExpence;
}
if (results.totalCount>1) {
names1=results.items[1].names;
email1=results.items[1].emial;
phone1=results.items[1].phone;
maritalStatus1=results.items[1].maritalStatus;
city1=results.items[1].city;
other1=results.items[1].cousterExpenses_other;
title1=results.items[1].title;
communicationWay1=results.items[1].communicationWay;
cstomerExpence1=results.items[1].cstomerExpence;
}
} )
.catch( (err) => {
console.log(err);
} );
// Add your code for this event here:
const pdfUrl = await getPdfUrl
({title,names,email,phone,city,maritalStatus,other,communicationWay,cstomerExpence,title1,
names1,email1,phone1,city1,maritalStatus1,other1,communicationWay1,cstomerExpence1
});
if (count===0) { $w("#text21").show();}
else{ $w("#downloadButton").link=wixLocation.to(pdfUrl);}
BELOW CODE IS BACKEND CODE/JSW CODE.
Also I want to open pdf in new tab. I know "_blank" method can be used to open a new tab.But I'm not sure how to add it with the url
import PDFGeneratorAPI from 'pdf-generator-api'
const apiKey = 'MYKEY';
const apiSecret = 'MYAPISECRET';
const baseUrl = 'https://us1.pdfgeneratorapi.com/api/v3/';
const workspace = "HELLO#gmail.com";
const templateID = "MYTEMPLATEID";
let Client = new PDFGeneratorAPI(apiKey, apiSecret)
Client.setBaseUrl(baseUrl)
Client.setWorkspace(workspace)
export async function getPdfUrl(data) {
const {response} = await Client.output(templateID, data, undefined, undefined, {output: 'url'})
return response
}
Just put it in a while loop with a boolean condition.
You can create a variable, for example allShowed, and set its value to False. After that, create another variable, for example numberOfDataToShow, and set it as the number of elements you want to display. Then create a counter, countShowed, initialized with 0 as its value.
Now create a while loop: while allShowed value is False, you loop (and add data).
Everytime a piece of your data is showed, you increment the value of countShowed (and set it to go on adding/showing data). When countShowed will have the exact same value of numberOfDataToShow, set allShowed to True. The loop will interrupt and all your data will be showed.
You would need to use the Container or Table component in PDF Generator API to iterate over a list of items. As #JustCallMeA said you need to send an array of items. PDF Generator API now has an official Wix Velo (previously Corvid) tutorial with a demo page: https://support.pdfgeneratorapi.com/en/article/how-to-integrate-with-wix-velo-13s8135

How to iterate on the links to google forms in the cells of a spreadsheet column to get their questions?

I have a Google spreadsheet with links to questionnaires. I wanted to know how to get the questions from each of the questionnaires.
I guess I have to do: at best, use the script editor and iterate on the lines, and at worst, do webscraping.
const puppeteer = require('puppeteer');
function appendString() {
var range = SpreadsheetApp.getActiveSheet().getActiveRange();
var numRows = range.getNumRows();
var numCols = 0;
for (var i = 1; i <= numRows; i++) {
for (var j = 1; j <= numCols; j++) {
var currentValue = range.getCell(i,j).getValue();
await page.goto(currentValue);
const pollFrame = page.frames().find() # From there I have some difficulties
}
}
}
But I get the following error:
SyntaxError: await is only valid in async function (ligne 10, fichier "Code.gs")
Not to mention the async problem or the buttonthat I still have to click, the selection looks like this:
<div class="freebirdFormviewerViewItemsItemItemTitle exportItemTitle freebirdCustomFont" id="i1" role="heading" aria-level="3" aria-describedby="i.desc.310938276">How often did you fly before the Covid-19 epidemic? </div>
But the IDs don't follow a logical numerical order, so I don't know how to extract them automatically.
Then I don't know how to do it. I wonder if it's simpler because they're products from the same supplier.
Here is the equivalent in csv format:
https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link
https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link
https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link
Update
So I tried the anwer kindly posted by Neven Subotic's:
// this array will store forms and their questions
let formAndQuestions = [];
let formIds = ["https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link",
"https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link",
"https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link"]
formIds.forEach( formId => {
const form = FormApp.openById( formId );
// lets get the name
const formName = form.getTitle();
// first we get all items
const allItemsInThisForm = form.getItems();
// then we get filter out anything that is not a questions
const allQuestionsInThisForm = allItemsInThisForm.filter( item => {
return isThisItemAQuestion( item )
});
// now we store them in our object
formAndQuestions.push( {
formId: formId,
formName: formName,
questions: allQuestionsInThisForm
})
});
// this function is used to only get the itemTypes you want
// see reference for more information
function isThisItemAQuestion( item ){
const itemType = item.getType();
const validQuestionItemTypes = [ FormApp.ItemType.TEXT, "add others here" ]
let isValid = false;
validQuestionItemsTypes.forEach( validItemType => {
if( itemType == validItemType ) {
isValid = true;
}
});
return isValid
}
Unfortunately I obtain the following error message with the following details Exception: No item with the given ID could be found, or you do not have permission to access it. (line 9, "const form = FormApp.openById( formId );"). I don't understand. As you can see in the gif, I can open these links, so I should have the permission to access them isn't it?
I also tried Ruben's ideas with:
// this array will store forms and their questions
let formAndQuestions = [];
let formIds = ["https://docs.google.com/forms/d/e/1FAIpQLSfzocEm6IEDKVzVGOlg8ijysWZyAvQur0NheJb_I_xozgKusA/viewform?usp=sf_link"]//,
//"https://docs.google.com/forms/d/e/1FAIpQLScrm0ZTrvlONf5MX37N93H_FajNzfbNy9ZtitX-Vq9PPuLPHA/viewform?usp=sf_link",
//"https://docs.google.com/forms/d/e/1FAIpQLSeolFSh3OyS_XpX1lRIJP-8CH8WG0X0hL98SM9d85LqC22Bow/viewform?usp=sf_link"]
function scrapeForms(){
formIds.forEach( formId => {
// The code below logs the HTML code of the Google home page.
var response = UrlFetchApp.fetch(formId);
results = response.getElementsByClassName("freebirdFormviewerViewItemsItemItemTitleContainer");
Logger.log(results.getContentText())
});
}
But got back:
TypeError: response.getElementsByClassName is not a function (ligne 13, fichier "Code")
According to What is this Javascript "require"? require is not part of the standard JavaScript an AFAIK it's not supported by Google Apps Script.
By the other hand, the error message can't be easily solved as Google Apps Script Chrome V8 engine doesn't support async functions. Related Is google apps script synchronous?
If you will be using Google Apps Script, and you are the form owner or a form editor, instead of trying to web scraping a Google Form use the Forms Service of Google Apps Script. For this you will need the form ../edit URLs instead of the ../viewform URLs. On the official docs there is a quickstart that might help you https://developers.google.com/apps-script/quickstart/forms.
You could use openByUrl to "open" a form. It will not be actually opened in your web browser, it will be opened on the server side. Then you could use getItems to get all the questions, sections, images, videos, etc.
If you aren't the form owner or a form editor then you should use UrlFetchApp service and somehow parse the web page source code of each form based on the position of the questions. Related question: Google Sheets: How to import the following data?
Also, if the form has several sections you should do a post request to emulate clicking on the next button in order to get the second and following sections. There are more "also if the form has..." but I will stop here as the main part of question was already answered, I think.
You first want to get all the forms, so place those in an array:
const formIds = ["someId", "anotherId", "andSoOn"]
Then, lets use the FormApp to get the form and all items. Items can be of different types, see documentation.
// this array will store forms and their questions
let formAndQuestions = [];
formIds.forEach( formId => {
const form = FormApp.openById( formId );
// lets get the name
const formName = form.getTitle();
// first we get all items
const allItemsInThisForm = form.getItems();
// then we get filter out anything that is not a questions
const allQuestionsInThisForm = allItemsInThisForm.filter( item => {
return isThisItemAQuestion( item )
});
// now we store them in our object
formAndQuestions.push( {
formId: formId,
formName: formName,
questions: allQuestionsInThisForm
}
});
// this function is used to only get the itemTypes you want
// see reference for more information
function isThisItemAQuestion( item ){
const itemType = item.getType();
const validQuestionItemTypes = [ FormApp.ItemType.TEXT, "add others here" ]
let isValid = false;
validQuestionItemsTypes.forEach( validItemType => {
if( itemType == validItemType ) {
isValid = true;
}
});
return isValid
}
Then you can initially log out the results and see what it looks like:
Logger.log( formAndQuestions )
Item Types

Each click triggers different API call

I have list and each clicked item triggers different API request. Each request have different duration. On success I'm displaying some data.
Issue is that when I click on item#1 which takes approx 6000 to load, and just after on item#2 which takes 2000 to load, I will have the last clicked item displayed - which is item#2 because it has already loaded and once item#1 has received data my data will change to that. This is wrong as I want to display data from the latest click.
This is how I handle event:
newList.on('click', 'li', (e) => {
let id = $(e.currentTarget).data("id");
store.getCharacterDetails(id).then(docs => {
this.clearDetails();
this.charDetails = docs;
this.displayDetails(this.charDetails);
})
My API is a simulation from store object.
I suppose this works as expected but I do want the last triggered request to be valid.
A crude and simple method can be creating an array and pushing the IDs and after the asynchronous operations you can just check if it is the latest click or not. But pitfall is that if clear and displayDetails takes much time and if someone click while it was clearing and displaying it will not register the latest click.
Anyway, here is the code maybe you can make something better out of it.
var latestClick = [];
newList.on('click', 'li', (e) => {
let id = $(e.currentTarget).data("id");
latestClick.push(id);
store.getCharacterDetails(id).then(docs => {
if(id === latestClick[latestClick.length - 1]){
this.clearDetails();
this.charDetails = docs;
this.displayDetails(this.charDetails);
latestClick = [];
}
})
})
Make charDetails an object that keeps all of the results, keyed by the ids. Keep track of the last clicked id.
// in constructor
this.charDetails = {};
this.lastId = null;
newList.on('click', 'li', (e) => {
let id = $(e.currentTarget).data("id");
this.lastId = id;
if (this.charDetails[id] === id) { // don't cancel requests, cache them!
this.displayDetails(this.charDetails[id])
} else {
store.getCharacterDetails(id).then(docs => {
// this runs later, cache the result
this.charDetails[id] = docs;
if (id === lastId) { // only update UI if the id was last clicked
this.displayDetails(docs)
}
});
}
});

Categories