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
}
};
});
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
}
};
});
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.
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.
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
}
};
}
});
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.
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');
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.
Related
I am following a tutorial and I understood everything up until everything beyond where I declared the let variable.
function submitMessage(event) {
event.preventDefault();
const email = document.getElementById("email").value;
const fullName = document.getElementById("fullName").value;
const feedbackType = document.getElementById("feedbackType").value;
const comment = document.getElementById("comment").value;
const messageObject = {
email,
fullName,
feedbackType,
comment
};
let currentMessages = [];
if (window.sessionStorage.getItem("messages")) {
currentMessages =
JSON.parse(
window.sessionStorage.getItem("messages")
);
}
currentMessages.push(messageObject);
window.sessionStorage.setItem(
"messages",
JSON.stringify(currentMessages)
);
}
You're setting the "messages" key for the session storage here:
window.sessionStorage.setItem(
"messages", // arbitrary key name
JSON.stringify(currentMessages) // value to store for this key
);
let currentMessage is an empty array that will hold messageObject variable expressed few row before.
After currentMessage there is a step that setup a session store that we going to call "messages".
The window.sessionStorage function is used to save some data inside the browser, in our case currentMessage. In this way if your refreshed the browser page you will able to get the last data save in window.sessionStorage.
So in the first step this function try to get messages object from the session storage that we have e called messages.
Then, one fetched it will push the currentMessage inside it with setItem, so after you reload the browser you will be able to retrieve the array passing through the session storage getItem and to get this value it need to search inside some key and the key is messages, in other word the key in the session storage that can hold our array.
I am new to session storage and I am trying to take two stored values and substract them then play it back in innerhtml. I don't know how to do that.
(index):897 Uncaught TypeError: Cannot read property 'getElementById' of undefined
let propertyPrice = document.getElementById("propertyPrice");
if (sessionStorage.getItem("propertyPriceStored")) {
propertyPrice.value = sessionStorage.getItem("propertyPriceStored");
}
propertyPrice.addEventListener("change", function() {
sessionStorage.setItem("propertyPriceStored", propertyPrice.value);
});
let deposit = document.getElementById("deposit");
if (sessionStorage.getItem("depositStored")) {
deposit.value = sessionStorage.getItem("depositStored");
}
deposit.addEventListener("change", function() {
sessionStorage.setItem("depositStored", deposit.value);
});
let loanAmount = propertyPrice - deposit;
loanAmount.document.getElementById("loanAmountResult").innerHTML;
I have managed to get the console log to show numbers on reloading of the page but i can get it to happen then and there on the page.
let deposit = document.getElementById("deposit");
// See if we have an autosave value
// (this will only happen if the page is accidentally refreshed)
if (sessionStorage.getItem("depositStored")) {
// Restore the contents of the text field
deposit.value = sessionStorage.getItem("depositStored");
}
// Listen for changes in the text field
deposit.addEventListener("change", function() {
// And save the results into the session storage object
sessionStorage.setItem("depositStored", deposit.value);
});
let loanAmount = propertyPrice.value - deposit.value;
console.log(loanAmount)
loanAmount.addEventListener("change", function() {
document.getElementById("loanAmountResult").innerHTML;
});
I'm trying to make a chat-room where connected users' names are displayed in an 'Online Users' section.
The code below will add each users' name to an array and display the contents of the array.
However, if a user leaves, their username isn't removed from the array. If I pop the array, it'll likely not remove the correct username. This makes me think I should, somehow, associate the socket-id with the username given, and create a disconnect event that removes the correct username from the array, and displays the updated version of the array. How could I alter this code to incorporate this?
script.js:
var user = user;
if (!user) {
user = prompt('Please choose a username:');
if (!user) {
alert('Your name has been set to "Anonymous"');
user = "Anonymous"
items.push(user);
} else {
alert('Your name has been set to "'+ user +'"');
}
}
socket.emit('theitems', user);
socket.on('theitems', function (data) {
$('.dispUser').html(data);
console.log(data);
});
server.js:
var newitems = [];
server(socket('theitems', ctx => { newitems.push(ctx.data); console.log(ctx.data); ctx.io.emit('theitems', newitems); }));
I believe that on user connection you update your array of users with new user, something like:
let users = [];
sockets.on('connection', socket) => {
// This is the user associated with a new socket connection.
// User that you will need to remove when connection is closed.
const user = { /* create your user */ };
users.push(user);
// Then you can subscribe to socket disconnect event
socket.on('disconnect', () => {
users = users.filter(u => u !== user); // removing disconnected user
// And then here you can notify your front-end with an updated users array
});
});
I am very new with firebase and javascript.
My project: Build a private messaging app. To do that, I want to define a sub collection in firestore for private messaging using the current user id and the destination user id.
Here is the function that allows this:
// generate the right SubCollection depending on current User and the User he tries to reach
function dmCollection(toUid) {
if (toUid === null) {
// If no destination user is definer, we set it to the below value
toUid = 'fixed_value';
};
const idPair = [firebase.auth().currentUser.uid, toUid].join('_').sort();
return firebase.firestore().collection('dms').doc(idPair).collection('messages');
};
My problem: I want to use the firebase.auth().currentUser.uid attribute, but it looks like the function is not waiting for firebase.auth initialization. How can I fix this problem?
Additional information:
I have two functions that are calling the first one (dmCollection):
// retrieve DMs
function messagesWith(uid) {
return dmCollection(uid).orderBy('sent', 'desc').get();
};
// send a DM
function sendDM(toUid, messageText) {
return dmCollection(toUid).add({
from: firebase.auth().currentUser.uid,
text: messageText,
sent: firebase.firestore.FieldValue.serverTimestamp(),
});
};
If I correctly understand your problem ("it looks like the function is not waiting for firebase.auth initialization"), you have two possible solutions:
Solution 1: Set an observer on the Auth object
As explained in the documentation, you can set an observer on the Auth object with the onAuthStateChanged() method:
By using an observer, you ensure that the Auth object isn't in an
intermediate state—such as initialization—when you get the current
user.
So you would modify your code as follows:
// retrieve DMs
function messagesWith(uid) {
return dmCollection(uid).orderBy('sent', 'desc').get();
};
// send a DM
function sendDM(toUid, messageText) {
return dmCollection(toUid).add({
from: firebase.auth().currentUser.uid,
text: messageText,
sent: firebase.firestore.FieldValue.serverTimestamp(),
});
};
// generate the right SubCollection depending on current User and the User he tries to reach
function dmCollection(toUid) {
if (toUid === null) {
// If no destination user is definer, we set it to the below value
toUid = 'fixed_value';
};
const idPair = [firebase.auth().currentUser.uid, toUid].join('_').sort();
return firebase.firestore().collection('dms').doc(idPair).collection('messages');
};
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
var messageText = '....';
sendDM(user.uid, messageText)
} else {
// No user is signed in.
}
});
Solution 2: Use the currentUser property
You could also "get the currently signed-in user by using the currentUser property" as explained in the same doc. "If a user isn't signed in, currentUser is null".
In this case you would do:
var user = firebase.auth().currentUser;
if (user) {
var messageText = '....';
sendDM(user.uid, messageText);
} else {
// No user is signed in.
// Ask the user to sign in, e.g. redirect to a sign in page
}
Which solution to choose?
It depends how you want to call the function(s) based on the user uid.
If you want to call the function(s) immediately after the user is signed in, use Solution 1.
If you want to call the function(s) at another specific moment (e.g. following a user action), use Solution 2.
How can I update certain properties of a local storage item or object as new data is inputted throughout the user journey and not lose what was previously entered or if the user decides to update?
My journey of 5 containers consisting of asking the user to input the following:
Name: string
Avatar: integer
Favourite Genres: multiple strings
On the first view I have created the local storage object / item that sets the name within the handleSubmit function.
handleSubmit(event) {
event.preventDefault();
//Profile object
let profile = { 'name': this.state.name, 'avatar': null, 'genres': '' };
// Put the object into storage
localStorage.setItem('profile', JSON.stringify(profile));
// Retrieve the object from storage
var retrievedObject = localStorage.getItem('profile');
//Log object
console.log('retrievedObject: ', JSON.parse(retrievedObject));
//On form submission update view
this.props.history.push('/profile/hello');
}
On my second view I want to update only the avatar property and maintain what the user had inputted in the previous view.
I'm doing this within the handleSelect function like so:
handleSelect(i) {
let selectedAvatarId;
let avatars = this.state.avatars;
avatars = avatars.map((val, index) => {
val.isActive = index === i ? true : false;
return val;
});
this.setState({
selectedAvatarId: selectedAvatarId
})
//Profile object
let profile = { 'avatar': i };
//Update local storage with selected avatar
localStorage.setItem('profile', JSON.stringify(profile));
}
You will need to read the existing value from localStorage, parse it as JSON and then manipulate the data, and write it back. There are numerous libraries out there for easily working with localStorage, but something along the lines of this should work as a generic function:
function updateProfile = (updatedData) => {
const profile = JSON.parse(localStorage.getItem('profile'));
Object.keys(updatedData).forEach((key) => {
profile[key] = updatedData[key];
});
localStorage.setItem('profile', JSON.stringify(profile));
}
If you use object spread, it could look a lot cleaner too:
function updateProfile = (updatedData) => {
const profile = {
...JSON.parse(localStorage.getItem('profile')),
...updatedData
};
localStorage.setItem('profile', JSON.stringify(profile));
}
There should probably be some safety checks in the above code, but hopefully gives you an idea for a starting point.
The only option as far as I know is to get it as a Json, amend accordingly and then save it is again.