I am encountering a 'small' problem when making a new object in the options page.
In the options page I create a few objects and save them as general settings. These objects have method to communicate with different API's. But as soon as I get one of those objects to work with, I lose the context on the page I am.
For example:
The options page I create an object that has a method 'request' and I send an ajax request to some api with this method. When I call this on an other page the ajax request is logged within the options page. When I close the options page I lose all context of the logs it makes.
Is there a way to force the context reference to the current page? or did I make a mistake with creating objects on the wrong pages/saving them and retrieving them on a page that needs them? (IE Should I only save the data I need to create objects on the page itself? (which seems like alot of overhead for the the same thing(?)).
Thanks in advance.
EDIT
I have an option page which creates an Object lets call it MyApi. I create a new MyApi and store it in chrome.storage.local . When a user has some text selected and clicks on the context menu I open a new page(selectedText.html) which shows the selected text and some API calls are made, which are mostly ajax requests. The moment I get the object from storage in selectedText.html and make any request with MyApi I see no logs in the network tab of the ajax requests, neither any console logs. But with my options page open I see everything in there.
EDIT2
save : function()
{
var obj = {'api':this.data};
chrome.storage.local.set(obj,function() { if(chrome.runtime.lastError) console.warn(chrome.runtime.lastError); });
}
This is in the background script.
You could achieve what you want this way:
Define your object/function/whatever in the background page.
Use chrome.runtime.getBackgroundPage() to get a hold on the background pages window object.
Execute the desired method, passing as argument an object of the local context. (Of course you have to modify the function to accept and make use of such an argument.)
Example:
In background.js:
function myFunc(winObj, msg) {
winObj.console.log(msg);
}
In otherPage.js:
chrome.runtime.getBackgroundPage(function(bgPage) {
bgPage.myFunc(window, "This will be logged in 'otherPage.html' !");
});
It is not the cleanest solution, but it might work...
Related
I have Tabulator nearly working as I need for a web application I am designing. This app is calling web services in a backend app written in Java.
I created an InitialFilter set, the filtering, sorting, and pagination is handled by the backend. Next, I am creating an Accordion control for the various filter inputs by the end-user. No issues yet. I created two buttons, one to Apply the filter based on the user preferences, and another to Reset/Clear the filter parameters.
The Tabulator object is already created and has the default data already showing on the page. When the user sets the custom filter and clicks the Apply button, a JQuery function captures the on-click event and executes the following code.
$(function(){
$('#btn-apply').on('click', function(e){
// handle click event of button
// Get values first
var subFrom = $('#txt-submission-from').val();
var subTo = $('#txt-submission-to').val();
// Set filters
NIBRSTable.clearFilter();
NIBRSTable.addFilter("submissionPeriod", ">=", subFrom);
NIBRSTable.addFilter("submissionPeriod", "<=", subTo);
// Call function to load data
NIBRSTable.setData();
});
});
Error Returned
Ajax Response Blocked - An active ajax request was blocked by an
attempt to change table data while the request was being made
tabulator.min.js:5:24222
I have tried commenting out one source line at a time. It appears the setFilter() calls are causing the Ajax Response Blocked error even though there is not anything actively occurring (the tabulator DOM is already loaded)
I have many more items for which the end-user may filter. The two filters shown in the code listing above are just a start.
That isn't an error message, that is just a console warning.
What it means is that multiple ajax requests have been made in quick succession and that one request has been made before the first one returned, therefore the response of the first request will be ignored so the table isn't partially redrawn.
In this case it is being triggered because you are calling the addFilter function twice in quick succession which is triggering the ajax request twice with the second filter being added before the first ajax request has been sent. (there is also no need to call the setData function, adding a filter when ajaxFiltering is enabled will automatically trigger the request).
To avoid this double ajax request you could pass an array of filter objects into the addFilter function and only call it once:
NIBRSTable.addFilter([
{
field:"submissionPeriod",
type:">=",
value:subFrom
},
{
field:"submissionPeriod",
type:"<=",
value:subTo
},
]);
Oli,
Thank you for the detailed response. Since the filters are dynamic and set by the end-user (i.e. cannot be hardcoded), I created an Object and conditionally adding the filter parameters. Using this object, I can call the NIBRSTable.addFilter(userFilter) and it works like a charm! I did make the mistake of trying to JSON Stringify the object and passing it to the addFilter method, but quickly learned JSON Stringify was unnecessary since the object array was already a JSON object.
Oddly, though I am still receiving a single warning "Ajax Response Blocked" even though there were no pending Ajax actions. I only have one .addFilter() and removed the .setData() as you responded. I will ignore for now since the filtering is working!
Ben
So I have one HTML page which consists of a bunch of form elements for the user to fill out. I push all the selections that the user makes into one global variable, allTheData[] inside my only Javascript file.
Then I have a 2nd HTML page which loads in after a user clicks a button. This HTML page is supposed to take some of the data inside the allTheData array and display it. I am calling the function to display allTheData by using:
window.onload = function () {
if (window.location.href.indexOf('Two') > -1) {
carousel();
}
}
function carousel() {
console.log("oh");
alert(allTheData.toString());
}
However, I am finding that nothing gets displayed in my 2nd HTML page and the allTheData array appears to be empty despite it getting it filled out previously in the 1st HTML page. I am pretty confident that I am correctly pushing data into the allTheData array because when I use alert(allTheData.toString()) while i'm still inside my 1st HTML page, all the data gets displayed.
I think there's something happening during my transition from the 1st to 2nd HTML page that causes the allTheData array to empty or something but I am not sure what it is. Please help a newbie out!
Web Storage: This sounds like a job for the window.sessionStorage object, which along with its cousin window.localStorage allows data-as-strings to be saved in the users browser for use across pages on the same domain.
However, keep in mind that they are both Cookie-like features and therefore their effectiveness depends on the user's Cookie preference for each domain.
A simple condition will determine if the web storage option is available, like so...
if (window.sessionStorage) {
// continue with app ...
} else {
// inform user about web storage
// and ask them to accept Cookies
// before reloading the page (or whatever)
}
Saving to and retrieving from web storage requires conversion to-and-from String data types, usually via JSON methods like so...
// save to...
var array = ['item0', 'item1', 2, 3, 'IV'];
sessionStorage.myApp = JSON.stringify(array);
// retrieve from...
var array = JSON.parse(sessionStorage.myApp);
There are more specific methods available than these. Further details and compatibility tables etc in Using the Web Storage API # MDN.
Hope that helps. :)
I'm making a Chrome extension that pulls a large amount of data from an API and uses it to modify content on a page. Because the amount of data is so large, I'd like to be able to save it once in the browser and be able to access it instead of doing an API call each time a page loads.
It's my understanding this can be done by putting the API call in the background page and then calling the variable from the background page in the content script. I've also tried storing the data in local storage. Neither method is working for me and I don't understand what I'm doing wrong.
In my code in the background page, I have the API results stored in a variable. I'm calling it in the content script like this:
var background = chrome.extension.getBackgroundPage();
var myData = background.APIData; //where APIData is the variable I set in the background page
My attempt to use local storage looks like this:
//Background page
chrome.storage.sync.set({'value': APIData}, function() {
// Notify that we saved.
console.log('APIData saved to storage');
});
//Content script
var myData = localStorage["APIData"];
As of this moment, the extension isn't even loading in the page using the code where I'm trying to access local storage. The extension will load with the other method but the data doesn't seem to be there. I know my API call is working because the extension works when I put it all in the content script. But that creates the problem where I'm calling the API each time the page loads. Help please!
Examine your code closely. You are setting the data to two different bins.
First, you call chrome.storage.sync.set and set "value" to the value of APIData.
Second, you call localStorage['APIData'] which refers to a separate storage bin and property name.
Two solutions here, the first being the preferred way to set/get persistent data in a Chrome extension:
A1) set it with chrome.storage.sync.set( { value:data } )
A2) get it with chrome.storage.sync.get( null, function( storage ){ storage.value } )
B1) set it with localStorage.value = data
B2) get it with localStorage.value
I am currently using window.sessionStorage to store a uniquely generated session ID. This ID is passed to all ajax calls to the server and persists across page navigation and reloads.
This seems to work perfectly for my target browsers, with one caveat: the duplicate tab functionality in Chrome.
Duplicating the tab copies the session storage to the new tab. When the new tab loads and communicates with the server, it now has the same "unique" identifier as the duplicated target tab.
Are there any ways that I can distinguish between the two duplicated tabs?
Or are they truly duplicates and no information is different between the two?
Nothing persists after the tab/window is closed, so even a simple numeric tab id or anything would work as long as it is unique for that current instance.
A variant of this worked for me:
https://stackoverflow.com/a/60164111/13375384
The trick is to only put the ID in session storage for the brief period of time when the tab is refreshing
In my TypeScript project, the solution looked like this:
const tabIdKey = "tabIdStorageKey"
const initTabId = (): string => {
const id = sessionStorage.getItem(tabIdKey)
if (id) {
sessionStorage.removeItem(tabIdKey)
return id
}
return uuid()
}
const tabId = initTabId()
window.addEventListener("beforeunload", () => {
sessionStorage.setItem(tabIdKey, tabId)
})
Session storage is on the browser level, and not on the tab level. See https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage .
The only method I know of to identify a tab is to pass a paramater at the end of each URL (and even so you would need to use JavaScript to prevent open in new Tab).
Other methods would be unbearable, such as embedding an iframe in the app (and storing the identifier in the parent), but possible.
In short, you should rephrase you question to simply identifying a tab, and you will nevertheless not find an answer.
My eventual solution to this was to generate a unique ID on the server side which gets stored in the server session and passed back to be stored in sessionStorage. I also have another value (let's call it FOO) that is set on the client side and stored.
Pretend it looks like this in a map:
{
"unique_id" : "ABC123",
"FOO" : "some value"
}
I send the values up to the server with every ajax request.
On page load, I check to see if I already have the unique_id, FOO pair in sessionStorage and load them. Any time the value of FOO changes, it will get compared with the server pair. If it has a different value then I store the new value for FOO under a new unique ID and hand it back to the client.
This doesn't 100% solve the problem, but it does make sure that FOO, which represents some persistent state, is never used incorrectly after tab duplication.
The tab that originally had had the unique_id, FOO pair will continue to have that pair while the duplicated tab will have a new pair.
Technically, until the value of FOO changes the two tabs share the same unique_id, but as long as unique_id gets reassigned when FOO changes, they won't overwrite each other's state within the server session.
Hi firstly sorry for my bad English. I Already searched in SO. but i didn't get the exact answer i needed.
My issue is i need to synch the Ajax request. i know we can use the "asynch : false ".
but this will make browser locked. I have a folder tree(i am using "tafel tree" js) in my web. the tree nodes are generated at run-time. each time
user click a node it will send request to server and add the node to the tree.
but issue is if the page is refreshed by clicking f5 then i need to load the tree structure that i already selected previously.
i implemented it using "asynch : false ". but this will makes browser too slow.
and here what i have
function loadPage() {
/* some other codes are here*/
/* here i call an ajax for get the inside folder list in correct order.(i am usig protoype)*/
new Ajax.Request(ajaxGetInsideFolderIdsUrl,
{
parameters: {branchId: CurrentSelectfolderId},
onSuccess: function(res) {
/* onSuccess i call another function*/
var brs = res.responseText.split(","); // branch ids in correct order.
syncFolder(brs)
}
}
function syncFolder(brs){
for(var i= 0 ; i < brs.length; i ++){
var tempbranch = tree.getBranchById(brs[i].getId());
selectAfterChange( tempbranch)
/*
selectAfterChange function is used for selecting current branch. calling "tafle tree" select() function in side it.
i just created an copy of "select()","_openPopulate()" functions used in "tafle tree" and modified it with "asynch : false ".and now its working fine.
*/
}
}
function selectAfterChange(brs){
brs.chk_select();
/* for selecting the branch (created a copy of current "select()" function used in "tafle tree" js
and modified it with "asynch : false "and now its working fine.) */
showList();// for listing items in side that folder (in another Ajax page).
}
My problem is if a user opened a long branch.
And then refresh the page it will take too much time to load because of synch Ajax call.
Taking too much time is not an big issue to me. but the browser is get locked until all the request executed.
is there any other way to do this.
I'm not familiar with the tree library you're using, but in general, the way you'd solve this is to store the currently expanded path(s) in the browser (local storage, or cookies, or wherever), and when you refresh the page, load just the nodes that are visible.
Let's say that the user is currently looking at path one/two/three/four. You save that path somewhere, and then when the page loads again, you create a queue of paths to request from the back end, by splitting the path, and appending the components of the path one by one:
["one", "one/two", "one/two/three"]
You then send the AJAX request for "one", and when you get the result back, update that node in the tree, and send a request for "one/two". When that request returns, update the tree, and send the request for "one/two/three".
Depending on your design, you can then start filling in the rest of the tree with more async requests...