Automatic saving of ng model values

The current question is a logical continuation of this question

annotation

Hi I am trying to automatically save values ng-model

based on history step. This way, when a person clicks the browser back / forward button, they get the same values โ€‹โ€‹in their inputs / checkboxes / etc. which were there before he left (including user changes).

I am using browser session storage (Web Storage) to store values ng-model

. this kind of storage works on a tab / session basis in a session, so it's absolutely okay to use it with this kind of problem.

Through 'ui-router' I can bind each user navigation to a unique UUID (GUID) which is later used to retrieve bucket

which contains all the stored values $scope

.

I even go out with an implementation that (partially) works, you can find it at the bottom of this question.

Problem

I am currently having a problem keeping values โ€‹โ€‹across the entire scope. I currently use a key ng-model

as a key for a bucket, for example, in ng-model="city.name"

I use "city.name"

as a key for my bucket. In this example, the resulting bucket that goes into the session store would look like

var bucket === {
    "city.name": "Some city name"
}
...
$window.sessionStorage.setItem(viewId, bucket);

      

Obviously, if we iterate over the list of cities, we can get a dozen of these keys. To solve this problem, I need a type id

that uniquely identifies the current location ng-model

within the entire structure $scope

.

I cannot use it $scope.$id

for this purpose because it changes every time the region is recreated. So, one of the ideas I came across is to calculate the current relative position $scope

in the $ scope tree using a property $scope

$$prevSibling

and concatenate it with the same position as its parent and parent parent until we reach $ rootScope. this is how i see it:

$rootScope.$index = function() {
    return this.$$index || (this.$$index = !this.$$prevSibling ? 0 : this.$$prevSibling.$index());
};

$rootScope.$uid = function() {
    return this.$$uid || (this.$$uid = (!this.$parent ? '' : this.$parent.$uid()) + ',' + this.$index());
};

      

The call $scope.$uid()

will return a string value like this '0,4,6,2,1,123'

that uniquely identifies the current position $scope

in the scope hierarchy. This value can be combined with a key ng-model

and will have the same bucket

{
    "0,4,6,2,1,1-city.name": "Some city name 1"
    "0,4,6,2,1,2-city.name": "Some city name 2"
    "0,4,6,2,1,3-city.name": "Some city name 3"
    ...
    "0,4,6,2,1,123-city.name": "Some city name 123"
}

      

Question

The implemented implementation will obviously fail when $scope

dynamically creating / destroying during "navigation", in other words it $scope

$$prevSibling

may change. I'm not sure if $ scopes can be added (which will index f * ck and break everything apart), but I'm pretty sure the scopes can be destroyed and removed, resulting in inconsistent indexing. So obviously I have a general question, what do you think about this problem, are there any better solutions to this problem from your point of view.

Thank you in advance!

Existing implementation

I have decorated ng-model

to save every change in session store, changes fade and persist after a 500ms delay, every change that happened in those 500ms restarts the previous timer.

$provide.decorator('ngModelDirective', function ($delegate) {
    var ngModel = $delegate[0], controller = ngModel.controller;

    var persisted, viewId;

    ngModel.controller = ['$scope', '$state', '$element', '$attrs', '$injector', '$parse', '$window', '$timeout', function ($scope, $state, $element, $attrs, $injector, $parse, $window, $timeout) {
        if (viewId !== $state.params.viewId) {
            viewId = $state.params.viewId;
            persisted = $window.sessionStorage.getItem(viewId);
        }

        if ('ng-model-non-persisted' in $attrs) {
        } else {
            var ngModelGet = $parse($attrs.ngModel),
                ngModelSet = ngModelGet.assign;

            if (ngModelSet) { // Otherwise base call will throw an exception
                if (persisted) {
                    persisted = angular.fromJson(persisted);

                    var model = ngModelGet(persisted);

                    if (model !== void 0) {
                        ngModelSet($scope, model);
                    }
                } else {
                    persisted = {};
                }

                var throttle, cachedValue;
                $scope.$watch(function () {
                    var value = ngModelGet($scope);

                    if (cachedValue !== value) {
                        cachedValue = value;

                        ngModelSet(persisted, value);

                        if (throttle) {
                            $timeout.cancel(throttle);
                        }

                        throttle = $timeout(function() {
                            $window.sessionStorage.setItem(viewId, angular.toJson(persisted));

                            throttle = null;
                        }, 500);
                    }
                });
            }
        }

        $injector.invoke(controller, this, {
            '$scope': $scope,
            '$element': $element,
            '$attrs': $attrs
        });
    }];
    return $delegate;
});

      

+3


source to share





All Articles