tehaaron
tehaaron

Reputation: 2250

JS data in localStorage only being kept in one key, value pair

I am trying to write a simple app in an attempt to learn about HTML5's localStorage. I (thought I) had successfully created a form to take data and store it in localStorage (with JSON.stringify) and then display it on the page (after JSON.parse). I then moved on to how to delete specific items and realized I think I am storing the data incorrectly because I only have 1 key, value pair when I thought I should have one for every entry. So calling localStorage.removeItem(key) just deleted all my data. I need help sorting out how to store each entry separately so they can be deleted 1 by 1.

html:

<form id="sme-submission" name="sme-submission">
    <ul class="no-list-style">
        <li>
        <h2>Add an SME</h2>
        </li>
        <li>
            <label for="firstname">First Name:</label><br />
            <input type="text" id="firstname"/>
        </li>
        <li>
            <label for="lastname">Last Name:</label><br />
            <input type="text" id="lastname"/>
        </li>
        <li>
            <label for="email">Email:</label><br />
            <input type="email" id="email"/>
        </li>
        <li>
            <label for="subject">Subject:</label><br />
            <input type="text" id="subject"/>
        </li>
        <li>
            <input type="submit" value="Save" id="btnSave" class="btn btn-success"/>
        </li>
    </ul>
</form>

<div id="list-display">

</div>

<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>

JS:

var rolodex = localStorage.getItem('rolodex'); //load stored data if there is any

window.onload = function() {
    rolodex = JSON.parse(rolodex);
    if(rolodex === null) { //if no data, initialize an empty array
        rolodex = [];
    } else {
    console.log(rolodex);
    }


    var btnSave = document.getElementById('btnSave');
    btnSave.addEventListener('click', Add, false);

    List();
};    

var Add = function() {
    var SME = JSON.stringify({
        firstname:document.getElementById('firstname').value,
        lastname:document.getElementById('lastname').value,
        email:document.getElementById('email').value,
        subject:document.getElementById('subject').value,

    });

    rolodex.push(SME);
    localStorage.setItem('rolodex', JSON.stringify(rolodex));

for (var key in SME) {
    var person = JSON.parse(SME[key]);
    var div = document.getElementById('list-display').lastChild;
    div.innerHTML = div.innerHTML + "<div class='sme-entry'><ul class='no-list-  style'><li><h3>"+person.subject+"<h3></li><li>"+person.firstname+" "+person.lastname+"</li><li>"+person.email+"</li></ul></div>";
}

alert("Saved");
};

var List = function() {
    for (var key in rolodex) {
    var person = JSON.parse(rolodex[key]);
    var div = document.getElementById('list-display');
    div.innerHTML = div.innerHTML + "<div class='sme-entry'><ul class='no-list-style'><li><h3>"+person.subject+"</h3></li><li>"+person.firstname+" "+person.lastname+"</li><li>"+person.email+"</li></ul></div>";
    }
};

var Remove = function() {

};

Upvotes: 1

Views: 3895

Answers (2)

LetterEh
LetterEh

Reputation: 26716

The key to localStorage is that it only does key-value storage.

You gave it one key ("rolodex"), therefore it's all one value JSON.stringify(rolodex);.

You have two options, here, as far as persistence goes; it doesn't matter which you choose, so long as you have a clear understanding of how each/either works.

First, a ground-rule or two:

  1. localStorage is key-value only, and that value must be a string (or able to be converted to a string).

  2. If you plan to go back and forth, from storage to your app, then you need to make sure you're converting from string, and converting to string, every time.

With that said, your options are as follows:

  1. Keep doing what you're doing. Right now, your graph looks like:
    [{ name : "Bob" }, { name : "Doug" }] // simplified
    So you know it's relatively simple to grab the key for that whole thing, turn it into an array of objects, and deal with it that way.
    Upside: it's pretty easy to work with, as far as going to/from storage is concerned.
    Downside: if it gets HUGE it could start slowing down, with only one key, it's easy to make a mistake and overwrite everything, you have to grab all of the data to update one email address, et cetera...

  2. Generate a unique id for every person (maybe it's name+email+mobile, or completely-random doesn't matter).
    Push each ID into the rolodex.
    Save each contact separately, using their ID.
    var id = abc123; contact.id = id; rolodex.push(id); localStorage.set(id, JSON.stringify(contact));

Upsides: Now, you have objects which are safer on their own.
When you edit and save, you're only accessing the bare-minimum required to do your job.
You can still "loop" through your contacts (by id), using the rolodex.

Downsides: While it's faster to access one person, you need to know their ID already.
To know that, you likely need to have the rolodex open... ...fine... ...but to know which ID is right, you probably have to loop through the rolodex, grab each ID, one at a time, grab each person by ID, and test them, until you find the one you want...
A lot less efficient to do it that way.
At the same time, if you do have an ID which is given to you by someone else, grabbing the right person got much faster. Lookups by index got very fast, while non-indexed lookups got stupid slow.

It's also more complex to build everything out.
This example might not be super-tough, but if you have different arrays of IDs which build different types of objects, and those different objects have properties which are other objects, referenced by ID, which you then link together...

...congratulations, you just replaced a relational-database, using arrays/properties as joining tables of foreign keys, while moving the complexity of that management from SQL to JS (might be easier or harder, depending on proficiency).

The ideal answer for a typical application is probably in the middle:
Keeping an array of small but useful objects to loop through (eg: first_name + last_name + id + email ), while keeping the larger versions of those objects under their own key (the full version, with birthday, latest partner's name, et cetera) would make it faster for you to find a list of "The Dave's I Know", or "My Family Members", without having to unpack everyone.

Of course, then you'd need to update users in two places (though legal name-changes are rare, and so you'd be pretty safe, other than through marriage to store non-volatile data in multiple spots).

The other option in the exact same vein, but on the opposite end of memory load, is to unpack everything at the start of the app.
Unpack your arrays, loop through, and for each ID, replace the ID with the full object at that ID.

Then, for each property of that object, if that property is an ID, grab the object at THAT ID, and attach it, et cetera...

Now you have all of your data in-memory, so you don't need to hit storage again, and any changes you make will only happen to an object at a time, so you can save only the objects (or sub-objects) that need saving.

Of course, the downside here is that it's the slowest to get started, and it requires the most memory (that's not much of a concern, these days, unless you want it to be fast on old phones which barely support localStorage).

Upvotes: 4

Scott Mermelstein
Scott Mermelstein

Reputation: 15397

Localstorage takes what you give it. In this case, you gave it an array full of items, so that's what it stored under one key. You can instead store each item separately, but you'll need to keep track of their names yourself (e.g. by storing a lastIndexUsed in local storage as well, and then storing keys for "rolodex" + lastIndexUsed).

Better would be to acknowledge that you're storing the whole array, and simply not delete it. Reload the array, and delete any single item you want from it, then save the array again.

Upvotes: 0

Related Questions