Question

I have a problem that seems complicated that I don't know how to solve.

My single page application uses knockoutJS and I want to cache my view model every time one of my input values change on my view.

Where the problem lies is the complexity of my view model. To use local storage I need to stringify my object but it has recursive values. I have some logic to handle this and stringify my object. But when I try to parse the JSON string back to an object, I lose my functions.

function cacheForm(agency) {
        //var serialized = stringifyOnce(agency);
        //var serialized = amplify.store("Agency", agency);#
        //var obj = new Object();
        //obj.test = "test";

        value = agency;

        var cache = [];
        parsed = JSON.stringify(value, function (key, value) {
            if (typeof value === 'object' && value !== null) {
                if (cache.indexOf(value) !== -1) {
                    // Circular reference found, discard key
                    return;
                }
                    // Store value in our collection
                    cache.push(value);
            }

            return value;
        });

        //var jsonString = JSON.stringify(cache);


        //cache = null; // Enable garbage collection

        var parsedRecursive = "{" + '"data"' + ":" + parsed + "," + '"expires"' + ":" + null + "}"; // Build the string based on how amplify likes it

        var obj = eval('(' + parsed + ')')

        var obj = jQuery.parseJSON(parsedRecursive);
        //// Put the object into storage
        //localStorage.setItem('Agency', parsedRecursive);

        myData = JSON.parse(parsedRecursive, function (key, value) {
            var type;
            if (value && typeof value === 'object') {
                type = value.type;
                if (typeof type === 'string' && typeof window[type] === 'function') {
                    return new (window[type])(value);
                }
            }
            return value;
        });

        //// Retrieve the object from storage
        //    var retrievedObject = localStorage.getItem('Agency');

        //    var obj = jQuery.parseJSON(parsedRecursive);

        //var agency2 = mapping.fromJS(obj);
        //agency;
        //amplify.store("Agency", agency);
        //amplify.store("Obj", obj);
        //var store = amplify.store(); // Look at all items by not providing a key
    }
});

I tried using eval but this returned an object without my data. To help you understand this is what my view model looks like, which is the agency parameter of cachForm.

Also I tried using amplify but because of my view model structure I run into the same issue.

I have also attached some screenshots to show what happens with my viewmodel in the code.

My Agency view model before stringifying it using JSON format

My JSON string

My JSON string parsed and assigned to an object

Was it helpful?

Solution 2

This is the answer I have posted in another post. They are both related.

JSON.parse returns children objects with null value, children values not being parsed

OTHER TIPS

It is not a good idea to cache the whole viewModel. A viewModel contains observables, computeds and functions or events handlers. You have better to cache the view model data, only the raw data.

On serialization :

So just serialize the view data (use ko.mapping.toJSON):

  var json = ko.mapping.toJSON(viewModel);

In order to rebuild a proper view model it is better to have a initialisation function. This is your view model constructor e.i. a function that adds the event handlers, functions and computed.

  var initializer  = your_constructor;

You also need to determine a cache key e.g. the page path.

  var cacheItem = {data : json, constructor : initializer};
  cache[key] = cacheItem;

On deserialization :

Get the cache item :

var cacheItem = cache[key];
var viewModel = JSON.parse(cacheItem.data);

Now viewModel contains the raw data.

viewModel = ko.mapping.fromJS(viewModel);

The properties were converted into observables.

If there is a constructor call it.

if(cacheItem.constructor)
     cacheItem.constructor.call(viewModel);

Then your view model is as you stored it.

I hope it helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top