Чтение из HTML5 localstorage json, не распознается в highcharts-ng

#javascript #html #angularjs #highcharts #local-storage

#javascript #HTML #angularjs #highcharts #локальное хранилище

Вопрос:

У меня есть одна странная проблема, я могу выразить ее в пунктах, чтобы вы могли лучше понять.

  • пожалуйста, откройте эту демонстрацию скрипки 1 в браузере, как вы можете видеть, я использую highchart-ng https://github.com/pablojim/highcharts-ng угловая директива для создания диаграммы. Это работает нормально.
  • Теперь, пожалуйста, откройте эту ссылку также в новой вкладке Demo 2, я только что использовал HTML5 localStorage.

    то, что я делаю, это

  • Сначала проверьте данные диаграммы в localStorage ‘chart’, если они существуют, используйте их, иначе
  • нарисуйте диаграмму из жестко закодированной переменной json $scope.highchartsNG.

Я имею в виду :

 if(JSON.parse(localStorage.getItem('chart'))){
        console.log('FROM CACHE');
        $scope.highchartsNG = JSON.parse(localStorage.getItem('chart'));
}else{
  $scope.highchartsNG = {
            options: {
             ...
             ...
}
 

И для очистки localStorage я предоставил кнопку

 <input type='button' onclick='javascript:localStorage.clear();' value='clear localStorage'  />
 

При нажатии я очищаю localStorage с помощью localStorage.clear();

Проблема в том, что когда я читаю из localstorage, метки оси x и оси y диаграммы меняются по-разному, но как?

Рассмотрите возможность выполнения следующих шагов :

  1. когда вы нажмете на кнопку «очистить localStorage», а затем запустите скрипку, вы получите правильную диаграмму, как и ожидалось. (Это правильно) хорошо, после этого я сохраняю данные в

    localStorage.setItem(‘диаграмма’, JSON.stringify($scope.highchartsNG));

Теперь
2. Снова, когда вы запускаете скрипку, она попадает внутрь if блока и получает данные из localstorage, затем изменяется ось x y диаграммы. ПОЧЕМУ

Ответ №1:

Причина, по которой вы видите разные результаты, заключается в том, что JSON не может сериализовать функцию (среди прочего).
Итак, когда вы сохраняете объект в localStorage, все свойства, которые ссылаются на функцию (например, форматеры), теряются.


Обновить:

Просто для удовольствия я внедрил простой «сериализатор», который способен структурировать / анализировать простую анонимную функцию (например, те, которые используются в вашей конфигурации).


Вы можете увидеть это в действии здесь (и найти код в этой обновленной скрипке).


Это не универсальное, универсальное, готовое к производству решение, но, похоже, оно подходит для вашего конкретного случая.
Вот краткий обзор того, как это работает:

  1. При «stringifying» он сначала обходит объект и преобразует каждую функцию в строку (используя свой toString() метод). (Бесполезный совет дня: это то, что Angular использует для распознавания введенных зависимостей, когда не используется нотация встроенного массива.)
    Затем он используется JSON.stringify для фактической привязки объекта.
  2. Во время синтаксического анализа он сначала анализирует строку как обычно (используя JSON.parse ), а затем обходит результирующий объект, обнаруживает строковые свойства, которые соответствуют регулярному выражению (разработанному для соответствия анонимным функциям) и, наконец, преобразует эти строковые функции в реальные функции.

Вот код:

 .factory('JSONwithFn', function () {
    var fnRE = /^[srn]*function[srn]*([^)]*)[srn]*{[sS]*}[srn]*$/i;

    function strToFn(str) {
        var args = str.substring(str.indexOf('(')   1, str.indexOf(')')).
        split(',').
        map(function (arg) {
            return arg.trim();
        });
        var fnBody = str.substring(str.indexOf('{')   1, str.lastIndexOf('}')).
        trim();
        args.push(fnBody);
        return Function.apply(null, args);
    }

    function parseFns(obj) {
        var retObj;

        switch (Object.prototype.toString.call(obj)) {
            case '[object Undefined]':
            case '[object Null]':
            case '[object Boolean]':
            case '[object Number]':
            case '[object Function]':
                retObj = obj;
                break;
            case '[object String]':
                retObj = fnRE.test(obj) ? strToFn(obj) : obj;
                break;
            case '[object Array]':
                retObj = obj.map(function (value) {
                    return parseFns(value);
                });
                break;
            case '[object Object]':
                retObj = {};
                Object.keys(obj).forEach(function (key) {
                    var value = obj[key];
                    retObj[key] = parseFns(value);
                });
                break;
            default:
                break;
        }

        return retObj;
    };

    function stringifyFns(obj) {
        var retObj;

        switch (Object.prototype.toString.call(obj)) {
            case '[object Undefined]':
            case '[object Null]':
            case '[object Boolean]':
            case '[object Number]':
            case '[object String]':
                retObj = obj;
                break;
            case '[object Function]':
                retObj = obj.toString();
                break;
            case '[object Array]':
                retObj = obj.map(function (value) {
                    return stringifyFns(value);
                });
                break;
            case '[object Object]':
                retObj = {};
                Object.keys(obj).forEach(function (key) {
                    var value = obj[key];
                    retObj[key] = stringifyFns(value);
                });
                break;
            default:
                break;
        }

        return retObj;
    }

    return {
        stringify: function (obj) {
            var obj2 = stringifyFns(obj);
            return JSON.stringify(obj2);
        },
        parse: function (str) {
            var obj = JSON.parse(str);
            return parseFns(obj);
        }
    };
});
 

Как использовать:

Просто вставьте его в свой контроллер (или что-то еще) и используйте JSNOwithFn.stringify/parse вместо JSON.stringify/parse .
Больше ничего не нужно менять в вашем коде.
Это просто сработает 🙂

Комментарии:

1. эй, извините за задержку, я принял ваш ответ, но не могли бы вы взглянуть на «готовое к производству решение, но, похоже, оно работает для вашего конкретного случая». можете ли вы подготовить его к производству. Просто запрос

2. Не совсем, потому что это зависит от того, что вы хотите и как вы собираетесь это использовать. Функция сериализации не является тривиальной (иначе разработчики уже реализовали бы ее). Дело в том, что вам не нужен универсальный сериализатор функций — вам нужен тот, который соответствует вашим требованиям. Например. мой сериализатор поддерживает анонимную функцию — если это нормально для вас, то никаких проблем, но если вы передадите ему именованную функцию, она сломается. Это зависит от того, что вы хотите в него добавить 🙂

Ответ №2:

Как и сказал ExpertSystem, JSON.stringify при сериализации ваша функция форматирования отключается.

В зависимости от того, что вы пытаетесь сделать, вы можете сохранить свою функцию форматирования отдельно в контроллере и переназначить ее после загрузки параметров диаграммы из localstorage.

Верхняя часть вашего контроллера:

 $scope.xAxisFormatter = function(){
    return 'Week '   this.value;
}
 

Ваши параметры диаграммы:

 <snip...>
xAxis: {
          tickInterval: 1,
          allowDecimals: false,
          labels: {
             formatter: $scope.xAxisFormatter
                   }                    
        },
<snip...>
 

При загрузке из localstorage:

 $scope.highchartsNG = JSON.parse(localStorage.getItem('chart'));
$scope.highchartsNG.options.xAxis.labels.formatter = $scope.xAxisFormatter;