Skip to content
This repository has been archived by the owner on Jan 29, 2024. It is now read-only.

Use localStorage when available for rememberLanguage #3

Closed

Conversation

rewritten
Copy link

Still not tested, I don't have the right setup to test it now. If anyone can test it before I am able to, please comment, so I can consider it done.

:)

@0x-r4bbit
Copy link
Member

Thanks for the PR. Please consider the Contributing Guide.

As you can see, your code failed on build process. Also, there aren't tests submitted at all.

@0x-r4bbit
Copy link
Member

BTW: I just found this angular-local-storage module. Maybe we should consider either extracting the best bits out of it, and pack it in a custom service/provider for ngTranslate, or try contributing to angular-local-storage, so that it at least provides a test suite.

@0x-r4bbit
Copy link
Member

+1 for setting up a kind of abstraction service, which decides to use localStorage service or $cookieStore. Better for testability, better to extend and maybe there's another way of store the language key, so it woudn't be a problem to add another store service.

@rewritten
Copy link
Author

I know. I didn't mean for the PR to be finished, it's there for discussion and I haven't had time to even test it :)

El 08/04/2013, a las 21:47, Pascal Precht notifications@github.com escribió:

Thanks for the PR. Please consider the Contributing Guide.

As you can see, your code failed on build process. Also, there aren't tests submitted at all.


Reply to this email directly or view it on GitHub.

@0x-r4bbit
Copy link
Member

Ah, okay my bad :)

@0x-r4bbit 0x-r4bbit mentioned this pull request Apr 10, 2013
@0x-r4bbit 0x-r4bbit mentioned this pull request Apr 23, 2013
29 tasks
@thobias
Copy link

thobias commented Apr 24, 2013

This feature is optional right? So perhaps the dependency can be optional as well?
Right now I had to include angular-cookies in my project without any intent to use it whatsoever, just to be able to use this component.

@0x-r4bbit
Copy link
Member

@thobias Actually you're totally right! I thought the same yesterday's eve, that the rememberLanguage() actually has to be a kinda extension of ngTranslate, to not necessary depend on ngCookies. Any idea how to solve this? :)

@thobias
Copy link

thobias commented Apr 24, 2013

I just looked at the code, the problem is that there's no way to conditionally inject a module into angular runtime (at least as far as I've seen), so we can't check to see if we need to load the $cookieStore…

I'm thinking the only "solution" to this is to leave this functionality out of the component and let users create their own cookie/localStorage-solution (which is what I would have done anyway). I think that's the cleanest solution, since it also makes the component more versatile (ie. it lets people use it how they want, and not force cookies/localStorage) and more focused.

This also makes sense in larger project where the locale may be a property of the logged in user or such (in which case we already have a session with the locale in it).

@0x-r4bbit
Copy link
Member

@thobias Totally agree with you, but I think there's a way to solve this.. I mean you could at least 'overwrite' $translate service with a ngTranslate extension module. I'll research to find a better solution on that.

@ajoslin
Copy link

ajoslin commented May 3, 2013

What if you just did something as simple as this:

if (localStorageIsSupported) {
  loadRememberSystem();
} else {
  try {
    var cookieStore = $injector.get('$cookieStore');
    loadRememberSystemWithCookieStore(cookieStore);
  catch (e) {
    //no $cookieStore, no angular-cookies, no remember!
  }
}

@DWand
Copy link
Member

DWand commented May 7, 2013

As for me, words "setting up a kind of abstraction service" is exactly what is needed. I don't know how about JS and AnglularJS, but thinking out of the OOP paradigm we might simply provide a storage interface. So user will be able create custom class/service which realizes this interface (and use cookies, local storage, or whatever he wants). And also ngTranslate will not be longer depend of cookiesStorage - this dependensy will be moved to the implementation of the interface.
storage

@0x-r4bbit
Copy link
Member

Good ol' UML <3

Since we're not able to get service instances at config() phrase, we could solve this by providing an API which let's a user of ngTranslate define a storageProvider which has to implement a specific interface (which is exactly what @DWand shows us with his UML diagram).

So let's say somebody want to use any kind of storage to store a language key and let his app remember the configured language. A possible solution for that could be (similar to registerLoader) to register a storage factory function (which is then the actual provider). Something like:

app.config(function ($translateProvider) {
    // registering storage provider
    $translateProvider.registerStorageProvider(['dep1', 'dep2', function ($dep1, $dep2) {
        return {
            get: function (key) { /* ... */ },
            set: function (key, value) { /* ... */}
        };
    }]);

    // make uses of it at run() phrase
    $translateProvider.rememberLanguage(true);
});

And later on at run() phrase, the registered provider gets injected/instantiated and used instead of the concrete $cookieStore implementation.

This gives the user full control of any kind of storage. It just has to implement the defined interface.

@thobias, @knalli ?

@0x-r4bbit
Copy link
Member

@ajoslin ?

@knalli
Copy link
Member

knalli commented May 7, 2013

Yep. Perhaps we can provide some nice built-ins where the transitive dependencies have to be managed outside? Just an idea, don't now if this will even work.

angular.module('app', ['ngTranslate', 'cookieStore']).config(function($translateProvider){
  $translateProvider.registerStore('cookie');
});

Just like the XHRs, small bluprints.

@0x-r4bbit
Copy link
Member

Yeah we could do so. :)

@0x-r4bbit
Copy link
Member

Now, since this is actually a kinda breaking change, I wonder if this feature should land in 1.0.0 or if we should implement this for 0.7.0 or 0.8.0 or so.

@ajoslin
Copy link

ajoslin commented May 7, 2013

@PascalPrecht I like that idea. As @knalli said you can just have pre-registered factories for localStorage and cookies. And then the user can just register those if he wants to use them, depending on what browser he is in and if he's loaded cookies.

Probably cookieStorageFactory and localStorageFactory or something could be pre-registered.

I wouldn't call it registerStorageProvider though, as the word Provider has a kind of different meaning. Probably registerStorageFactory.

@rewritten
Copy link
Author

So, after all this discussion, my take on this is that forcing ANY interface on a storage/caching engine is not good. What if any other library needs caching too and requires another interface?

I propose to let the user configure a "getter" and a "setter" function, which would just be used by the library if possible. In the lines of the following: (by default a no-op)

angular.module('ngTranslate').value(
  'translate.config.cache', {
    get: function(){}, 
    set: function(){}
  }
);

so the user could use (in their app)

angular.module('myApp').value(
  'translate.config.cache', {
    get: function() {
      return localStorage.get("LANG");
    }, 
    set: function(val) {
      localStorage.set("LANG", val);
    }
  }
);

and the library could provide their own implementations, as an optional module, so another user could use

angular.module('myApp', ['ngTranslate', 'ngTranslate-rememberInCookie'])

and the additional dependency would just override the 'translate.config.cache' value (and use whatever dependency needed)

@0x-r4bbit
Copy link
Member

I wouldn't call it registerStorageProvider though, as the word Provider has a kind of different meaning. Probably
registerStorageFactory.

I agree.

What if any other library needs caching too and requires another interface?

This is just where registerStorageFactory() comes in. Which also has to return any kind of object which has at least a get() and a set() method.

So in some cases, it could just be used as a facade-pattern.

@0x-r4bbit
Copy link
Member

So, here is a WIP @knalli @ajoslin @DWand @rewritten bbd9c4e

@0x-r4bbit
Copy link
Member

Regarding the proposed commit above, here's a checklist (which now has to be fine tuned):

  • rename $COOKIE_KEY to something else since it's not necessarily a cookie (how about $STORAGE_KEY?)

Landed as 049512b

@ajoslin
Copy link

ajoslin commented May 9, 2013

I had a thought for your loader registration function. You technically could have it every way (json, url, or string-dependency).

function getLoader(value) {
  if (angular.isArray(value) || angular.isFunction(value)) {
    return $injector.invoke(value); //factory
  } else if (angular.isObject(value)) {
    return json(value); //json
  } else if (angular.isString(value)) {
    //if it's a string, it must be a dependency name or a url
    try {
      return $injector.get(value); //dependency name
    } catch (e) {
      //exception? our dependency doesn't exist. it must be a url!
      return url(value);
    }
  }
}

@knalli
Copy link
Member

knalli commented May 9, 2013

What if I do not want to use storage? Do we implement a NoOp/Null variant?

@0x-r4bbit
Copy link
Member

You just don't use it :)

On Thursday, May 9, 2013, Jan Philipp wrote:

What if I do not want to use storage? Do we implement a NoOp/Null variant?


Reply to this email directly or view it on GitHubhttps://github.com//pull/3#issuecomment-17655334
.

/pp Sent from Gmail Mobile

@DWand
Copy link
Member

DWand commented May 9, 2013

Hey! Just one more question ))
If there is a method useStorage() and others instead of rememberLanguage(), could we use it to fully configure the storage?
I mean something like this:
js $translateProvider.useStorage({ storage: '$cookieBlahBlahBlah', [storageKey: 'someKey'], [other params if needed] });

@DWand
Copy link
Member

DWand commented May 9, 2013

And what is the difference between
"add functionality to overwrite default $STORAGE_KEY"
and
"implement $translateProvider#setStorageId() (or setStoragePrefix())"
?

@0x-r4bbit
Copy link
Member

If there is a method useStorage() and others instead of rememberLanguage(), could we use it to fully configure the storage?

Yeah, we could actually do so. But I'd say we introduce this kind of feature when there's more you can configure. As long as we have just these few methods we don't need that configuration pattern. So in conclusion: Yes we'll do so, but not yet :)

And what is the difference between
"add functionality to overwrite default $STORAGE_KEY"
and
"implement $translateProvider#setStorageId() (or setStoragePrefix())"
?

The first is to make it possible to change the value which is stored in $STORAGE_KEY and the second is there to customize a specific prefix for the value of $STORAGE_KEY to avoid conflicts.

@0x-r4bbit
Copy link
Member

$COOKIE_KEY -> $STORAGE_KEY just landed as 049512b

@0x-r4bbit
Copy link
Member

There's now useLocalStorage() and useCookieStorage() in 71d65fc

And also useStorage(). rememberLanguage() is removed.

There's still a bug in a unit test. Maybe one of you guys could figure out what's broken there? @knalli @ajoslin

@knalli
Copy link
Member

knalli commented May 10, 2013

well, bisect it, see my comment. the specified commit was the good one. ;)

I can have a look at it later or tomorrow.

@knalli
Copy link
Member

knalli commented May 10, 2013

okay, time was there.

71d65fcf909147a3c43bbdb945eb0b22ba0ea7a1 is the first bad commit
commit 71d65fcf909147a3c43bbdb945eb0b22ba0ea7a1
Author: Pascal Precht <pascal.precht@googlemail.com>
Date:   Fri May 10 12:39:51 2013 +0200

    Add tests for new storage layers

:040000 040000 ddd5e76cff4c5e431653b9eedd3891463149c58b 3ab18ea64f6e14d2198f1a808db3da40f1b0e994 M  ngTranslate
:040000 040000 a621f954d6b7e5c760d71fae86d2d7a59e098f5a fe03e97be356e0e60e053e5c5b8387b9a4b4f060 M  test
'bisect run' erfolgreich ausgeführt

@0x-r4bbit
Copy link
Member

Yeah I know where it is and I know that's an issue with the $translateLocalStorage but I can't figure out, why it doesn't set the storageKey property on startup.

@knalli
Copy link
Member

knalli commented May 10, 2013

Because the locale storage module is constructed before you set the language. And after you set the language, the locale storage is not being refreshed.

@0x-r4bbit
Copy link
Member

Whoat? :-o

@knalli
Copy link
Member

knalli commented May 10, 2013

Instrumented some breakpoints and the thing will be clear. useLocalStorage only "sets" the storage. Nothing more.

@0x-r4bbit
Copy link
Member

Sorry I don't get it. useLocalStorage() does the same as useCookieStorage(). And in tests, when setting and getting values explicit with a 'used' localStorage, it works fine.

So yeah, useLocalStorage() really only sets the storage service, but $translate service instantiate it. So it is there and it actually does its job. Except that it not set the lang key at startup or ngTranslates run()

@DWand
Copy link
Member

DWand commented May 10, 2013

There are some errors in the current code. You use the $STORAGE_KEY instead of $storageKey. Maybe it might affect on results. Here is correct variant: 98028bb

@0x-r4bbit
Copy link
Member

@DWand This is fixed now.

@0x-r4bbit
Copy link
Member

I really can't get around this weird error.

https://github.com/PascalPrecht/ng-translate/blob/canary/test/unit/translateServiceSpec.js#L624-L630

Anybody else have an idea whats wrong there? I also logged the code at different states. Really don't get it.

@0x-r4bbit
Copy link
Member

So, the checklist is done. There is just this weird bug with useLocalStorage() left.

@knalli
Copy link
Member

knalli commented May 12, 2013

Got it, test case wasn't solid cleaned up.

@knalli
Copy link
Member

knalli commented May 12, 2013

With 625b1d6, works for me.

@knalli
Copy link
Member

knalli commented May 12, 2013

spyOn were something like that

     beforeEach(function() {
        var store = {};
        spyOn(localStorage, 'getItem').andCallFake(function(key) {
          return store[key];
        });
        spyOn(localStorage, 'setItem').andCallFake(function(key, value) {return store[key] = value + '';});

      });

@knalli knalli mentioned this pull request May 12, 2013
@0x-r4bbit
Copy link
Member

Since everything landed in canary now, this will be closed. Thanks for all your input and work. You guys are great!

@0x-r4bbit 0x-r4bbit closed this May 12, 2013
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants