#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 диаграммы меняются по-разному, но как?
Рассмотрите возможность выполнения следующих шагов :
- когда вы нажмете на кнопку «очистить localStorage», а затем запустите скрипку, вы получите правильную диаграмму, как и ожидалось. (Это правильно) хорошо, после этого я сохраняю данные в
localStorage.setItem(‘диаграмма’, JSON.stringify($scope.highchartsNG));
Теперь
2. Снова, когда вы запускаете скрипку, она попадает внутрь if
блока и получает данные из localstorage, затем изменяется ось x y диаграммы. ПОЧЕМУ
Ответ №1:
Причина, по которой вы видите разные результаты, заключается в том, что JSON не может сериализовать функцию (среди прочего).
Итак, когда вы сохраняете объект в localStorage, все свойства, которые ссылаются на функцию (например, форматеры), теряются.
Обновить:
Просто для удовольствия я внедрил простой «сериализатор», который способен структурировать / анализировать простую анонимную функцию (например, те, которые используются в вашей конфигурации).
Вы можете увидеть это в действии здесь (и найти код в этой обновленной скрипке).
Это не универсальное, универсальное, готовое к производству решение, но, похоже, оно подходит для вашего конкретного случая.
Вот краткий обзор того, как это работает:
- При «stringifying» он сначала обходит объект и преобразует каждую функцию в строку (используя свой
toString()
метод). (Бесполезный совет дня: это то, что Angular использует для распознавания введенных зависимостей, когда не используется нотация встроенного массива.)
Затем он используетсяJSON.stringify
для фактической привязки объекта. - Во время синтаксического анализа он сначала анализирует строку как обычно (используя
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;