Detecting if there is a change in a localStorage variable - javascript

I have a case where I need to pass a variable from a modal back to the main screen. I have decided the cleanest way to do so is to store that value in localStorage so that I can pass it back to the main file and then display it on the main page.
Is there a way to listen to the localStorage item being changed? I've been searching, but only have found information getting and setting the variable.
My item looks like this.
var length = $('.table').find('tbody').find('tr').length;
localStorage.setItem('length', length);

There's a storage event on window object for local storage changes.
MDN local storage api
So you can watch the changes. But, if I am correct, that would work only for the same domain frames. So if they are, this is your choice.

you can use a global scope like define it out of function and it will be one global scope.
var globalVariable={
x: 'globalval'
};
or use...
window.globalvar1='test' //it will set to global.
that way it will be bind to window scope.

when you set the item, always first do a get and compare with that value,
if value is changed notify, otherwise set.
var length = $('.table').find('tbody').find('tr').length;
if(localStorage.getItem(length)){
localStorage.getItem(length)!=length?notifyChange():
}

Note: This is an expensive and overly complicated approach but meets your needs for pure localStorage.
If you must use localStorage the only way I can think of that you can watch/listen for a variable in localStorage is like so
setInterval(function(){
var modalValue = localStorage.getItem( 'length' );
if( modalValue ){
// use the value
// remove the value when you are done so that this code doesn't run every time
localStorage.setItem( 'length', null );
}
}, 100 );
This will check for the variable every 1/10 of a second and run the code when it's set.
You mentioned in a comment that it is complicated and that their are iframes involved. Perhaps you can leverage the messaging api to better meet your needs?

Don't use localstorage for critical functionality. localstorage is blocked completely when using private browsing on certain browsers.
Only use localstorage for non-critical enhancements.
There is no callback for localstorage, so you wont be able to monitor changes as they happen. You could however pull the value on page load, store the value in your execution context and watch for that value to change.
Otherwise, to do what you want will require using a database.

Don't do this, find a mechanism of dependency injection that will let you pass the the variable in a sane manner. Do that either with something akin to Angular.js' service/factory pattern, or a React store.
Leveraging a mechanism like that will also allow you to listen for changes to whatever you're storing. In fact, that's the whole point of things like RxJS Observables which are at the core of Angular2+'s framework.
Additionally, something like couchdb would let you persist this data asynchronously and still not have the drawbacks of coordinating globals through raw localstorage.
Allowing for uncontrolled access to some global variable is just asking for a defect.

Related

Make Meteor Reactive to Specific Subitem of Meteor.user()

What I'm Trying to Do...
I need to use some subproperties which are stored in the user's Meteor.user() object, such as Meteor.user().profile.preferences.preference_one, Meteor.user().profile.preferences.preference_two, et cetera. These subproperties are being used inside reactive autorun blocks because there are recalculations that must be done anytime they change.
My Problem Is...
I've discovered that when I refer to the value of these subproperties from within a reactive block, then the autorun is fired for any change to the Meteor.user() object, including changes which do not affect in any way the data that I am explicitly referencing. For example, if Meteor.user().profile.name is updated, then any autorun that includes Meteor.user().profile.preferences.preference_one or Meteor.user().profile.preferences.preference_two gets fired as well, because they all have a common parent.
I have seen a similar question dealing with limiting the scope of Meteor's reactivity, but it deals with a custom collection, not the Meteor.users collection. I cannot see how the solution there could be made applicable because they are specifying fields in subscriptions to limit what subproperties are published to the client, and in my case, I need all the subproperties of Meteor.user(). But I need to be able to choose which subproperties I am reacting to!
Storing subproperty values locally and then comparing on every change would of course work, but it is a brute force solution solution in that it requires extra logic and that the autoruns will all be firing anyway.
I don't know if this is the best way, but have a look at this example:
Tracker.autorun(function() {
var user = Meteor.user();
if (user && user.profile)
Session.set('p1', user.profile.preference1);
});
Tracker.autorun(function() {
var p1 = Session.get('p1');
console.log("p1 is " + p1);
});
The first autorun will fire every time the user data changes, however the second autorun will fire only when that particular property changes.
David's solution is great (as always).
Just to offer some variety, I'd suggest moving your preferences (or the whole darn profile) to its own collection. Then, use a .publish(null,... to always have access to that collection.
Either solution will work great, it is simply my preference to have nothing except login credentials attached to the critical users collection.

How can I clone a <browser> element in Firefox using XUL?

I am developing a firefox extension where I need to save the state of an arbitrary web page in order to be able to restore that webpage later. The quirk is that I need to restore the entire state of the page, including the state of all javascript variables. The "saving" can be done in memory, it doesn't need to be serializable.
So, is there a way to exactly clone a browser element, so that it starts running from the same point of execution that the original is currently at?
If not, how much effort would it require to add this to firefox (using C++), and which files and documentation would I start looking at?
No, there isn't a way to do exactly what you want. Even the built-in session restore will only restore form fields (and some other selected things), but not the full JS and native object state.
Implementing something like this yourself not feasible (and would be also a massive task):
You could uneval() most js objects, but that will loose type information and you'll only get the source, but not any internal state (think "hidden" state via closures). Native objects like window or document need some special treatment, and getting the internal state isn't exactly always possible here without some C++-level "reflection".
You could potentially get a lot of the actual state using the debugger API in new ways, however I don't see any way to actually restore it later. And "a lot" is still not the same as "all".
About the closed-over "hidden" state:
There is no way I know of to reliably get the internal state of counter in the following example, let alone restore it later, without getting as low-level as a platform-dependent full memory dump.
var count = (function() {
var counter = 0;
return function() { return ++counter; };
})();
count();
count();
I guess that you could walk the properties of all objects and save them somewhere but preserving context of e.g. bound functions would be difficult. Maybe you could make some use of the session store?
See:
Session_store_API and nsISessionStore

Google apps script: how to persist data in spreadsheet between different function calls?

In a Google spreadsheet using the Script Editor, I do function calls, but I am not quite sure if the best way to store persistant data (data that I will continue to use) is to use global variables (using objects, arrays, strings), or there is a better way to store data.
I don't want to use cells which could be another way.
Another question, is it possible to create (pseudo) classes in this environment? Best way?
Both ScriptProperties and ScriptDB are deprecated.
Instead, you should be using the new class PropertiesService which is split into three sections of narrowing scope:
Document - Gets a property store that all users can access within the current document, if the script is published as an add-on.
Script - Gets a property store that all users can access, but only within this script.
User - Gets a property store that only the current user can access, and only within this script.
Here's an example persisting a user property across calls:
var properties = PropertiesService.getScriptProperties();
function saveValue(lastDate) {
properties.setProperty('lastCalled', lastDate);
}
function getValue() {
return properties.getProperty('lastCalled');
}
The script execution environment is stateless, so you cannot access local variables from previous runs, but you can store getScriptProperties() in a local variable because it will be re-run for each return trip to the server so it can be called in either method.
If you need to store something on a more temporary basis, you can use the CacheService API
Persistent data can be stored using the Class ScriptProperties:
http://code.google.com/googleapps/appsscript/class_scriptproperties.html
All values are stored as a string and will have to be converted back with the likes or parsInt or parseFloat when they are retrieved.
JSON objects can also be stored in this manner.
My experience has been that every query to retrieve or store values takes a long time. At the very least, I would cache the information in your javascript code as much as possible when it is safe. My scripts always execute all at once, so I don't need to keep global variables as I simply pass the retrieved data arrays around, manipulate them, and finally store them back in one fell swoop. If I needed persistence across script invocations and I didn't care about dropping intermediate values on close of the webpage, then I'd use globals. Clearly you have to think about what happens if your script is stopped in the middle and you haven't yet stored the values back to Google.

HTML5 localStorage: Is my code following correct standards?

OK, so I have created an HTML5 canvas game that uses localStorage. I have localStorage working perfectly but I'm not sure that it follows the correct standards, or if it is completely fine the way that I have written it.
//check if storage is set already, if not set the variable
function checkLocalStorage(){
if (localStorage.timesPlayed){
timesPlayed = Number(localStorage.timesPlayed);
}
else{
timesPlayed = 0;
}
}
//code snippet to increase my variable
timesPlayed += 1;
//code snippet to set the localStorage
localStorage.timesPlayed = timesPlayed;
This works perfectly!? But from what I have read, i should be using localStorage.getItem and localStorage.setItem?
Should I change the way I write localStorage??
This is just the link to the website where I learned this way to access localStorage
http://hacks.mozilla.org/2009/06/localstorage/
It works, and it probably won't break, but I'd still try to cast things in the correct type when using localStorage. getItem and setItem are the preferred ways of doing things, but the thing that jumped out at me was that your methods of getting and setting the value won't work for any type but a number, which means you have to code cautiously if you're using a lot of localStorage.
You're sending a number to localStorage, where it's converted to a string. This can get you into a lot of trouble when dealing with booleans, for example:
var won = false;
localStorage.isWinner = won;
if (localStorage.isWinner) { // always true
alert("You won!");
}
Arrays and objects get ugly too:
localStorage.test = {foo: "bar", dog: "cat"};
// returns "[object Object]"
In other words, what you have works, but it's a good habit to do things correctly and consistently from the start, as it will simplify debugging later. The correct way -- which works with strings, numbers, arrays, objects -- is this:
localStorage.setItem("key", JSON.stringify(value));
JSON.parse(localStorage.getItem("key"));
// or as a de facto alternative
localStorage.key = JSON.stringify(value);
JSON.parse(localStorage.key);
I don't think the array access notation for localStorage is part of the standard, but most browsers that implement will probably allow for that possibility. If you want to be especially careful, then use getItem and setItem - but personally, I don't foresee this being a problem.
Mozilla says:
Although the values can be set and read via the standard JavaScript property access method usage of getItem and setItem methods is recommended.
http://dev.w3.org/html5/webstorage/#storage-0
Even the examples in that draft they use the property access notation, so I think you're OK.
For a small application, direct reading and writing to localStorage is probaby OK, however I think in a more complex application it would be a good idea to wrap localStorage and provide an API with get and set methods suitable for the particular application (e.g. get/setMySpecialObject and get/setMySpecialValue).
I don't think an application should care about where data is stored, it should just call an API to get or set it. The code behind the API works out how to store it and where to get it from. Genearlly it's most efficient to read and write data into an object and read/write to localStorage behind the scenes. Get data from localStorage the first time it is requested and cache it. If it's updated along the way, write back to localStorage as appropriate, it should not require commands from the application to do so.

Javascript global namespace and dynamic property question

Say I want to have a global object which can be visible and accessible across pages(??)
// core.js
var MyLib = {}; // global Object cointainer
MyLib.value = 1;
If I define this way, then I can have access to MyLib.value in other places as long as I load the core.js.
However, if I want to add new property to object MyLib in somewhere else, say:
//extra.js
MyLib.otherVal = 2;
Then I try to access MyLib.otherVal from a different place, it is not available. I probably have some fundamental misunderstanding on how this suppose to work. I hope someone can enlighten me.
After reading the comments, I realized the scope I want is indeed across pages. Is that even possible?
thanks
Oliver
If you want to carry data across pages, there are really three major methods:
LocalStorage. See this page for a fairly thorough explanation of the concept, how to use it, and so forth. Here is a library dealing with JavaScript storage.
Cookies. Cookies can store 4KiB of data, but some users disable them.
window.name. You can store up to 2MiB of data in window.name. Here is a library that focuses on storing data in window.name; it seems fairly well-written.
You could potentially write an app to take advantage of all three of these techniques, starting with LocalStorage and falling back to window.name if all else fails.

Categories

Resources