Javascript webstorage data storage problem, undefined cannot be stored directly

This article shares a small problem I encountered when using sessionStorage, and I will avoid this pitfall in the future.

The requirement is cell editing in easyui tables. When you click save, the editing of the current row will end, and then the editingId (the ID of the current editing row record) will be modified.

Table of Contents

1. Problems to be solved

2. Complete page code

3. Specific business logic

adding data

save data

Edit data

4. Problem analysis

5. Problem solving


1. Problems to be solved

As shown in the picture, there is no response when clicking the modify button, and no error is reported.

2. Complete page code

menu_list.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/themes/icon.css"/>
<link rel="stylesheet" href="/css/themes/default/easyui.css" />
<title>System Management>>Menu List</title>
<script src="/js/public/jquery.min.js"></script>
<script src="/js/easyui/jquery.easyui.min.js"></script>
<script src="/js/easyui/easyui-lang-zh_CN.js"></script>
<script src="/js/public/util.js"></script>
<script src="/js/public/public.js"></script>
<script src="/js/public/sessionStorage.js"></script>
<script src="/js/menu_list.js"></script>
</head>
\t
<body>
<div>
<a href="javascript:" class="easyui-linkbutton" onclick="insert()">Add</a>
<a href="javascript:" class="easyui-linkbutton" onclick="edit()">Edit</a>
<a href="javascript:" class="easyui-linkbutton" onclick="save()">Save</a>
<a href="javascript:" class="easyui-linkbutton" onclick="cancel()">Cancel</a>
<a href="javascript:" class="easyui-linkbutton" onclick="expandAll()">Expand all</a>
<a href="javascript:" class="easyui-linkbutton" onclick="collapseAll()">Collapse All</a>
</div>

<div id="mm" class="easyui-menu" style="width:120px;">
<div onclick="append()" data-options="iconCls:'icon-add'">Add</div>
<div onclick="removeIt()" data-options="iconCls:'icon-remove'">Remove</div>
<div class="menu-sep"></div>
<div onclick="collapse()">Collapse</div>
<div onclick="expand()">Expand</div>
</div>
\t
<table id="menu_treegrid"></table>
</body>
</html>

menu_list.js

let parentId;
let data = {
id: "",
name: "xxx",
url: "",
icon: "icon-xxx"
};

/**
 * Add to
 */
function insert() {
let menuId = new Date().getTime();
let treegrid = $("#menu_treegrid");

treegrid.treegrid("append",{
data: [{
id: menuId,
name: "xxx",
url: "/html/xxx",
icon: "icon-xxx"
}]
});

//Save editingId to sessionStorage
storage("editingId", menuId);
\t
// Enable line editing
treegrid.treegrid("beginEdit", menuId);
}

/**
 * Revise
 */
function edit() {
let editingId = getStorage("editingId");

console.log(editingId);

if (editingId) {
$("#menu_treegrid").treegrid("select", editingId);
} else {
let row = $("#menu_treegrid").treegrid("getSelected");

console.log("Start editing line:");
console.log(row);

if (row) {
storage("editingId", row.id);

$("#menu_treegrid").treegrid("beginEdit", row.id);
}
}
}

/**
 * save
 */
function save() {
let editingId = getStorage("editingId");

if (editingId) {
let treegrid = $("#menu_treegrid");

if (editingId) {
// Only when editing is completed can the latest value be obtained
treegrid.treegrid("endEdit", editingId);

let postData = {
id: editingId,
name: data.name,
url: data.url,
icon: data.icon,
parentId: parentId
};

console.log(postData);
storage("editingId", undefined);

// post("/menu/updateById", postData, function () {
// storage("editingId", undefined);
// }, error);
}
}
}

/**
 * Cancel
 */
function cancel() {
let editingId = getStorage("editingId");

if (editingId) {
$("#menu_treegrid").treegrid("cancelEdit", editingId);

storage("editingId", undefined);
}
}

/**
 *Collapse all
 */
function collapseAll() {
$("#menu_treegrid").treegrid("collapseAll");
}

/**
 * Expand All
 */
function expandAll() {
$("#menu_treegrid").treegrid("expandAll");
}

/**
 * Add to
 */
function append() {
let menuId = new Date().getTime();
let treegrid = $("#menu_treegrid");
let node = treegrid.treegrid("getSelected");

parentId = node.id;
storage("editingId", menuId);

treegrid.treegrid("append",{
parent: node.id,
data: [{
id: menuId,
name: "xxx",
url: "",
icon: "icon-xxx"
}]
});

let editingId = getStorage("editingId");

treegrid.treegrid("beginEdit", editingId);
}

/**
 * delete
 */
function removeIt() {
let node = $("#menu_treegrid").treegrid("getSelected");

if (node) {
$("#menu_treegrid").treegrid("remove", node.id);
}
}

/**
 * fold
 */
function collapse() {
let node = $("#menu_treegrid").treegrid("getSelected");

if (node) {
$("#menu_treegrid").treegrid("collapse", node.id);
}
}

/**
 * Expand
 */
function expand() {
let node = $("#menu_treegrid").treegrid("getSelected");
\t
if (node) {
$("#menu_treegrid").treegrid("expand", node.id);
}
}

$(document).ready(function() {
$("#menu_treegrid").treegrid({
url: "/menu/listTreeGrid",
method: "get",
idField: "id",
treeField: "name",
fitColumns: true,
pagination: true,
pageSize: 10,
pageList: [10, 20, 50, 100],
columns:[[
{title: "menu number", field: "id", hidden: true},
{title: "menu name", field: "name", align: "left", editor: "textbox", width: 100},
{title: "icon style", field: "icon", align: "left", editor: "textbox", width: 100},
{title: "page address", field: "url", align: "left", editor: "textbox", width: 200,
formatter: function(value) {
if (value) {
return "<a href='" + value + "'>" + value + "</a>";
} else {
return "<div>/</div>";
}
}
}
]],
onAfterEdit: function (row, changes) {
console.log(changes);

data = {
id: row.id,
name: row.name ? row.name : changes.name,
url: row.url ? row.url : changes.url,
icon: row.icon ? row.icon : changes.icon
};;
},
onContextMenu: function (e, row) {
e.preventDefault();

$(this).treegrid("select", row.id);

$("#mm").menu("show",{
left: e.pageX,
top:e.pageY
});
}
}).treegrid("clientPaging");

});

(function($) {
function pagerFilter(data) {
if ($.isArray(data)) { // is array
data = {
total: data.length,
rows: data
}
}

let dg = $(this);
let state = dg.data("treegrid");
let opts = dg.treegrid("options");
let pager = dg.treegrid("getPager");

pager.pagination({
onSelectPage:function(pageNum, pageSize) {
opts.pageNumber = pageNum;
opts.pageSize = pageSize;

pager.pagination("refresh",{
pageNumber:pageNum,
pageSize:pageSize
});
dg.treegrid("loadData",state.originalRows);
}
});

if (!state.originalRows){
state.originalRows = data.rows;
}

let topRows = [];
let childRows = [];

$.map(state.originalRows, function(row){
row._parentId ? childRows.push(row) : topRows.push(row);
});

data.total = topRows.length;

let start = (opts.pageNumber-1)*parseInt(opts.pageSize);
let end = start + parseInt(opts.pageSize);

data.rows = $.extend(true,[],topRows.slice(start, end).concat(childRows));

return data;
}

let appendMethod = $.fn.treegrid.methods.append;
let loadDataMethod = $.fn.treegrid.methods.loadData;

$.extend($.fn.treegrid.methods, {
clientPaging: function(jq) {
return jq.each(function() {
let state = $(this).data("treegrid");
let opts = state.options;
opts.loadFilter = pagerFilter;
let onBeforeLoad = opts.onBeforeLoad;

opts.onBeforeLoad = function(row,param) {
state.originalRows = null;
onBeforeLoad.call(this, row, param);
}
$(this).treegrid("loadData", state.data);
$(this).treegrid("reload");
});
},
loadData: function(jq, data) {
jq.each(function() {
$(this).data("treegrid").originalRows = null;
});

return loadDataMethod.call($.fn.treegrid.methods, jq, data);
},
append: function(jq, param) {
return jq.each(function() {
let state = $(this).data("treegrid");

if (state.options.loadFilter == pagerFilter){
$.map(param.data, function(row) {
row._parentId = row._parentId || param.parent;
state.originalRows.push(row);
});
$(this).treegrid("loadData", state.originalRows);
} else {
appendMethod.call($.fn.treegrid.methods, jq, param);
}
})
}
});
})(jQuery);

3. Specific business logic

Add data

When the Add button is clicked, a row of records will be added at the end of the table, row editing will be enabled, and sessionStorage will be used to save the ID of the row data being edited.

/**
 * Add to
 */
function insert() {
let menuId = new Date().getTime();
let treegrid = $("#menu_treegrid");

treegrid.treegrid("append",{
data: [{
id: menuId,
name: "xxx",
url: "/html/xxx",
icon: "icon-xxx"
}]
});

//Save editingId to sessionStorage
storage("editingId", menuId);
\t
// Enable line editing
treegrid.treegrid("beginEdit", menuId);
}

Save data

Clicking save will modify the value of editingId to undefined, which is equivalent to deletion.

/**
 * save
 */
function save() {
let editingId = getStorage("editingId");

if (editingId) {
let treegrid = $("#menu_treegrid");

if (editingId) {
//Only after editing can the latest value be obtained
treegrid.treegrid("endEdit", editingId);

let postData = {
id: editingId,
name: data.name,
url: data.url,
icon: data.icon,
parentId: parentId
};

console.log(postData);

            // Modify the value of editingId to undefined, which is equivalent to deletion
storage("editingId", undefined);

// post("/menu/updateById", postData, function () {
// storage("editingId", undefined);
// }, error);
}
}
}

Edit data

Select a row in the table and click the edit button. It will determine whether there is currently a row being edited based on the value of editingId. If so, the current row will be selected, but the row being edited will not end. If there is no data row being edited, editing of the currently selected row is started.

/**
 * Revise
 */
function edit() {
let editingId = getStorage("editingId");

console.log(editingId);

if (editingId) {
$("#menu_treegrid").treegrid("select", editingId);
} else {
let row = $("#menu_treegrid").treegrid("getSelected");

console.log("Start editing line:");
console.log(row);

if (row) {
storage("editingId", row.id);

$("#menu_treegrid").treegrid("beginEdit", row.id);
}
}
}

Four. Problem Analysis

There is no response when clicking the modify button, but no error is reported. The problem is obviously that the if branch keeps selecting the current line.

Therefore, the key to the problem lies in the value of editingId. The value printed by the browser above is undefined, which is obviously not a problem.

However, the fatal thing is here. The undefined printed here is not undefined, but “undefined” of the string.

If you print undefined directly, it will be gray, as shown in the figure. The one below is really undefined.

5. Problem Solving

Solution 1: Since undefined cannot be stored directly, just store an empty string in it, and the problem is perfectly solved~

/**
 * save
 */
function save() {
let editingId = getStorage("editingId");

if (editingId) {
let treegrid = $("#menu_treegrid");

if (editingId) {
// Only when editing is completed can the latest value be obtained
treegrid.treegrid("endEdit", editingId);

let postData = {
id: editingId,
name: data.name,
url: data.url,
icon: data.icon,
parentId: parentId
};

storage("editingId", "");
}
}
}

Solution 2: Delete data from localStorage

/**
 * save
 */
function save() {
let editingId = getStorage("editingId");

if (editingId) {
let treegrid = $("#menu_treegrid");

if (editingId) {
// Only when editing is completed can the latest value be obtained
treegrid.treegrid("endEdit", editingId);

let postData = {
id: editingId,
name: data.name,
url: data.url,
icon: data.icon,
parentId: parentId
};

localStorage.removeItem("editingId");
}
}
}