Assigning a field name from a method to Firestore document - javascript

Let's say I have a collection called 'users'. When someone clicks the upvote button, I want it to update the user document with an entry that's the slug from the page they are viewing. However, I'm not sure how to accomplish it using the code I have.
voteUp() {
const voteup_ref = db.collection("builds").doc(this.$route.params.slug)
const voteuser_ref = db.collection("users").doc(this.alias)
return db.runTransaction(t => {
return t.get(voteup_ref).then(doc => {
const newCount = doc.data().voteup + 1
const NewSlug = voteuser_ref
t.update(voteup_ref, {
voteup: newCount
})
t.update(voteuser_ref, {
NewSlug: 'voteup'
})
});
})
},
So, if someone is viewing a page with the slug of 'my-awesome-comment', I want 'my-awesome-comment' to be entered in the user's document with a value of "voteup".
What's actually happening is that it will write an entry to the user document as I expect, but it's entering the field name as 'NewSlug' with a value of "voteup" instead of whatever the slug actually is.
What am I doing wrong?

The way you've written the JSON object NewSlug itself is the property name, instead of using its value. To do the latter, you need to use square bracket notation ([]):
let update = {};
update[NewSlug] = 'voteup';
t.update(voteuser_ref, update)

Related

Setting and modifying array and pushing to localStorage creates nested array

I have a function that takes a string from a search field during runtime, and stores it to localstorage. Since we want to store all search strings from the end user to record it, we need to get the current data from localstorage, and add the latest search string.
Here is my code:
const setDatatoLocalStorag = (searchQuery: string) => {
let searchHistory = localStorage.getItem("searchHistory");
let searchQueryArr = [];
if (searchHistory) {
JSON.parse(searchHistory);
searchQueryArr.push(searchQuery, searchHistory);
} else {
searchQueryArr.push(searchQuery);
}
localStorage.setItem("searchHistory", JSON.stringify(searchQueryArr));
}
Lets assume we run the function twice, with the searchQuery "dog" and "cat". This is how it will look like in localstorage:
["cat","[\"dog\"]"]
I believe localstorage will get the item as string "[myData]" which will cause the error. How to properly handle this?
I have tried to follow How to store an array of objects in Local Storage? withous success.
The problem is you aren't assigning JSON.parse(searchHistory); to a variable. I think what you want to do is this:
var searchQueryArr = ['dog'];
localStorage.setItem("searchHistory", JSON.stringify(searchQueryArr));
var searchHistory = JSON.parse(localStorage.getItem("searchHistory") || '[]');
console.log(searchQueryArr, searchHistory);
Note I wasn't able to get this to work with the inline editor, but I did try it on a real server and it worked.
Make a setter/getter pair that hide the encoding/unencoding. Then add a higher-level push that does a get and a set...
// const pretend local storage, keys => strings
const myLocalStorage = {}
function mySetItem(key, value) {
// use the actual local storage setItem() here
myLocalStorage[key] = JSON.stringify(value);
}
function myGetItem(key) {
// use the actual local storage getItem() here
return JSON.parse(myLocalStorage[key]);
}
function myPush(key, value) {
let current = myGetItem(key);
current.push(value)
mySetItem(key, current)
}
// test
const key = 'myKey'
mySetItem(key, []);
myPush(key, { message: 'hello' })
myPush(key, { message: 'dolly' })
console.log(myGetItem(key))

Login system that checks out if the user already has an account. If they have one then they get to login, if they don't then they must try again

My overall problem is actually finding the values of the variables ''loginEmail'' and ''loginPass'' inside my ''arrayRegistros''. It only becomes TRUE when I write the email and password inside includes manually, however, it always ends up turning into FALSE when I use the variables themselves. I tried converting the variables into strings, then used document.getElementById alongside a few other ideas but until now, none of them completed the login system I had planned. The help I need is how one can find a certain variable's value/object, inside a certain array.
login(registro){
this.arrayRegistros;
var loginEmail = document.getElementById('userEmail');
var loginPass = document.getElementById('userPass');
var contaEmail = this.arrayRegistros.some((loginEmail) => {
return loginEmail.emailRegistro.includes(loginEmail)
})
var contaPass = this.arrayRegistros.some((loginPass) => {
return loginPass.passRegistro.includes(loginPass)
})
console.log(contaEmail)
console.log(contaPass)
}
you should get values from inputs like this:
const data = [
{
user : 'jhon',
password : 'asdf123'
},
{
user : 'bob',
password : 'asdf124'
}
]
const userName = document.getElementById('userEmail').value;
const passWord= document.getElementById('userPassword').value;
const findUser = data.filter( item => item.user === userName && item.password === passWord);
if(findUser.length > 0){
//user found
}
The first problem is that you are naming the parameter on the Array.prototype.some function the same as the variable you want to check outside of the predicate scope.
Second, suposing that this.arrayRegistros is a array with objects with the keys emailRegistro and passRegistro containing strings, DOM Elements CANNOT match with strings, but a element.value can.
Another thing you should have in mind is that includes is not an equality operator, 'a-very-strong-password'.includes('a'); will return true.
And, last, you should never validate login and password on the browser, because the user can edit the JavaScript code on-the-fly and get to login without any real credential.
With that in mind, I think the solution would be something like that (ignoring the browser validation problem):
const $userField = document.getElementById('userEmail');
const $passField = document.getElementById('userPass');
const registros = [
{
email: 'example#example.com',
password: 'a-very-strong-password'
},
...anotherUsers
];
function login(registro) {
const { value: user } = $userField;
const { value: pass } = $passField;
// You can use `Array.prototype.some` to just know if the specific user credentials exist, or use `Array.prototype.find` to know if exist AND grab the user, to further utilization
const item = registros.find(item => item.email === user && item.password === pass);
if (item) {
// User found
} else {
// User not found
}
}

Creating a If/Else Statement in JS which is dependant on a data attribute

So I have an app, which contains a button directing people to my 'Ccenter', normally this would bring me to a .DE domain, but I want to create the ability for it to redirect to the .AT instance of my site.
I can decide which domain the button should bring me to by looking at my recipient data, which always contains a country specific email address.
'recipient_email':data.ticket.recipient,
I thought the best way for this to work is an If/Else Statement on the Button itself, where I could use if “data.ticket.recipient.contains?(.de)” to ensure the button links to the .DE app instance, otherwise link to the .AT instance.
I'm unsure how to structure that statement within my code, and would appreciate some pointers or advice on if this is the best way to achieve what I'm aiming for.
Current code:
client.get(`ticket.customField:${cCenterCaseIdFieldName}`).then((result) => {
updateDataPoints(result, data, cCenterCaseIdFieldName);
const recipientEmail = data.ticket.recipient;
const zendeskID = data.ticket.id;
const cCenterUrlAustria = getAustrianCcenterUrl(zendeskID)
const cCenterUrl = getCcenterUrl(zendeskID);
const collapse = $('#collapseExample')
$("#myBtnToCcenter").click(() => openModalPopup(cCenterUrl));
$("#myBtnToAustrianCcenter").click(() => openModalPopup(cCenterUrlAustria));
Modal Pop Up Which Results From Button Click:
function openModalPopup(locationUrl) {
const modalOptions = {
location: 'modal',
url: locationUrl,
size: {
width: '80vw',
height: '80vh'
}
};
i guess you could just do sth. like this:
const recipientEmail = data.ticket.recipient;
var cCenterUrl;
if(recipientEmail.indexOf(".de") > 0) {
cCenterUrl = getCcenterUrl(zendeskID);
}else{
cCenterUrl = getAustrianCcenterUrl(zendeskID)
}
$("#myBtnToCcenter").click(() => openModalPopup(cCenterUrl));
and just use 1 button, if cCenterUrl can change during runtime, u cannot use const of course
One way would be to get the correct URL first, then set the onClick handler accordingly. Something like:
var url = data.ticket.recipient.includes(".de") ? getCcenterUrl(zendeskID) : getAustrianCcenterUrl(zendeskID);
$("#myBtnToCcenter").click(() => openModalPopup(url));
Side note: The includes() function is case-sensitive. If you are not sure of the case (upper/lowercase) of the data.ticket.recipient string, then you can do
data.ticket.recipient.toLowerCase().includes(".de".toLowerCase())
instead of data.ticket.recipient.includes(".de").

how should i dynamically update a firebase document

i'm working on a simple note-taking app for my portfolio using JS and Firebase. Before i tell you what's happening i feel like i need to show you how my code works, if you have any tips and concerns please tell me as it would be GREATLY appreciated. That being said, let's have a look "together". I'm using this class to create the notes:
const htmlElements = [document.querySelector('.notes'), document.querySelector('.note')];
const [notesDiv, noteDiv] = htmlElements;
class CreateNote {
constructor(title, body) {
this.title = title;
this.body = body;
this.render = () => {
const div1 = document.createElement('div');
div1.className = 'notes-prev-container';
div1.addEventListener('click', () => { this.clickHandler(this) });
const div2 = document.createElement('div');
div2.className = 'notes-prev';
const hr = document.createElement('hr');
hr.className = 'notes__line';
// Nest 'div2' inside 'div1'
div1.appendChild(div2);
div1.appendChild(hr);
/*
Create Paragraph 1 & 2 and give them the same
class name and some text
*/
const p1 = document.createElement('p');
p1.className = 'notes-prev__title';
p1.innerText = this.title;
const p2 = document.createElement('p');
p2.className = 'notes-prev__body';
p2.innerText = this.body;
// Nest p 1 & 2 inside 'div2'
div2.appendChild(p1);
div2.appendChild(p2);
// Finally, render the div to its root tag
notesDiv.appendChild(div1);
}
}
/*
Every time this method is called, it creates 2 textareas,
one for the note title and the other for its body then it
appends it to the DOM.
*/
renderNoteContent () {
const title = document.createElement('textarea');
title.placeholder = 'Title';
title.value = this.title;
title.className = 'note__title';
const body = document.createElement('textarea');
body.placeholder = 'Body';
body.value = this.body;
body.className = 'note__body';
noteDiv.appendChild(title);
noteDiv.appendChild(body);
}
/*
When this method is called, it checks to see if there's a
note rendered already (childElementCount === 1 because there's a
button, so if there's only this button it means there's no
textareas rendered).
If yes, then merely call the renderNoteContent method. Else
get the tags with the classes 'note__title' and 'note__body'
and remove them from the DOM, then call renderNoteContent to
create the textareas with the clicked notes values.
This function gets mentioned at line 19.
*/
clickHandler(thisClass) {
if (noteDiv.childElementCount === 1) {
thisClass.renderNoteContent();
} else {
document.querySelector('.note__title').remove();
document.querySelector('.note__body').remove();
thisClass.renderNoteContent();
}
}
}
Now i need 2 buttons, createNotesButton and saveNotesButton respectively. These 2 buttons must be inside a function that will be called inside .onAuthStateChanged (why? because they will be needing access to the currentUser on firebase auth).
I want the createNotesButton to create a note prototype, render it to the DOM and create a new document on firestore, where this note contents will be stored. Here's how i did it:
PS: I feel like i'm not using this class correctly, so again if you have any tips i appreciate it.
import {db} from '../../firebase_variables/firebase-variables.js';
import {CreateNote} from '../create_notes_class/create_notes_class.js';
const htmlElements = [
document.querySelector('.createNotes-button'),
document.querySelector('.saveNotes-button')
];
const [createNotesButton, saveNotesButton] = htmlElements;
function clickHandler(user) {
/*
1. Creates a class.
2. Creates a new document on firebase with the class's empty value.
3. Renders the empty class to the DOM.
*/
createNotesButton.addEventListener('click', () => {
const note = new CreateNote('', '');
note.render();
// Each user has it's own note collection, said collection has their `uid` as name.
db.collection(`${user.uid}`).doc().set({
title: `${note.title}`,
body: `${note.body}`
})
})
}
Now i need a saveNotesButton, he's the one i'm having issues with. He needs to save the displayed note's content on firestore. Here's what i tried doing:
import {db} from '../../firebase_variables/firebase-variables.js';
import {CreateNote} from '../create_notes_class/create_notes_class.js';
const htmlElements = [
document.querySelector('.createNotes-button'),
document.querySelector('.saveNotes-button')
];
const [createNotesButton, saveNotesButton] = htmlElements;
function clickHandler(user) {
createNotesButton.addEventListener('click', () => {...})
/*
1. Creates 2 variables, `title` and `body, if there's not a note being displayed
their values will be null, which is why the rest of the code is inside an if
statement
2. If statement to check if there's a note being displayed, if yes then:
1. Call the user's note collection. Any document who has the title field equal to the
displayed note's value gets returned as a promise.
2. Then call an specific user document and update the fields `title` and `body` with
the displayed note's values.
3. If no then do nothing.
*/
saveNotesButton.addEventListener('click', () => {
const title = document.querySelector('.note__title');
const body = document.querySelector('.note__body');
db.collection(`${user.uid}`).where('title', '==', `${title.value}`)
.get()
.then(userCollection => {
db.collection(`${user.uid}`).doc(`${userCollection.docs[0].id}`).update({
title: `${title.value}`,
body: `${body.value}`
})
})
.catch(error => {
console.log('Error getting documents: ', error);
});
});
}
This didn't work because i'm using title.value as a query, so if i change it's value it will also change the queries direction to a path that doesn't exist.
So here's the question: how can i make it so the saveNotesButton does its job? I was thinking of adding another field to each note, something that won't change so i can easily identify and edit each note. Again, if there's something in my code that you think can or should be formatted please let me know, i'm using this project as a way to solidify my native JS knowledge so please be patient. I feel like if i had used React i would've finished this sometime ago but definitely wouldn't have learned as much, anyway thanks for your help in advance.
I was thinking of adding another field to each note, something that won't change so i can easily identify and edit each note.
Yes, you absolutely need an immutable identifier for each note document in the firestore so you can unambiguously reference it. You almost always want this whenever you're storing a data object, in any application with any database.
But, the firestore already does this for you: after calling db.collection(user.uid).doc() you should get a doc with an ID. That's the ID you want to use when updating the note.
The part of your code that interacts with the DOM will need to keep track of this. I suggest moving the code the creates the firestore document into the constructor of CreateNote and storing it on this. You'll need the user id there as well.
constructor(title, body, userId) {
this.title = title;
this.body = body;
const docRef = db.collection(userId).doc();
this.docId = docRef.id;
/* etc. */
Then any time you have an instance of CreateNote, you'll know the right user and document to reference.
Other suggestions (since you asked)
Use JsPrettier. It's worth the setup, you'll never go back.
Use HTML semantics correctly. Divs shouldn't be appended as children of hrs, because they're for "a thematic break between paragraph-level elements: for example, a change of scene in a story, or a shift of topic within a section." MDN
For your next project, use a framework. Essentially no one hand-codes event listeners and appends children to get things done. I see the value for basic understanding, but there's a rich and beautiful world of frameworks out there; don't limit yourself by avoiding them :-)

Retrieving child record when parent key is unknown in Firebase

I have a data like this:
"customers": {
"aHh4OTQ2NTlAa2xvYXAuY29t": {
"customerId": "xxx",
"name": "yyy",
"subscription": "zzz"
}
}
I need to retrive a customer by customerId. The parent key is just B64 encoded mail address due to path limitations. Usually I am querying data by this email address, but for a few occasions I know only customerId. I've tried this:
getCustomersRef()
.orderByChild('customerId')
.equalTo(customerId)
.limitToFirst(1)
.once('child_added', cb);
This works nicely in case the customer really exists. In opposite case the callback is never called.
I tried value event which works, but that gives me whole tree starting with encoded email address so I cannot reach the actual data inside. Or can I?
I have found this answer Test if a data exist in Firebase, but that again assumes that you I know all path elements.
getCustomersRef().once('value', (snapshot) => {
snapshot.hasChild(`customerId/${customerId}`);
});
What else I can do here ?
Update
I think I found solution, but it doesn't feel right.
let found = null;
snapshot.forEach((childSnapshot) => {
found = childSnapshot.val();
});
return found;
old; misunderstood the question :
If you know the "endcodedB64Email", this is the way.:
var endcodedB64Email = B64_encoded_mail_address;
firebase.database().ref(`customers/${endcodedB64Email}`).once("value").then(snapshot => {
// this is getting your customerId/uid. Remember to set your rules up for your database for security! Check out tutorials on YouTube/Firebase's channel.
var uid = snapshot.val().customerId;
console.log(uid) // would return 'xxx' from looking at your database
// you want to check with '.hasChild()'? If you type in e.g. 'snapshot.hasChild(`customerId`)' then this would return true, because 'customerId' exists in your database if I am not wrong ...
});
UPDATE (correction) :
We have to know at least one key. So if you under some circumstances
only know the customer-uid-key, then I would do it like this.:
// this is the customer-uid-key that is know.
var uid = firebase.auth().currentUser.uid; // this fetches the user-id, referring to the current user logged in with the firebase-login-function
// this is the "B64EmailKey" that we will find if there is a match in the firebase-database
var B64EmailUserKey = undefined;
// "take a picture" of alle the values under the key "customers" in the Firebase database-JSON-object
firebase.database().ref("customers").once("value").then(snapshot => {
// this counter-variable is used to know when the last key in the "customers"-object is passed
var i = 0;
// run a loop on all values under "customers". "B64EmailKey" is a parameter. This parameter stores data; in this case the value for the current "snapshot"-value getting caught
snapshot.forEach(B64EmailKey => {
// increase the counter by 1 every time a new key is run
i++;
// this variable defines the value (an object in this case)
var B64EmailKey_value = B64EmailKey.val();
// if there is a match for "customerId" under any of the "B64EmailKey"-keys, then we have found the corresponding correct email linked to that uid
if (B64EmailKey_value.customerId === uid) {
// save the "B64EmailKey"-value/key and quit the function
B64EmailUserKey = B64EmailKey_value.customerId;
return B64UserKeyAction(B64EmailUserKey)
}
// if no linked "B64EmailUserKey" was found to the "uid"
if (i === Object.keys(snapshot).length) {
// the last key (B64EmailKey) under "customers" was returned. e.g. no "B64EmailUserKey" linkage to the "uid" was found
return console.log("Could not find an email linked to your account.")
}
});
});
// run your corresponding actions here
function B64UserKeyAction (emailEncrypted) {
return console.log(`The email-key for user: ${auth.currentUser.uid} is ${emailEncrypted}`)
}
I recommend putting this in a function or class, so you can easily call it up and reuse the code in an organized way.
I also want to add that the rules for your firebase must be defined to make everything secure. And if sensitive data must be calculated (e.g. price), then do this on server-side of Firebase! Use Cloud Functions. This is new for Firebase 2017.

Categories

Resources