Undefined NULL - by Shidhin

Scribbles of a UI developer, JavaScript enthusiast

AngularJS: Real Time Model Persistence Using Local Storage

| Comments

Last week, I came across a situation while working on a simple AngularJS application. I’d to sync the data between multiple open tabs. As you guess, the easy way to sync data between multiple tabs is through local storage. Ofcourse there are better ways of doing this using websockets; but here I am going to explain the client-side way –– using local storage and real time updating other tabs.

The Code

I created a demo app in Plunker. Code is given below

index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
  <script  src="http://code.angularjs.org/1.2.13/angular.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>
<body ng-controller="MaintCtrl as m">
  <h1></h1>
  <h2>Current LocalStorage value is = <em></em></h2>
  <input type="text" ng-model="m.value">
  <input type="button" value="update" ng-click="m.update(m.value)">
</body>
</html>

And the JavaScript :

script.js
var app;
app = angular.module("app", []);
app.controller("MaintCtrl", function(LS) {
  this.greeting = "This is a localstorage demo app";
  this.value = LS.getData();
  this.latestData = function() {
    return LS.getData();
  };
  this.update = function(val) {
    return LS.setData(val);
  };
});
app.factory("LS", function($window, $rootScope) {
  return {
    setData: function(val) {
      $window.localStorage && $window.localStorage.setItem('my-storage', val);
      return this;
    },
    getData: function() {
      return $window.localStorage && $window.localStorage.getItem('my-storage');
    }
  };
});

The above shows a simple application that stores data in local storage. This works well when there’s only one tab. Our challenge is to ensure the view is same when the application is opened in multiple tabs as well.

The Technique

Local storage provides an event called storage on window object. Whenever the localstorage gets updated, all the event listeners attached to storage gets invoked. The first parameter to the handler function is an event object which contains an event.key pointing to the local storage value changed.

angular.element($window).on('storage', function(event) {
    if (event.key === 'my-storage') {
      $rootScope.$apply();
    }
  });

$rootScope.$apply(), will trigger a digest cycle from root scope, and all the views will be updated with the new model value.

Final JavaScript :

var app;
app = angular.module("app", []);
app.controller("MaintCtrl", function(LS) {
  this.greeting = "This is a localstorage demo app";
  this.value = LS.getData();
  this.latestData = function() {
    return LS.getData();
  };
  this.update = function(val) {
    return LS.setData(val);
  };
});
app.factory("LS", function($window, $rootScope) {
  angular.element($window).on('storage', function(event) {
    if (event.key === 'my-storage') {
      $rootScope.$apply();
    }
  });
  return {
    setData: function(val) {
      $window.localStorage && $window.localStorage.setItem('my-storage', val);
      return this;
    },
    getData: function() {
      return $window.localStorage && $window.localStorage.getItem('my-storage');
    }
  };
});

Demo

Try opening the following link in multiple tabs. Update the input text from different tabs, and you can see them updating the other tabs as well.

Angular LocalStorage Demo

Comments