8

I would like to keep my cache keys in a central place.I would like to do it as a constant file.At this moment I have declared cache keys each and every page where it's required.But I need to remove that duplication.How can I do that?

One of cache key declaration:

purchasedOfflineArticlesKey: string = 'myLibraryPurchasedOfflineArticles';

Can you tell me a proper design for this? Do I need to create a class for this and use that class in other places where it needs or any other way? Hope you'll give feedback for this.

Update:

I have cache service like below.

local-cache-service.ts

import { Injectable } from '@angular/core';
import { CacheService } from "ionic-cache";

@Injectable()
export class LocalCacheServiceProvider {

  constructor(private cache: CacheService) { }

  //get Item
  getItem(key: string): Promise<any> {
    return this.cache.getItem(key);
  }
}

I have used it like this:

offline-articles.ts

private purchasedOfflineArticlesKey: string = 'myLibraryPurchasedOfflineArticles';

 constructor(private localCacheService: LocalCacheServiceProvider) {
  }

     ionViewDidLoad() {
        this.localCacheService.getItem(this.purchasedOfflineArticlesKey).then(values => {
          this.arrangeMyOfflineArticles(values);
        }).catch(reason => {
        });
      }
Sampath
  • 63,341
  • 64
  • 307
  • 441
  • 1
    Please take a look at [this SO answer](https://stackoverflow.com/questions/39576991/ionic2-angular2-read-a-custom-config-file/39577841#39577841), there you can find the angular recommended way to deal with static config files. A few things have changed (we don't use the providers array from the component now, but the same should be done in the NgModule instead). – sebaferreras Aug 03 '17 at 10:38
  • 1
    You don't need classes for something that cannot benefit from class instantiation. Cache keys likely should be declared in the place where cache service is declared. If you don't have centralized cache service, maybe that's the real problem. – Estus Flask Aug 03 '17 at 10:55
  • Yes, I do have.Thanks a lot for the info.Hope you can put that as an answer too @estus – Sampath Aug 03 '17 at 11:05
  • The answer could be more informative if the question contained information how these constants are used in the app. – Estus Flask Aug 03 '17 at 11:07
  • @estus but in the proposed approach (flagged as duplicated), we are not using _Clases_. In fact, the section of the [Angular docs related to this](https://angular.io/guide/dependency-injection#non-class-dependencies) is called _Non-class dependencies_. – sebaferreras Aug 03 '17 at 11:09
  • @estus I have done that.Please see it. – Sampath Aug 03 '17 at 11:12
  • 1
    @sebaferreras Hope you'll remove the `duplicate flag` hence now we have a more elegant solution as @estus proposed. – Sampath Aug 03 '17 at 11:15
  • I wouldn't say that it's more _elegant_, it's just that those keys are (conceptually) part of the same service, and thus should be stored inside of that service. But if the issue is related to creating/handling static configurations (used among the entire app, like analytics keys, api urls, and so on), the way *Angular* proposes to do it, is how it is done in the answer from the first comment. That being said, I've removed the duplicated flag :) – sebaferreras Aug 03 '17 at 11:22
  • 1
    Yes,you're right.I'll see your reference too.But my use case is a sub set of what you have suggested and can do it more easily.But it was my bad hence I didn't give more info about my use case.Thanks for removing Duplicate flag :) @sebaferreras – Sampath Aug 03 '17 at 11:26

3 Answers3

9

In one of my projects, I have defined a namespace for this purpose in a file named constants.ts. You can do the same. Below is some sample code:

export namespace AppConstants
{
    // Class for general global variables.
    export class General
    {
       public static readonly WELCOME_TITLE = 'Welcome to my App';
    };
}

In my app where I want to use the constants, I am importing this namespace:

import { AppConstants } from './core/common/constants';

I can access these constants like:

myMethod(){
    console.log(AppConstants.General.WELCOME_TITLE);
}
FAISAL
  • 33,618
  • 10
  • 97
  • 105
4

Classes shouldn't be used for something that cannot benefit from class instantiation.

One option is to have them as exports in separate file, this way non-existent keys can be resolved on import:

export const foo = 'foo';
export const bar = 'bar';

Alternatively, cache keys can be declared as object keys, e.g. in the place where cache service is declared.

export const CACHE_KEYS = {
    foo: 'foo',
    bar: 'bar'
};

@Injectable()
export class LocalCacheServiceProvider {
  constructor(private cache: CacheService) { }

  getItem(key: keyof typeof CACHE_KEYS): Promise<any> {
    return this.cache.getItem(key);
  }
}

The service can make use of keyof constraint to limit the keys to known ones.

Depending on the design of the application, LocalCacheServiceProvider and key set can be extended per module/component to provide unique set of keys for the unit.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • What if I'll declare `public` constants on the service like this `readonly purchasedOfflineArticlesKey: string = 'myLibraryPurchasedOfflineArticles';`? – Sampath Aug 03 '17 at 11:39
  • `keyof` constraint will be messed up then, it includes class methods, too. You won't be able to reach the constants from the outside, it should be `static readonly purchasedOfflineArticlesKey` then. Having it in separate object looks cleaner to me. – Estus Flask Aug 03 '17 at 11:47
  • Why do I need a `static` key word? Hope I can declare `constants` only by using `readonly` no? My idea is to not to use `CACHE_KEYS` object.Your thoughts? – Sampath Aug 03 '17 at 11:52
  • This way you can do `localCacheService.getItem(CACHE_KEYS.foo)`. That's how constants are usually used. Cache keys and service methods cannot be distinguished if they will be class properties. `localCacheService.getItem('getItem')` won't throw the error. – Estus Flask Aug 03 '17 at 12:01
  • I just did this.Inside the service where I have declared a public constant as : `readonly purchasedOfflineArticlesKey: string = 'myLibraryPurchasedOfflineArticles';`. Then the page where I need it did like this: `this.localCacheService.getItem(this.localCacheService.purchasedOfflineArticlesKey).then(values => { this.arrangeMyOfflineArticles(values); }).catch(reason => { });`.Simple no? Any issues? – Sampath Aug 03 '17 at 12:10
  • `this.localCacheService.purcha‌​sedOfflineArticlesKe‌​y` is long enough. In this setup you can do `this.localCacheService.getItem(this.localCacheService.getItem)` and this won't cause type error, this indicates a design mistake. Usually constants don't pushed into a class that uses them if they don't have to, and here there's no good reason to do that. You asked about a proper design. Pushing them into the class doesn't look proper. Also, this is more verbose. – Estus Flask Aug 03 '17 at 12:18
  • Make sense.Thanks a lot :) – Sampath Aug 03 '17 at 12:41
0

I had a similar issue. Those are the options i tried so far. i personally tend to option 1 and 3.

my-constants-file.ts option 1:

export const MyConstants = {
  NEW_VERSION: "2.0.0",
  OLD_VERSION: "1.1.0"
} as const; // makes fields readonly!

my-constants-file.ts option 2:

export class MyConstants {
  static readonly NEW_VERSION = "2.0.0";
  static readonly OLD_VERSION = "1.1.0";
}

my-constants-file.ts option 3:

export enum MyConstants {
  NEW_VERSION = "2.0.0",
  OLD_VERSION = "1.1.0",
}

use constant in other module file:

import { MyConstants } from './my-constants-file';

var version = MyConstants.NEW_VERSION;
Welcor
  • 2,431
  • 21
  • 32