Home

Awesome

Angular Storage

Decorators and services for cookies, session- and localStorage

This library adds decorators that make it super easy to automagically save and restore variables using HTML5's localStorage and sessionStorage. It also provides Angular-Injectable Session- and LocalStorageService.

What's included?

CHANGELOG

v3.1.0:

v3.0.0:

v2.1.0 - support for Angular 7 & TypeScript 3

v2.0.0 - support for Angular 6 (RxJS v6)

v1.4.x:

Upcoming (TODO)

Installation

Versions 3+

Just run: ng add ngx-store. Done!

Older versions

  1. Download the library: npm i ngx-store --save
  2. Import the WebStorageModule in your app.module.ts:
    import { NgModule } from '@angular/core';
    import { WebStorageModule } from 'ngx-store';
    
    @NgModule({
      imports: [
        WebStorageModule.forRoot(),
      ],
    })
    export class AppModule {}
    

Configuration

Things you should take into consideration while configuring this module:

As this project uses decorating functions, it is important to provide custom configuration in global variable named NGXSTORE_CONFIG before Angular application load. Here are some ways to do it:

  1. Add <script> in index.html (before Angular sources)
    <script>
    var NGXSTORE_CONFIG = {
      prefix: 'ngx_',      // default: 'ngx_'
      clearType: 'prefix', // default: 'prefix'
      mutateObjects: true, // default: true
      debugMode: false,    // you can enable debug logs if you ever meet any bug to localize its source
      cookiesScope: '',    // what you pass here will prepend base domain e.g. "." => .domain.com (all subdomains)
      cookiesCheckInterval: 0, // number in ms describing how often cookies should be checked for changes, 0 = disabled
      previousPrefix: 'angular2ws_', // you have to set it only if you were using custom prefix in old version ('angular2ws_' is a default value)
    };
    </script>
    
  2. If you use webpack, you can provide global variable in your webpack.js file this way:
    plugins: [
      new webpack.DefinePlugin({
        NGXSTORE_CONFIG: JSON.stringify({
          prefix: '', // etc
        })
      }),
    ]
    

Decorators config

Decorating functions can take config object with the following fields:

Usage

  1. Pretty easy to use decorators. Here is where the real magic happens.

    import { CookieStorage, LocalStorage, SessionStorage } from 'ngx-store';
    
    export class MySuperComponent {
      // it will be stored under ${prefix}viewCounts name
      @LocalStorage() viewCounts: number = 0;
      // this under name: ${prefix}differentLocalStorageKey
      @LocalStorage('differentLocalStorageKey') userName: string = '';
      // it will be stored under ${prefix}itWillBeRemovedAfterBrowserClose in session storage
      @SessionStorage({key: 'itWillBeRemovedAfterBrowserClose'}) previousUserNames: Array<string> = [];
      // it will be read from cookie 'user_id' (can be shared with backend) and saved to localStorage and cookies after change
      @LocalStorage() @CookieStorage({prefix: '', key: 'user_id'}) userId: string = '';
      // it will be stored in a cookie named ${prefix}user_workspaces for 24 hours
      @CookieStorage({key: 'user_workspaces', expires: new Date(new Date().getTime() + 24 * 60 * 60 * 1000)}) userWorkspaces = [];
      // the value will be transferred from localStorage's "location" key
      @LocalStorage({key: 'myLocation', migrateKey: 'location'}) myLocation: string = '';
    
      constructor() {
        this.viewCounts++;
        this.userName = 'some name stored in localstorage';
        this.previousUserNames.push(this.userName);
        for (let userName of this.previousUserNames) {
          console.log(userName);
        }
        this.previousUserNames.map(userName => userName.split('').reverse().join(''));
      }
    }
    

    Sharing variables across classes: Decorated variables can be easily shared across different classes, e.g. Angular Components (also after their destruction) without need to create new service for this purpose.

    import { LocalStorage, SharedStorage } from 'ngx-store';
    
    export class HomeComponent {
      @SharedStorage() title: string = 'Homepage'; // it will be kept in temp memory until app reload
      @LocalStorage() userNote: string = 'Leave your note here'; // it will be read from and saved to localStorage
    
      constructor() {
        setTimeout(() => {
          console.log('userNote:', this.userNote); // it should be changed after user's visit to NestedComponent
        }, 5000);
      }
    }
    
    export class NestedComponent {
      @SharedStorage('title') homeTitle: string = '';
      @LocalStorage() protected userNote: string = '';
    
      constructor() {
        console.log('homeTitle:', this.homeTitle); // should print 'Homepage'
        console.log('userNote:', this.userNote); // should print userNote set in HomeComponent
        this.userNote = "You've visited NestedComponent!";
      }
    }
    

    Force save changes: If you need to modify stored object by not a direct assignment, then you can take advantage of .save() method to force save made changes. Example:

    import { CookieStorage, LocalStorage, SessionStorage, WebstorableArray } from 'ngx-store';
    
    export class MySuperComponent {
      @LocalStorage() someObject: any = { c: 3 };
      @SessionStorage() arrayOfSomethings: WebstorableArray<number> = <any>[0,1,2,3,4];
      @CookieStorage({ mutate: false }) someCookie: {version?: number, content?: string} = {};
    
      constructor() {
        this.someObject.a = 1;
        this.someObject['b'] = 2;
        delete this.someObject['c'];
        for (let i = 0; i < this.arrayOfSomethings.length; i++) {
          this.arrayOfSomethings[i] += i;
        }
        this.someCookie.version++;
        this.someCookie.content = 'please save me';
        // upper changes won't be saved without the lines below
        this.someObject.save();
        this.arrayOfSomethings.save();
        this.someCookie = this.someCookie; // it looks weird, but also will do the job even without object mutation
       }
    }
    

    Limited lifecycle classes in AoT compilation: There is a special case when Service or Component in your application containing decorated variable is being destroyed:

    import { OnDestroy } from '@angular/core';
    import { LocalStorage } from 'ngx-store';
    
    export class SomeService implements OnDestroy { // implement the interface
        @LocalStorage() destroyedVariable: any = {};
    
        ngOnDestroy() {} // event empty method is needed to allow ngx-store handle class destruction
    }
    
  2. Use the services to manage your data:

    import { CookiesStorageService, LocalStorageService, SessionStorageService, SharedStorageService } from 'ngx-store';
    
    export class MyService {
      constructor(
        localStorageService: LocalStorageService,
        sessionStorageService: SessionStorageService,
        cookiesStorageService: CookiesStorageService,
        sharedStorageService: SharedStorageService,
      ) {
        console.log('all cookies:');
        cookiesStorageService.utility.forEach((value, key) => console.log(key + '=', value));
      }
    
      public saveSomeData(object: Object, array: Array<any>) {
        this.localStorageService.set('someObject', object);
        this.sessionStorageService.set('someArray', array);
    
        this.localStorageService.keys.forEach((key) => {
          console.log(key + ' =', this.localStorageService.get(key));
        });
      }
    
      public clearSomeData(): void {
        this.localStorageService.clear('decorators'); // removes only variables created by decorating functions
        this.localStorageService.clear('prefix'); // removes variables starting with set prefix (including decorators)
        this.sessionStorageService.clear('all'); // removes all session storage data
      }
    }
    

Note: Always define default value at the property you are using decorator.

Note: Never use for-in loop on decorated Arrays without filtering by .hasOwnProperty().

Note: Please don't ngx-store circular structures as this library uses JSON.stringify to encode data before saving.

Note: When you change prefix from '' (empty string) old values won't be removed automatically to avoid deleting necessary data. You should handle it manually or set clearType to 'all' for some time.

Contributions are welcome!