Closed stramel closed 9 years ago
I have actually been thinking about this lately myself. This is possible, although I am not 100% happy with it at the moment; and yes I need to add it to the readme!
So the way to do it right now is:
// add the item to session storage
locker.setStorageDriver('session').put('sessionKey', 'sessionData');
// or add it to local storage
locker.setStorageDriver('local').put('persistentKey', 'persistentData');
without it, it will use the default provider specified via lockerProvider.setStorageDriver()
. If this hasn't been set then it will use local storage as a default.
A downside to this approach is that any subsequent operations that have not specified a driver to use, will use the previously set driver. for example:
// set the defualt driver in config
lockerProvider.setStorageDriver('session');
// then
locker.setStorageDriver('local').put('foo', 'bar');
locker.get('foo'); // will be undefined!
I do think I will push an update so that it is less wordy... so soon it will be:
locker.driver('session').get('foo', 'bar');
locker.namespace('example').put('some', 'thing');
I will also be having a play around with setting the driver/namespace via a parameter to the methods instead. We shall see :smile:
Thanks!
@tymondesigns You could return an object with overridden settings in the chainable interface.
locker.setStorageDriver('local');
locker.driver('session').get('foo'); //< From sessionStorage
locker.get('foo'); //< From localStorage (set by setStorageDriver)
Example implementation (no input check and error handling for simplicity):
// Each factory creates a driver with options bound to it:
var driverFactories = {
local: function (options) {
options = options || {};
options.namespace = options.namespace || '';
return {
get: function (key) {
return JSON.parse(localStorage.getItem(options.namespace + key));
},
set: function (key, value) {
localStorage.setItem(options.namespace + key, JSON.stringify(value);
return this; //< Chain continues here.
},
/** Non-destructive API continues here. **/
namespace: function (ns) {
// Clone the options:
var o = {};
for (var x in options) { o[x] = options[x]; }
// Update the new options object:
o.namespace = ns;
// Create new driver with updated options:
return driverFactories.local(options);
},
/** Destructive API to alter existing driver options. */
setNamespace: function (ns) {
// Update the current options object:
options.namespace = ns;
return this; //< May be chained, too, if needed.
}
};
},
session: function (options) {
// ...
}
};
// Pre-created drivers with default options:
var drivers = {
local: driverFactories.local(),
session: driverFactories.session()
};
// Current driver for destructive API:
var currentDriver = drivers.local; //< Defaults to localStorage
// Locker service API:
var lockerApi = {
/** Non-destructive API starts with a pre-created driver. */
driver: function (driver) {
return drivers[driver];
// The pre-created driver options may be altered
// via its own destructive API, see above.
},
/** Destructive API alters the environment. */
setStorageDriver: function (driver) {
currentDriver = drivers[driver];
return this; //< May be chained, too, if required.
},
/** Destructive API uses the driver from the environment. */
get: function (key) {
return currentDriver && currentDriver.get(key);
},
// ...
};
@sompylasar Thanks, you've definitely given me some ideas there. I will try to come up with an elegant solution over the next few days. My aim is to try to keep the package as lightweight as possible whilst still providing the necessary functionality.
Happy to say that I've managed to get this to work as I wanted!
The code sits over on the develop branch at the moment, so feel free to take a look :smile: The changes are pretty drastic!
I welcome any feedback of course
@tymondesigns A little code review for you.
The overall architecture of drivers/resolve looks good.
General note: the error reporting to the user app's code is missing (there are only console warnings that only help at the development stage, not in production).
https://github.com/tymondesigns/angular-locker/blob/develop/src/angular-locker.js#L183
The value
could be boolean false
; instead compare with undefined
and null
.
https://github.com/tymondesigns/angular-locker/blob/develop/src/angular-locker.js#L348
Access localStorage
via window
, the same way in every place (like here: https://github.com/tymondesigns/angular-locker/blob/develop/src/angular-locker.js#L47 and here https://github.com/tymondesigns/angular-locker/blob/develop/src/angular-locker.js#L367 ).
https://github.com/tymondesigns/angular-locker/blob/develop/src/angular-locker.js#L348
Should sessionStorage
be also checked? Maybe should each driver be checked in its own way.
https://github.com/tymondesigns/angular-locker/blob/develop/src/angular-locker.js#L367
The Locker
constructor does not check for the driver
existence, so it may fail with a runtime exception later on when used. Better to fail earlier with a warning that certain type of storage is not supported.
Thanks for reading.
Thanks for that, I have made some of those changes:
The supported()
method can now take local
or session
as a param (will default to local). And I am using the same support detection technique as Modernizr (see docblock link).
As for error reporting, yes you are right.. I think I will create an error handler that will respect a debug mode that can be set via lockerProvider
, and only log out to the console if debug mode is on etc.
I suppose it would be good to use Angular's exceptionHandler
for this, and could then use the $log
provider within it.
In relation to your final point. Do you mean that browser support should be checked first? Since an error will be thrown if support for Storage isn't there.
Finally do you have any feature requests at all?
Maybe the ability to bind values to a $scope
or events being triggered when things are added/removed, or even the ability to add a custom driver: e.g. lockerProvider.addCustomDriver('customDriver', {});
@tymondesigns Thanks for your attention.
Good point about exceptionHandler
, would be nice to have support for it as fas as this is an Angular library.
About my final point. Assume there is no window.localStorage
at all (it's undefined
), but in the provider you create a Locker
and pass the value of window.localStorage
property which is undefined
to the constructor without a check (neither from where you pass, nor inside the constructor). Then you put the value into the _driver
. Then you use the _driver
assuming it is defined and is an object with certain methods.
About feature requests. Currently I'm not doing any Angular projects so I cannot image what could be useful (I used ngStorage
before, that's how I stumbled upon angular-locker
).
Anyways, it would be good to have support for storage events to receive storage updates that happen in other windows and in iframes on the same page (the event should be handled for both localStorage
and sessionStorage
).
@sompylasar Thanks for your input. I understand you perfectly now about your final point.
I will add the error handling stuff soon, and figure out what features I think could be added.
@stramel I will tag a release soon :smile:
Ok, just tagged a new release as 1.0.0 as I would like to follow semver properly from now on.
@stramel feel free to give it a whirl :smile:
@tymondesigns Thanks for the quick turn around! I am about ready for a major release but after that I will work to get it implemented. Thanks again!
First off, great library and I would love to use it. The only problem is that I am using both Local and Session Storage in my project. I don't see a way to implement both using the instructions in the README and nothing is coming to mind for an easy way to do it.
Is there an easy way to do this that I am missing?