Trouble storing simple object using Chrome storage API - javascript

I'm trying to use the Google Chrome Storage API (https://developer.chrome.com/extensions/storage) to persist a textarea that acts as a persistent notes field in my plugin
In the HTML I have a handler setup to run save_notes on blur.
I want to fire load_notes() immediately if the textarea is blank. save_notes should save the textarea to object key 'stored_obj'
I didn't code it right and it's not working, anyone see the issue?
notes.js
///// select the text area
// var note_area = document.getElementById("textarea")
var note_area = document.querySelector("#textarea")
//// create a function to save the textarea to local storage
// I'll also want to check & load previously saved notes
;(function load_notes () {
if (!note_area) {
note_area.value = chrome.storage.sync.get(stored_obj)
}
})()
function save_notes () {
chrome.storage.sync.set({
stored_obj: note_area
}, function () {
console.log("Saved into Chrome Storage")
})
}
//// setup a blur handler in notes.html to fire function save_notes
notes.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Note Screen</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css">
<link rel="stylesheet" href="style.css">
<script src="scripts/notes.js"></script>
</head>
<body>
<div class="container">
<ul class="nav nav-tabs">
<li>Settings</li>
<li role="presentation" class="active">Notes</li>
</ul>
<div id="notes-header">
<h1 id="noteh1">Note Screen</h1>
</div>
<hr>
<div class="row">
<div class="col-sm-8" id="notes-section">
<h2>Websurf Notes</h2>
<p>Write notes here. Reference them to do searches and checks during free time</p>
<!--<button id='notesave' class="btn btn-primary">Save Notes & Close Tab</button>-->
<br>
<textarea name="ntxt" id="notetext" cols="20" rows="20" class="form-control" onblur="save_notes()"></textarea>
</div>
<div class="col-sm-4">
<h2>Override</h2>
<p>Things that demand an immediate view are the ONLY intended targets of this override</p>
<div class="form-group">
<!--<form action="" class="form-control">-->
<select name="" id="bypass-limit" class="form-control">
<option value="">Bypass Time Limit</option>
<option value="5">5 minutes</option>
<option value="10">10 minutes</option>
<option value="15">15 minutes</option>
<option value="20">20 minutes</option>
<option value="30">30 minutes</option>
</select>
</div>
<div class="form-group">
<button id="bypass-button" class="btn btn-danger">
Bypass Now (action will be logged)
</button>
<!--</form>-->
</div>
</div>
</div>
</div>
</body>
</html>

chrome.storage.sync.get() is an asynchronous function just like set() is. You have the proper callback function set up for set(), but not get:
chrome.storage.sync.get("stored_obj", function(result) { note_area.value = result } );
That is the first thing that jumps out at me, there may be other errors.

Wrong id
In var note_area = document.querySelector("#textarea")
note_area will always return null. There is id such as textarea it is notetext or use
var note_area = document.querySelector("textarea")
Also in the if statement if(note_area.val === "")
Inline event handler
Extension produces following error on run
Refused to execute inline event handler because it violates CSP.
this because of onblur="save_notes()"
It is not allowed now. Instead, use this in your js file document.querySelector("#notetext").addEventListener("blur", save_notes);
chrome.storage.sync
chrome.storage.sync.get(stored_obj)
stored_obj does not exit! so it can't get you anything from the store use this instead.
chrome.storage.sync.get("stored_obj")
It will return you an object resultObject do resultObject.stored_obj to get the value.
You need to pass note_area.val in set

Related

How to run HTML from google library?

I have a Google Library with HTML form, process form script addNewItemand script to run the form in popup window addItem.
function addItem()
{
var html = HtmlService.createHtmlOutputFromFile('input/form.html');
SpreadsheetApp.getUi()
.showModalDialog(html, 'Add New Recipe');
}
function addNewItem(form_data)
{
var url = "SPREADSHEET_URL_TO_DATA_COLLECTION";
var ss = SpreadsheetApp.openByUrl(url);
var sheet = ss.getSheetByName('List');
var asiaTime = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd");
var dishName = form_data.dish_name;
var cuisineName = form_data.cuisine_name;
var placeName = form_data.place_name;
var categoryName = form_data.category_name;
var num = sheet.getRange(sheet.getLastRow(), 1).getValue() + 1 || sheet.getLastRow();
sheet.appendRow([num, dishName, cuisineName, placeName, categoryName, asiaTime]);
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
</head>
<body>
<form id="myform">
<div class="block form-group">
<label for="dish_name">Dish name</label>
<input type='text' name='dish_name' id="dish_name" required="required"/>
</div>
<div class="block form-group">
<label for="place_name">Place</label>
<select id="place_name" name="place_name" type="text" required>
<option value="LL4H">LL4H</option>
<option value="LL4T">LL4T</option>
</select>
</div>
<div class="block form-group">
<label for="cuisine_name">Cuisine</label>
<select id="cuisine_name" name="cuisine_name" type="text" required>
<option value="Asian">Asian</option>
<option value="Western">Western</option>
</select>
</div>
<div class="block form-group">
<label for="category_name">Category</label>
<input id="category_name" name="category_name" type="text" list="quote-choices" required>
<datalist id="quote-choices">
<option value="Starter">Starter</option>
<option value="Main course">Main course</option>
<option value="Veggi side">Veggi side</option>
<option value="Carbs side">Carbs side</option>
<option value="Dessert">Dessert</option>
<option value="Dough">Dough</option>
<option value="Sauce">Sauce</option>
<option value="Drink">Drink</option>
<option value="Other">Other</option>
</datalist>
</div>
<div class="block">
<button type="submit" class="action">Submit</button>
</div>
</form>
<script>
document.querySelector("#myform").addEventListener("submit",
function(e)
{
e.preventDefault(); //stop form from submitting
console.log(this)
google.script.run.withSuccessHandler(()=> google.script.host.close()).addNewItem(this);
}
);
</script>
</body>
</html>
I connected this Library with the Google Spreadsheet and declared new function to run library script to open the form.
function createRecipe() {
RecipeBuilder.addItem();
}
function addNewItem(form_data) {
RecipeBuilder.addNewItem(form_data);
}
Form appears in popup window well.
I click Submit to submit my data from the form, but serverside process does not start.
How to run this form correct? Where I'm wrong? How to fix it?
UPDATED
It's still does not work with library but works well if I put it in bound-container script of some spreadsheet. Unfortunately, I can't use it in bound-container because full code of addNewItem(form_data) function must replicate current spreadsheet. Finally it will be 1000+ Google Spreadsheets with same numbers of bound-containers. It will be super complicated to update it
I believe your goal and situation as follows.
You want to use addItem(), addNewItem(form_data) and input/form.html as a GAS library.
When you call this library from the client side, when the submit button is clicked, the data is not sent.
You want to remove this issue.
The library name is RecipeBuilder.
For this, how about this modification?
Modification points:
I think that the reason of your issue is google.script.run.addNewItem(this);. In this case, when addNewItem(this) is run, this function is searched from the GAS project. By this, an error occurs like Uncaught TypeError: google.script.run.addNewItem is not a function. I think that this is the reason of your issue.
In order to remove this issue, how about the following modification? In this modification, one more function is added to the client side.
Modified script:
function createRecipe() {
RecipeBuilder.addItem();
}
// Added
function addNewItem(form_data) {
RecipeBuilder.addNewItem(form_data);
}
By this, when google.script.run.addNewItem(this); is run at the library side, addNewItem of client side is run, and then, RecipeBuilder.addNewItem of the library side is run.
Note:
In your Javascript of library side, google.script.run.addNewItem(this); and google.script.host.close(); are run in order. But google.script.run works with the asynchronous process. So if the process of addNewItem(this) is slow, the dialog might be closed before addNewItem(this) is run. So I think that the following modification might be also used.
From
google.script.run.addNewItem(this);
google.script.host.close();//close this dialogbox
To
google.script.run.withSuccessHandler(()=> google.script.host.close()).addNewItem(this);
Reference:
Class google.script.run
I recommend you put your scripts into:
document.addEventListener('DOMContentLoaded', event => {
// your code here
});

How to set text value from `select` drop list into `input` text field?

I am beginner in java and not familiar with javascript or php, so I just can explore and copypast simplies exemplars of javascript/php code for my needs. So that what I did find in stackowerflow and try to implement for my need:
Script:
<script type="text/javascript">
function onchange() {
var e = document.getElementById("persId");
var persSelected = e.options[e.selectedIndex].text;
document.getElementById("myInput").value=persSelected;
}
</script>
And body:
<body>
<div th:replace="fragments/main.html :: top_menu"/>
<div class="w3-container" align="center">
<form action="/add" method="post" class="w3-container w3-card-4 w3-light-grey w3-text-blue w3-margin w3-center" style="width: 50%" >
<h2 class="w3-center">Add the challenger</h2>
<select name="pers" id="persId" onchange="onchange();">
<option th:each="person: ${personList}">
<title id="titleId" th:text="${person.getName()}"/>
</option>
</select>
<div class="w3-row w3-section">
<div class="w3-col" style="width:50px"><i class="w3-xxlarge fa fa-user"></i></div>
<div class="w3-rest">
<input class="w3-input w3-border" id="myInput" name="lastName" type="text" onkeyup="myFunction()" placeholder="Search for last names.." title="Type in a last name">
</div>
</div>
I get the list of person into drop list, but not have the selected item into input text field.
How to correctly implement this code?
There are to problems with your code
options don't have title elements
since there are no title elements you have to get the selected value from the select element
Let me explain.
This code makes no sense:
<option th:each="person: ${personList}">
<title id="titleId" th:text="${person.getName()}"/>
</option>
Options don't have titles. Instead you should just use text inside of options.
Like this:
<option th:each="person: ${personList}">
${person.getName()} // this should just turn into text
</option>
so you will end up with:
<option th:each="person: ${personList}">
John Doe
</option>
okay first thing done now to fix your other problem. :)
This line could have worked.
var persSelected = e.options[e.selectedIndex].text;
but since there are no element inside the option, then we need to get the value somewhere else.
The easiest way to do so would be to get the value of the select element.
like this:
var persSelected = document.getElementById("persId").value;
here is an example of a working script file:
Script:
<script type="text/javascript">
function onchange() {
var persSelected = document.getElementById("persId").value;
document.getElementById("myInput").value = persSelected;
}
</script>
Seems like this line:
document.getElementById("persId").value=persSelected;
Should be:
document.getElementById("myInput").value=persSelected;
I dont know - how and why are using parameter type in script block.. But, when I erase this code: type="text/javascript" all works well

Uncaught TypeError: $(...).ready(...) is not a function even though it works on JsFiddle

The JSFiddle in question seems to have no trouble: http://jsfiddle.net/S3LF3/ (if you type a url like google.com it will select the value of ".com" or whatever you put after the dot and it is in the list)
However the jQuery function seems to be faulty?
In chrome I get this error:
Uncaught TypeError: $(...).ready(...) is not a function
Here is my code:
<html>
<head>
<title>Add Site</title>
<script src="js/jquery-3.1.1.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/msdropdown/dd.css" />
<script src="js/msdropdown/jquery.dd.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/msdropdown/flags.css" />
</head>
<body>
<h1>Add new site</h1>
<div class="contentbox">
<form method="post">
<input type="hidden" name="addsite" value="true"/>
<p>
<label for="site_url">Site url:</label>
<input type="text" name="site_url" id="urlText" placeholder="domain.xxx" value=""/>
</p>
<p>
<label for="site_url">Search locale:</label>
<select name="locale" id="locale">
<option value="">
Select locale
</option>
<optgroup label="Popular">
<option value=".dk" data-image="images/msdropdown/icons/blank.gif" data-imagecss="flag dk" data-title="Denmark">Denmark - Danish</option>
<option value=".de" data-image="images/msdropdown/icons/blank.gif" data-imagecss="flag de" data-title="Germany">Germany - German</option>
<option value=".au" data-image="images/msdropdown/icons/blank.gif" data-imagecss="flag au" data-title="Australia">Australia - English</option>
</optgroup>
</select>
</p>
<p>
<label for="site_url"></label>
<input type="submit" name="submit" class="btn" value="Add">
</p>
</form>
</div>
</body>
</html>
<script>
$(document).ready(function() {
$("#locale").msDropdown();
})
(function ($) {
$('#urlText').on('change', function () {
var value = this.value,
parts = this.value.split('.'),
str, $opt;
for (var i = 0; i < parts.length; i++) {
str = '.' + parts.slice(i).join('.');
$opt = $('#locale option[value="' + str + '"]');
if ($opt.length) {
$opt.prop('selected', true);
break;
}
}
})
})(jQuery);
</script>
What I want to do is when a user types a url, like "google.dk" , it should select the value with ".dk" at the end from the dropdown for him.
I played with your code and found out, it's the missing semicolon at the end of $(document).ready();
$(document).ready(function() {
$("#locale").msDropdown();
});
Just try it in the built-in editor:
<html>
<head>
<title>Add Site</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/msdropdown/dd.css" />
<script src="js/msdropdown/jquery.dd.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/msdropdown/flags.css" />
</head>
<body>
<h1>Add new site</h1>
<div class="contentbox">
<form method="post">
<input type="hidden" name="addsite" value="true"/>
<p>
<label for="site_url">Site url:</label>
<input type="text" name="site_url" id="urlText" placeholder="domain.xxx" value=""/>
</p>
<p>
<label for="site_url">Search locale:</label>
<select name="locale" id="locale">
<option value="">
Select locale
</option>
<optgroup label="Popular">
<option value=".dk" data-image="images/msdropdown/icons/blank.gif" data-imagecss="flag dk" data-title="Denmark">Denmark - Danish</option>
<option value=".de" data-image="images/msdropdown/icons/blank.gif" data-imagecss="flag de" data-title="Germany">Germany - German</option>
<option value=".au" data-image="images/msdropdown/icons/blank.gif" data-imagecss="flag au" data-title="Australia">Australia - English</option>
</optgroup>
</select>
</p>
<p>
<label for="site_url"></label>
<input type="submit" name="submit" class="btn" value="Add">
</p>
</form>
</div>
</body>
</html>
<script>
$(document).ready(function() {
//$("#locale").msDropdown();
});
(function ($) {
$('#urlText').on('change', function () {
var value = this.value,
parts = this.value.split('.'),
str, $opt;
for (var i = 0; i < parts.length; i++) {
str = '.' + parts.slice(i).join('.');
$opt = $('#locale option[value="' + str + '"]');
if ($opt.length) {
$opt.prop('selected', true);
break;
}
}
})
})(jQuery);
</script>
The reason the JSFiddle works fine is it doesn't actually use the country flag drop down menu plugin you found. The way the plugin works is different than the way a standard <select> html tag works.
I looked inside the jquery.dd.js file, and it seems there is no way to interact with the dropdown menu using javascript. Therefore, your line $opt.prop('selected', true); is valid for a real drop down menu (a <select> tag), but not for the plugin's drop down menu, which handles the selected state differently. Also, the plugin provides little documentation and has little to no comments in the code, so this makes it diffcult to know how the plugin works.
I can see 2 solutions to your problem:
You can try to understand the plugin's code in the jquery.dd.js file and try to implement a way to tell the plugin which element you want to select. I wouldn't normally recommend editing third-party plugins, but since the site you got the plugin from has not been updated since 2012, there is little chance for the plugin to receive updates in the future, so editing it would be safe in this case. It will require some knowledge in jQuery plugin making, though.
You can try to find another country flag drop down plugin that can be updated using javascript instead of using your mouse only. Such projects can be found on Github (here are some results found on Github: Country flag drop down menu results on Github).
Previous information found:
From the code you provided, you are missing a semi-colon after the call of the $(document).ready() function. It should look like this:
$(document).ready(function() {
$("#locale").msDropdown();
}); // <--- This semi-colon is missing
Also, the code in (function ($) {// code} )(jQuery); may be executed before the document was ready, which means this code could sometimes not work. Try putting this code inside your $(document).ready(); anonymous function. This way, you will be sure all html nodes will be accessible by jQuery.
Finally, you should consider putting your javascript logic somewhere inside the <html> tag (inside the <head>, right after <body> or right before </body>). While putting the <script> tag after the </html> tag may work at first, this is far from valid html and may be the cause of some problems you're having. The best way would be to put your javascript in a .js file and link it in your <head> the same way you linked jQuery and msdropdown:
<head>
<!-- Your other linked files... -->
<script src="js/main-logic.js"></script>
</head>
The JQuery shortcut - $() - seems to have been overridden by another library is my guess.
Try using:
jQuery( document ).ready(function( $ ) {
// Code that uses jQuery's $ can follow here.
});

How to change the content of a page with javascript based on select input?

Let's say I have a webpage like this:
<div style="margin:auto;text-align:center;padding-top:10px;">
<form action="" method="POST">
<p style="text-align:center;">
<select>
<option value="blogs">blogs.php</option>
<option value="portfolio">portfolio.php</option>
<option value="contact">contact.php</option>
<option value="home">home.php</option>
</select>
</p>
<p style="text-align:center;">
<input type="submit" name="submit" value="Submit"/>
</p>
</form>
</div>
<div class="pagetitle">
<h5>Option Value (for example blogs)</h5>
</div>
As you can see, a user can choose from 4 options in the select menu. I want to know ,is there any way to change the content of this div => <div class="pagetitle"> with javascript onsubmit ? For example if a user choosed blogs.php ,the h5 tag will change to <h5>blogs</h5> & if he choosed portfolio.php ,it'll be changed to <h5>portfolio</h5> . I really appreciate if you know how to do this ... thanks in advance!
You'll need to start by adding a javascript function to handle the event.
<script>
function changedSelect(x)
{
document.getElementById('pageTitleDiv').innerHTML = "<h5>"+x+"</h5>";
}
</script>
And then you'll need to trigger the event when the "select" box is changed.
<select onchange="changedSelect(this.value)">
Finally, I would give the div an "ID" so that it is specifically altered.
<div class="pagetitle" id="pageTitleDiv">
You need to bind a jquery .change event to the select element and have it's selected value populated as the text of h5 tag
<script>
$(document).ready(function(){
$('select').change(function()
{
$('h5').text($(this).val());
}).change(); // this is optional - it basically invokes the change event as soon as the function is registered.
});
</script>
Example : https://jsfiddle.net/DinoMyte/6x80vabm/4/
Using vanilla javascript, I added an id to the select element and used javascript to get the value of the select option, then I updated the element on the DOM.
<div style="margin:auto;text-align:center;padding-top:10px;">
<form action="" method="POST">
<p style="text-align:center;">
<select id="myselect">
<option value="blogs">blogs.php</option>
<option value="portfolio">portfolio.php</option>
<option value="contact">contact.php</option>
<option value="home">home.php</option>
</select>
</p>
<p style="text-align:center;">
<input type="submit" name="submit" onclick="myFunction()" value="Submit"/>
</p>
</form>
</div>
<div class="pagetitle">
<h5>Option Value (for example blogs)</h5>
</div>
<script>
function myFunction() {
var x = document.getElementById("myselect").selectedIndex;
var _value = document.getElementsByTagName("option")[x].value;
document.getElementsByTagName('h5')[0].innerText = _value
}
</script>
Note: This can be achieved in several ways using vanilla JS or libraries, but I think this answers your question.

Bootstrap Combobox Update

The Background
I have the following HTML document:
<!-- file: index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Form</title>
<!-- Plugins CSS -->
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="css/bootstrap-combobox.css">
</head>
<body class="one-page fixed-header">
<div class="page-box">
<div class="page-box-content">
<h3>Form 1</h3>
<div class="content">
<div class="combobox-container" id="employee-name-box">
<label for="addScheduleLoc">Location: <span class="required">*</span></label>
<select class="form-control combobox location" name="addScheduleLoc" id="addScheduleLoc" required>
<option value selected="selected">---Select Location---</option>
</select>
</div>
<div class="combobox-container" id="employee-name-box">
<label for="addScheduleTier">Tier: <span class="required">*</span></label>
<select class="form-control combobox tier" name="addScheduleTier" id="addScheduleTier" required>
<option value selected="selected">---Select Tier---</option>
</select>
</div>
</div>
<hr>
<h3>Form 2</h3>
<div id="changeScheduleModalBody" class="content">
<div class="combobox-container" id="employee-name-box">
<label for="chScheduleLoc">Location: <span class="required">*</span></label>
<select class="form-control combobox location" name="chScheduleLoc" id="chScheduleLoc" required>
<option value selected="selected">---Select Location---</option>
</select>
</div>
<div class="combobox-container" id="employee-name-box">
<label for="chScheduleTier">Tier: <span class="required">*</span></label>
<select class="form-control combobox tier" name="chScheduleTier" id="chScheduleTier" required>
<option value selected="selected">---Select Tier---</option>
</select>
</div>
</div>
</div>
<!-- .page-box-content -->
</div>
<!-- .page-box -->
<script src="js/jquery-2.1.3.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/bootstrap-combobox.js"></script>
<script src="js/testJavascript.js"></script>
</body>
</html>
There is an AJAX call to a server which retrieves some data after the page has loaded and puts the data into a variable and is used to populate the comboboxes. There are multiple comboboxes that should contain the same information (eg. All comboboxes with class "location" should contain the same list of locations). I have simulated this in the following javascript file. Please note that the initialization of the comboboxes must happen first because I may need to update the contents of the associated <select> element after the combobox is initialized.
//file: js/testJavascript.js
var options = {
locations: "<option id='1'>London</option><option id='2'>Los Angeles</option><option id='3'>Sydney</option>",
tiers: "<option id='1'>Upper</option><option id='2'>Middle</option><option id='3'>Lower</option>"
}
$(document).ready(function(){
$('.combobox').combobox({
bsVersion: '3'
}); //This statement cannot be moved.
$('select.location').append(options.locations);
$('select.tier').append(options.tiers);
});
The Problem
The problem that I'm running into is after I append the options to the <select> elements, the comboboxes aren't updated to show the most recent information. I realize that if I call $('.combobox').combobox({...}); after options are added, the combobox will be correct. But I may need to update the information in the comboboxes after the comboboxes are initialized the first time.
The Question
How can I get the comboboxes to update after I add options to the corresponding <select> tag and after they have already been initialized?
After some more research and fiddling around, I found that the following code works to refresh the combobox. (using the issue shown here)
$('select.location').each(function(){ //need to run in .each to refresh all the elements
$(this).data('combobox').refresh();
});
$('select.tier').each(function(){
$(this).data('combobox').refresh();
});
Try to refresh your combobox using the following code :
$('select.location', 'select.tier').data('combobox','refresh');
Hope this helps.

Categories