-1
  • The goal is the user can save up to 7 field vals in obj userA, logout, log back in and the saved vals are there, retrievable. Specific to each user.
  • I am trying to create an object i.e. userA and update it, as the user saves each field value (i.e. BaseMap: basemapSaved), save the updated state in local storage, then retrieve saved state using local storage. So, when the user logs out, then logs back in, their saved data is still there specific to their username.

Below is my most recent attempt (full js): Any pointers? Am I going about this all wrong?

UPDATED ATTEMPT BELOW WITH BOUNTY.


I am simply trying to save an object of data and a field within it (i.e. userA.BaseMap.basemapSaved;) with local storage, on click.

I later want to parse that saved object in local storage, get that field, and update my API object i.e. object.data.field (userA.BaseMap.basemapSaved;) with the value saved and gathered. I can do this pro grammatically pretty easy, but the idea is to save the state per user, so they can log out, then log back in and have their selection saved.

  // Here I am trying to initialize the variables
    var currentUser;
    var basemapSaved;
    var userA[key] = {};

    // This function I am getting the logged in username, I want to set this as the object key in userA i.e. userA[key]

    function checkUser()  { 
      var node = document.querySelectorAll("span.username")[0];
      currentUser = node.textContent;
      var key = currentUser;
      console.log("current user is:" + key);
    }

    // This is just a handler to wait to my basemap gallery loads before trying to save

    var basemapMod = document.getElementsByClassName("basemap")[0]; 
    basemapMod.addEventListener('click', ()=>{ 
      setTimeout(
        function() {
          BaseMapSaver();
        }, 2000);
    });

    function BaseMapSaver() {
      savebtnBM.addEventListener('click', ()=>{ 
        checkUser();
        // This is where I get the data from my API to save, gathers fine
        basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id;
        // Below I am trying to set it, at first without the object key but would like to use the key
        var saveMap = localStorage.setItem('userA', JSON.stringify(userA));

        console.log(userA);
      });
    }
    // Home button
    var defaultViewHbtn = document.getElementById("home");

    defaultViewHbtn.addEventListener('click', ()=>{

        checkUser();

        // Here I try to parse the value from local storage object
        const userAParseValue = JSON.parse(localStorage.getItem('userA'));

        // Errors with Uncaught TypeError: Cannot read property 'BaseMap' of undefined
        userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
        console.log(userBaseMap);

        app.widget.Manager.Gallery.activeBasemap.Item.id = {
          portalItem: {
              id: userA.BaseMap.basemapSaved // this is where I want to load saved value from local storage object
            }
          };
    });
Dr Upvote
  • 8,023
  • 24
  • 91
  • 204
  • What is the specific problem? Please describe in the text of your question what the problem is, not (only) in the code's comments. – trincot Jan 31 '20 at 20:26
  • The problem is the object states (i.e. the one field.data I am demoing above) is not being saved and retrieved with local storage. – Dr Upvote Jan 31 '20 at 20:28
  • I don't see any field.data in your code. You reference `basemapSaved` as a property, but you never set that property. Moreover, you define `BaseMap` as a (empty) string, so it makes no sense to treat it as an object. I note that you do `userA = currentUser;`, but then immediately assign a *different* value to `userA`. I have no idea what you are trying to do... – trincot Jan 31 '20 at 20:32
  • 1
    should not you ```getItem('userA')``` since you stored your item with key ```'userA'``` ? – grodzi Jan 31 '20 at 21:13
  • Thank you for your candor @trincot . I have updated the code and post to a more clean and simplified version. Hopefully now it is understandable. – Dr Upvote Jan 31 '20 at 21:48
  • my previous remark holds. Note that you may be wary about basemapSaved if it is not "basic" object but some instances of Object (especially like classes), you will not retrieve an instance after JSON.parse. I assume it is primitive type (seems to be an id (so number or string)), but in case... – grodzi Jan 31 '20 at 21:55
  • I still can't make sense of the code. I will leave it to others to look into it further. Sorry. – trincot Jan 31 '20 at 22:00
  • Maybe I should just ace the local storage idea and create new objects of data per user, and populate it as they save each field. And retrieve said data if found from clicking home button. I just ideally didn't want to have 50 objects of data for all 50 users – Dr Upvote Jan 31 '20 at 22:43
  • seems that post is still opened. Were you able to fix it? If yes probably mark answer as accepted or answer yourself, if not, care to explain why basemapMod binds a click to a function which itself will binds another click (BaseMapServer) ? – grodzi Feb 04 '20 at 06:44
  • No this is still unsolved. – Dr Upvote Feb 10 '20 at 14:11
  • `var userA[key] = {};` is syntactically incorrect. What did you try to do here? – Guerric P Feb 10 '20 at 17:36
  • Before you click `defaultViewHbtn`, do you show what's the data in your local storage? I found the `logger` in your `BaseMapSaver` function, Is any incorrect data structure in the shown? – Rach Chen Feb 15 '20 at 13:09
  • I think `userAParseValue` is already the object you saved. `userAParseValue.userA` is undefined, but `userAParseValue.BaseMap` should be exactly what you want. – 鄭脈龍 Feb 17 '20 at 07:53

5 Answers5

1

It Should work check addEventListener function:-

Hbtn.addEventListener('click', ()=>{
        checkUser();
        const userAParseValue = JSON.parse(localStorage.getItem('userA'));
        userBaseMap = userAParseValue.userA.BaseMap.basemapSaved; 
        console.log(userBaseMap);

        myApp.widgets.bigData.Gallery.map = {
          Item: {
              id: userA.BaseMap.basemapSaved
            }
          };
    });
Gaurav verma
  • 679
  • 6
  • 12
  • Thanks this is helpful, upvote, but it's still failing; now at setItem perhaps because of the object key? can I set and retrieve a local storage object using a key? or am i initializing var userA incorrectly? – Dr Upvote Jan 31 '20 at 23:56
  • Make it very simple First make one 'tmp' Variable and store your complete object. After that just console it and check it data is proper or not. After that do JSON.stringify(tmp) and store in local storage with key name Like 'UserA'. and get value using getItem with 'UserA' and do JSON.parse. and console it and access your data what you want. – Gaurav verma Feb 01 '20 at 11:08
  • regarding your question @CaptainRon your code should be failing in ```checkUser``` since ```userA``` is undefined when you try ```userA[key]```. As for why it fails in setItem (in case your actually initialized ```userA = {}```), this is because you access ```BaseMap``` from ```userA``` which is ```undefined```, and then ```Basemap.basemapSaved``` which you can't access on ```undefined```. What you would have wanted is (in the simplest form) ```JSON.stringify(userA)``` – grodzi Feb 04 '20 at 06:49
  • Here is a simplified version of the logic I am trying to achieve. https://jsfiddle.net/ragkw3qu/ – Dr Upvote Feb 10 '20 at 16:16
1

You can use localStorage and the approach you're trying to make work, but you'll end up with localStorage having a separate object for each user. If that's OK then you use localStorage after load to check if a user is logged in and then load the users' data. Then update the data to the localStorage when the values change. You may check inline comments for details:

HTML if there is a user logged in:

<h3>User <span class="username"><?php echo $user; ?></span> is logged in</h3>
<form method="post">
  <input type="hidden" name="action" value="logout"/>
  <button type="submit">Logout</button>
</form>
<hr/>
<div>
  <h2>User counter: <span id="counter"></span></h2>
  <div>
    <button id="inc-counter">Increase</button> 
    <button id="dec-counter">Decrease</button>
  </div>
</div>

Javascript to handle localStorage:

// Get user stored on page HTML
const user = document.querySelector("span.username");
// Something to update/alter using user data and/or user input
const counter = document.querySelector("#counter");
const incCounter = document.querySelector("#inc-counter");
const decCounter = document.querySelector("#dec-counter");

if(user) { // If there is a user logged in
  // Get the username
  const username = user.textContent;
  // Get the localStorage the belongs to that user (using username for key)
  let storageUser = JSON.parse(localStorage.getItem(username) || 'null');
  // Use default user object if the user has no previous settings stored
  let currentUser = storageUser || {
    BaseMap: {
      counter: 0
    }
  };

  // Display the user data
  function displayCounter() {
    const BaseMap = 'BaseMap' in currentUser ? currentUser.BaseMap : {};
    let userCounter = 'counter' in BaseMap ? BaseMap.counter : 0;
    counter.textContent = userCounter;
  }

  // Alter the user data and save it to localStorage user settings object
  function alterCounter(addToCounter) {
    // Check if BaseMap object exists or default
    const BaseMap = 'BaseMap' in currentUser ? currentUser.BaseMap : {};
    // Check if data exists or default
    let userCounter = 'counter' in BaseMap ? BaseMap.counter : 0;

    // Alter user data according to user input
    userCounter += addToCounter;

    // Change user settings object
    currentUser['BaseMap']['counter'] = userCounter;

    // Save user settings object
    localStorage.setItem(username, JSON.stringify(currentUser));

    // Display altered user data
    displayCounter();
  }

  // Initialize by display retrieved/default data
  displayCounter();

  // Add event listeners to user inputs
  incCounter.addEventListener('click', () => alterCounter(1));
  decCounter.addEventListener('click', () => alterCounter(-1));
}

You can check an online example that I've made at the link below:

https://zikro.gr/dbg/so/60010743/ (Users userA, userB both with password 1234 can be used for demonstration)

That will work and retrieve/save user data to the localStorage using username for each user. Keep in mind that this method will only save the user settings for a specific browser location. If you want to have user settings when the user logs in from anywhere, then you should go with the traditional workaround which is based on server session, but it's not so flexible when it comes to user settings because you'll have to update each data/setting using server requests each time the user makes a change which it's possible but it requires server + client implementation.

A combination of both server side settings storage + server session + client localStorage would be the best approach to this situation.

Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
1

here is my answer

<html>
<body>
 <span class="border username">121</span>
 <div class="border basemap">Base Map</div>
 <div class="border saveBtn">Save</div>
 <div id="home" class="border">Home</div>

 <style>
  .border{
   border: solid gray 1px;
   border-radius: 2px;
   text-align: center;
   background: #eee;
   margin: 5px;
   width: 100px;
  }
 </style>
 <script type="text/javascript">
  // Here I am trying to initialize the variables
  var key = 1;
  var currentUser;
  var basemapSaved;
  var userA = {
   BaseMap: {
    id: 1234
   }
  };

  var app = {
   widget: {
    Manager: {
     Gallery: {
      activeBasemap: {
       Item: {
        id: {
              portalItem: {
          id: 1234 // this is where I want to load saved value from local storage object
         }
        }
       }
      }
     }
    }
   }
  };

  // This function I am getting the logged in username, I want to set this as the object key in userA i.e. userA[key]

  function checkUser()  { 
   var node = document.querySelectorAll("span.username")[0];
   currentUser = node.textContent;
   var key = currentUser;
   console.log("current user is:" + key);
  }

  // This is just a handler to wait to my basemap gallery loads before trying to save

  var basemapMod = document.getElementsByClassName("basemap")[0]; 
  basemapMod.addEventListener('click', ()=>{ 
   console.log("basemapMod click");
   setTimeout(
    function() {
     BaseMapSaver();
    }, 2000);
  });

  function BaseMapSaver() {
   var savebtnBM = document.getElementsByClassName("saveBtn")[0];
   savebtnBM.addEventListener('click', ()=>{ 
    console.log("savebtnBM click");
    checkUser();
    // This is where I get the data from my API to save, gathers fine
    basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id.portalItem.id;
        
        /** saving users, instead of userA */
    const userAParseValue = JSON.parse(localStorage.getItem('users'));
    userA.BaseMap.basemapSaved = basemapSaved;
    const finalUsers = {...userAParseValue, userA}
    // Below I am trying to set it, at first without the object key but would like to use the key
    var saveMap = localStorage.setItem('users', JSON.stringify(finalUsers));

    console.log(userA);
   });
  }
  // Home button
  var defaultViewHbtn = document.getElementById("home");

  defaultViewHbtn.addEventListener('click', ()=>{
   console.log("defaultViewHbtn click");
   checkUser();

   // Here I try to parse the value from local storage object
   const userAParseValue = JSON.parse(localStorage.getItem('users'));

   // Errors with Uncaught TypeError: Cannot read property 'BaseMap' of undefined
   userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
   console.log(userBaseMap);

   app.widget.Manager.Gallery.activeBasemap.Item.id = {
    portalItem: {
      id: userA.BaseMap.basemapSaved // this is where I want to load saved value from local storage object
     }
    };
  });
 </script>
</body>
</html>

I changed a few structures which were not coherent. Saving and loading them was causing discrepancies. I also suggest storing all users in a single object and accessing the data from userMap because multiple users can use same browser.

1

Based on the two requirements that you have defined in your original question, this should do what you ask.

The goal is the user can save up to 7 field vals in obj userA, logout, log back in and the saved vals are there, retrievable. Specific to each user.

I am trying to create an object i.e. userA and update it, as the user saves each field value (i.e. BaseMap: basemapSaved), save the updated state in local storage, then retrieve saved state using local storage. So, when the user logs out, then logs back in, their saved data is still there specific to their username.

// retrieve user from localstorage. defaults to {}
// This looks to retrieve the user from local storage by username.
// Returns a `userObj` an object with two properties.
// `username` - the name of the user
// `user` - the stored object that was retrieved from local storage.
//          defaults to {} if nothing in user storage

// Not a good strategy btw, a lot of us share the same names :)

function getUser(username) {
    let user = localStorage.getItem(username) || {};
    try {
        user = JSON.parse(user);
    } catch (e) {
        user = {};
    }
    return { username, user }
}

// Store user object in localstorage
// Store a user in local storage, keyed by their username
function setUser(username, user) {
    localStorage.setItem(username, JSON.stringify(user));
}

// set a key/ value on user object in localstorage
// Don't allow anymore than 7 properties to be stored on the user object
function setUserProperty(userObj, key, value) {
    let { username, user } = userObj;

    if (Object.keys(user).length > 7) {
        throw new Error('user properties exceeds 7')
    }

    user[key] = value;
    setUser(username, user);
}


// modified to return a user from local storage or {}
function checkUser()  { 
    var node = document.querySelectorAll("span.username")[0];
    const currentUser = node.textContent;
    return getUser(currentUser);
}

// This is just a handler to wait to my basemap gallery loads before trying to save
var basemapMod = document.getElementsByClassName("basemap")[0]; 
basemapMod.addEventListener('click', () => { 
    setTimeout(
        function() {
            BaseMapSaver();
    }, 2000);
});

// Fyi Capitals indicate constructors - not functions!
function BaseMapSaver() {
    savebtnBM.addEventListener('click', () => { 
        const userObj = checkUser(); // get the user from localstorage
        const basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id;
        setUserProperty(userObj, 'basemap', basemapSaved) // store the basemap on the user object in local storage with the key 'basemap'
        console.log(JSON.stringify(userObj));
    });
}


var defaultViewHbtn = document.getElementById("home");

defaultViewHbtn.addEventListener('click', () => {
    // get user from localstorage
    const { user } = checkUser();
    const userBaseMap = user.basemap

    // if we have a store basemap
    if (userBaseMap) {
        app.widget.Manager.Gallery.activeBasemap.Item.id = {
            portalItem: {
              id: userBaseMap // load it
            }
        };
    }
});

jeeves
  • 1,871
  • 9
  • 25
0

There are many ways to handle this depending upon your use case. You have specifically mentioned LocalStorage hence everyone is suggesting the same but cookies will fit your bill as well as long as you handle the expiry time properly for them.

  1. Local Storage

Make an Object of fields you will like to store for that user

let obj = {'keyname' : value, 'keyname' : value}; 

//store it - mapping it with user
localStorage.setItem('userID', JSON.stringify(obj));

//retrieve and use on login success
let ret_obj= localStorage.getItem('userID');
  1. Cookies

You can set an arbitrary expiration time and then you again have choice of choosing just one variable or store it as a JSON itself.

document.cookie = "userName=Robert; expires=Fri, 31 Dec 9999 23:59:59 GMT"; 

*Cookies will hold limited amount of data, as in not huge data (Which I don't think is the use case here because I checked your jsfiddle example, you are basically trying to store some data)

If you want to store JSON data in cookies check this out Click Here

*Why am I suggesting cookies? Many enterprises already do something similar for example even post logging out when you visit a website they will display your name and ask you to sign-in, it is just a personalisation addition.

Gandalf the White
  • 2,415
  • 2
  • 18
  • 39