Im currently trying to find out if its possible to change a text in extension when a submit has happend. I currently have a text saying "ENTER DISCORD USER ID AND SUBMIT" and when an user has entered its USER ID and submitted, the text should be changed to "USER ID SUBMITTED" and the text should always say that afterwards, meaning that if someone closes and opens the extensions - the text should still say "USER ID SUBMITTED".
<!DOCTYPE html>
<head>
<link rel="stylesheet" href="../css/style.css">
<script src="../js/popup.js"></script>
</head>
<body>
<div class="text-center">
<form id="form" class="form-control mt10">
<label> <input type="number" id="discord-id-input" name="discord-id-input"></label>
<button id="discord-id-button" type="submit" class="submit"></button>
<output id="help-text" class="help-text" value="">ENTER DISCORD USER ID AND SUBMIT</output>
</form>
</div>
</body>
function get_discord_id(callback) {
chrome.storage.sync.get(["discord_id"], (result) => {
callback(result.discord_id);
});
}
function set_discord_id(discord_id) {
chrome.storage.sync.set({ discord_id: discord_id }, () => {});
}
window.addEventListener("DOMContentLoaded", (e) => {
// check if discord ID is already stored
get_discord_id((discord_id) => {
if (discord_id == null) {
form.addEventListener('submit', () => {
let value = document.getElementById("discord-id-input").value;
chrome.runtime.sendMessage({ discord_id: value }, function(response) {});
set_discord_id(value);
document.getElementById('help-text').innerHTML = 'USER ID SUBMITTED';
});
e.preventDefault();
};
});
});
I wonder how I am able to permanent change a text after a submitted trigger has happend?
Since you already store the discord_id and retrieve it when the popup window is opened, you can determine whether the id has been submitted based on the presents of discord_id.
Here is the updated JS code to do that
function get_discord_id(callback) {
chrome.storage.sync.get(["discord_id"], (result) => {
callback(result.discord_id);
});
}
function set_discord_id(discord_id) {
chrome.storage.sync.set({ discord_id: discord_id }, () => {});
}
window.addEventListener("DOMContentLoaded", (e) => {
// check if discord ID is already stored
get_discord_id((discord_id) => {
if (discord_id) {
document.getElementById('help-text').innerHTML = 'USER ID SUBMITTED';
} else {
form.addEventListener('submit', () => {
let value = document.getElementById("discord-id-input").value;
chrome.runtime.sendMessage({ discord_id: value }, function(response) {});
set_discord_id(value);
document.getElementById('help-text').innerHTML = 'USER ID SUBMITTED';
});
e.preventDefault();
};
});
});
Related
In my registration form I have checkbox that confirms whether the user accepted the terms and conditions. The checkbox should validate once I hit the submit button, however since the checkbox is initially unselected, the validation error shows up straight away. Eventually, the error disappears reactively once I tick the checkbox, but for this particular scenario I would like to have the validation error show up only after I hit submit (if I did not check it). I'm not getting any particular console errors, but I'm simply getting stuck on the execution. Would anyone be able to show me how I can achieve this? I'd appreciate any help!
Checkbox.vue - this is the component representing the checkbox.
<template>
<div class="check-wrapper">
<label :for="id" class="check-label">
<input v-model="checkboxValue"
:id="id"
:checked="isCheckboxChecked"
:oninput="checkCheckbox()"
type="checkbox"
name="newsletter"/>
<span v-if="labelText && !isLabelHtmlText">{{ labelText }}</span>
<span v-if="labelText && isLabelHtmlText" class="label-html" v-html="labelText"></span>
<span :class="{'check-mark-error': checkboxError}" class="check-mark"></span>
</label>
<p v-if="checkboxError" class="checkbox-error text-error">{{checkboxError}}</p>
</div>
</template>
<script>
data: () => ({
checkboxValue: false
}),
methods: {
updateValue: function () {
if (this.$props.callback) {
this.$props.callback(this.$props.id, this.$props.checkboxData, this.checkboxValue);
}
},
checkCheckbox: function () {
this.updateValue();
}
}
</script>
Register.vue - this is the parent component where the registration takes place
<template>
<BasicCheckbox :id="'terms-privacy'"
:callback="onTermsClick"
:label-text="'terms and conditions'"
:is-label-html-text="true"
:checkbox-error="termsPrivacyError"
class="terms-privacy"/>
</template>
<script>
methods: {
validateData: function (data) {
if (!this.termsPrivacyError) {
this.sendRegistration(data).then(response => {
if (response) {
console.log('Registration successful');
this.loginUser({email: data.email, password: data.password}).then(response => {
if (response) {
console.log('User logged in!');
this.$router.push({name: ROUTE_NAMES_HOME.HOME});
}
})
}
});
}
},
// Terms and Privacy Checkbox
onTermsClick: function (checkboxId, checkboxData, data) {
this.termsPrivacyError = !data ? termsPrivacyErrorText : '';
},
}
</script>
First of all, this is not an elegant solution but it works, we use a computed value to control if the error should be displayed. We update it in submit method, and cancel it when we click it checkbox for demonstration purpose.
new Vue({
el: "#app",
data: {
termsState: false,
validated: false
},
computed: {
termsError() {
return this.validated && !this.termsState
}
},
methods: {
handleTermsState() {
this.validated = false
},
handleSubmit() {
this.validated = true
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='app'>
<label for="terms">
Terms and Privacy Policy
<input type="checkbox" id="terms" name="terms" v-model="termsState" #change="handleTermsState">
{{ termsState }}
</label>
<p style="color: red" class="for-error terms-error" v-if="termsError">You have to agree the terms and privacy condition.</p>
<div><button type="submit" #click="handleSubmit">Submit</button></div>
</div>
From your scenario, what I understood, the validation is not happening when the user didn't check privacy and policy. If the user ticks and unticks the checkbox, the validation is working.
If that's the case, what you can do is check the child component "Checkbox.vue" data property "checkboxValue" value, as the default value is already false, and it will be true if the user did the action and tick the checkbox. Just before submitting the form, add the checkboxValue condition check.
Here is the updated Register.vue component file:
<template>
<BasicCheckbox
:id="'terms-privacy'"
:callback="onTermsClick"
:label-text="'terms and conditions'"
:is-label-html-text="true"
ref="BasicCheckbox"
:checkbox-error="termsPrivacyError"
class="terms-privacy"
/>
</template>
<script>
methods: {
validateData: function (data) {
if (!this.termsPrivacyError && this.$refs.BasicCheckbox.checkboxValue) {
this.sendRegistration(data).then(response => {
if (response) {
console.log('Registration successful');
this.loginUser({email: data.email, password: data.password}).then(response => {
if (response) {
console.log('User logged in!');
this.$router.push({name: ROUTE_NAMES_HOME.HOME});
}
})
}
});
}
},
// Terms and Privacy Checkbox
onTermsClick: function (checkboxId, checkboxData, data) {
this.termsPrivacyError = !data ? termsPrivacyErrorText : '';
},
}
</script>
What I modified only:
I added the attribute of ref for the component stage `BasicCheckbox':
ref="BasicCheckbox"
And for the validation, I just only added condition whether the ref component 'BasicCheckbox' has value `true':
if (!this.termsPrivacyError && this.$refs.BasicCheckbox.checkboxValue)
I have a custom form that makes a few requests to a database to verify the user. I noticed that if I have a single google account it works fine but it doesn't with multiple. The other thing I noticed is that the script doesn't throw any error it just doesn't communicate back the result from the custom form.
This is how my custom forms look like:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div class="container select-client">
<div class="client">Client</div>
<select class="client-select">
<option>Select Client</option>
<!-- ...options -->
</select>
<div class="market">Market</div>
<select class="market-select">
<option>Select Market</option>
<!-- ...options -->
</select>
<div class="error-message"></div>
<button class="button" id="select-button" onclick="handleSelect()">Select</button>
</div>
<script>
// ...code to validate the user
function handleSelect() {
var _client = clients.find(
(client) => client.id === parseInt(selectedClient)
);
var _market = markets.find(
(market) => market.id === parseInt(selectedMarkets)
);
if (!_client && !_market) {
return;
}
if (!_client) {
errorMessageClientMarket.innerHTML = 'Please select client';
return;
}
if (!_market) {
errorMessageClientMarket.innerHTML = 'Please select market';
return;
}
google.script.run
.withSuccessHandler()
.loginData({ token, market: _market, client: _client, user: userInfo, platform });
google.script.host.close();
}
</script>
</body>
</html>
This is how I create the custom form using app script
const loginForm = () => {
const html = HtmlService.createHtmlOutputFromFile('loginFormHtml')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
.setWidth(600)
.setHeight(600);
const ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, `Login`);
};
This is the callback function:
const loginData = (data) => { // <--- this function is ignored when a the users has multiple google accounts
console.log('LOGIN FORM');
const { token, market, user, client, platform } = data;
UserProperties.setProperty('token', token);
UserProperties.setProperty('userId', user.id);
UserProperties.setProperty('clientId', client.id);
UserProperties.setProperty('clientName', client.name);
UserProperties.setProperty('marketId', market.id);
UserProperties.setProperty('marketName', market.code_name);
UserProperties.setProperty('username', `${user.first_name} ${user.last_name}`);
UserProperties.setProperty('userEmailAddress', user.email);
UserProperties.setProperty('platform', platform);
const info = UserProperties.getProperties();
console.log('info ---> ', info)
const ui = SpreadsheetApp.getUi();
getMenu(true);
ui.alert('Logged in Successfully');
};
Does anyone know if there's a away to fix this?
i got an issue regarding checkboxes with nedb. I want to send true or false if the checkbox is checked or not to the database i cannot solve this issue. i am working with node.js and nedb. please help!
client js eventlistener:
var taskDone = document.querySelectorAll('.taskDone');
taskDone.forEach(btn => {
btn.addEventListener('click', (e) => {
var done = e.target.attributes[1].value;
let id = e.target.getAttribute('data-id');
let isDone = document.querySelector(`input[data-id=${id}]`).value;
console.log(isDone + "isdone")
if ($(taskDone).is(':checked')) {
$('.text').addClass('line-through')
console.log("trues")
$.ajax({
url: 'http://localhost:3000/done/' + id,
type: 'PUT',
data: { isDone }
}).done(function (data) {
//location.reload()
console.log(data)
})
} else {
console.log('falses')
$('.text').removeClass('line-through')
}
})
})
update function to nedb:
function taskIsDone (id, done) {
return new Promise((resolve, reject) => {
db.update({ _id: id }, { $set: done }, { returnUpdatedDocs: true }, (err, num, updateDocs) => {
if (err) {
reject(err)
} else {
resolve(updateDocs)
}
})
})
}
server:
app.put('/done/:_id', async(req, res) => {
try {
var id = req.params._id;
let done = {
title: req.body.isDone,
}
const updateToDo = await taskIsDone(id, done)
console.log(updateToDo + " Todo done");
res.json(updateToDo);
} catch (error) {
res.json({error: error.message});
}
})
html/ejs:
<% for ( var i = 0; i < row.length; i++) { %>
<div class="edit-container" >
<input type="text" name="editTask" value="<%=row[i].title %>" data-id="<%=row[i]._id %>">
<button name="<%= row[i]._id %>" class="edit" data-id="<%=row[i]._id %>">save edit</button>
</div>
<div>
<input type="checkbox" name="isDone" class="taskDone" data-id="<%=row[i]._id %>">
<span class="text"><%= row[i].title %></span>
<button class="delete" name="<%= row[i]._id %>">delete</button>
</div>
<br>
<% } %>
i could really need some help with this! thanks
I have recreated a minimal example of what you are trying to do with checkbox checked state. I have added three checkboxes with same class name .taskDone
And i have using a change function not a click function. Every-time you clicked on the checkbox and check it will show the console log with checked and the data-id of that checkbox as well.
To get the data-id you can simply use .data function of jQuery and just specify what you want after the data-** to get it stored value.
In addition, do not use fat arrow - => function with jQuery. Use normal function statements so you can access you things by using $(this) instead of specifying each class or id
Live Working Demo:
let taskDone = document.querySelectorAll('.taskDone'); //get all the chechbox with same class .taskDone
taskDone.forEach(function(btn) { //use normal function
btn.addEventListener('change', function() {
let id = $(this).data('id') //get the data id of checkbox
if ($(this).is(':checked')) { //check if the clicked checkbox is checked or not
console.log(id + ' is Checked - Updating neDB') //console.log
$.ajax({
url: 'http://localhost:3000/done/' + id,
type: 'PUT',
data: 'isDone'
}).done(function(data) {
console.log(data)
})
} else {
console.log("Not Checked")
}
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="checkbox" name="isDone" class="taskDone" data-id="1">
<input type="checkbox" name="isDone" class="taskDone" data-id="2">
<input type="checkbox" name="isDone" class="taskDone" data-id="3">
I know this has a simple answer but I appear to be stuck. I have an upload image input in a form. Following several tutorials, I have successfully created the upload method. My issue is once the image is uploaded to Firestore storage I use this.$emit('imgurl', downloadURL)
My problem is I do not know how to get that value so when the user submits the form the url gets added to the database.
Parts of the code:
HTML:
<div class="field avatar">
<label for="avatar">Avatar</label>
<input type="file" name="imgurl" accept="image/*" #change="detectFiles($event.target.files)">
<div class="progress-bar green" :style="{ width: progressUpload + '%'}">{{ progressUpload }}%</div>
<img class="avatar" v-bind:src="this.downloadURL">
</div>
Methods:
detectFiles (fileList) {
Array.from(Array(fileList.length).keys()).map( x => {
this.upload(fileList[x])
})
},
upload (file) {
var storage = firebase.storage();
this.uploadTask = storage.ref('avatars/'+file.name).put(file);
}
Watch:
watch: {
uploadTask: function() {
this.uploadTask.on('state_changed', sp => {
this.progressUpload = Math.floor(sp.bytesTransferred / sp.totalBytes * 100)
},
null,
() => {
this.uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
this.downloadURL = downloadURL
this.$emit('imgurl', downloadURL)
})
})
}
}
Add to the database:
db.collection('teams').add({
team_name: this.team_name,
team_id: this.team_id,
bio: this.bio,
url: this.imgurl,
}).then(() => {
this.$router.push({ name: 'Admin' })
}).catch(err => {
console.log(err)
})
You can pass a function as a prop to a child component, then call this function passing your downloadURL as argument.
Parent Component
HTML
<child passURL="getDownloadURL">
JS
data: {
return {
downloadURL: null
}
},
methods: {
getDownloadURL: function(url) {
this.downloadURL = url
}
}
Child Component
JS
props: ['passURL'],
Inside your watcher, you can call
this.passURL(downloadURL)
Instead of $emit.
I found the answer. I added a hidden input field
<input type="hidden" name="imgurl" v-model="imgurl">
and replaced the emit with this.imgurl = downloadURL
I've looked at all the threads that already exist on this topic and have not been able to come up with a solution for my case.
I have multiple forms rendering with the help of Handlebars like this:
<ul>
{{#each listRecords}}
<form id="form{{id}}" action="/expand-list-records-save/{{../listId}}/{{id}}" method="POST">
<div class="record-box">
<li>{{recordTitle}} by {{artistName}} ({{releaseYear}})
<br>
<div>
<label>Paste media embed code here:</label>
<textarea class="form-control form-control-lg" name="embeddedmedia" cols="30" rows="10">{{embeddedMedia}}</textarea>
</div>
<br>
<br>
</li>
</div>
</div>
</form>
{{/each}}
</ul>
<input id="submit" class="btn btn-secondary btn-block" type="submit" value="Submit embed code" >
<script>
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
for (var i=0; i < document.forms.length; i++) {
console.log(`submitting ${document.forms[i].id}`)
document.forms[i].submit();
}
})
})
</script>
my Node.js + Express.js route looks like this
router.route('/expand-list-records-save/:listId/:recordId')
.post((req, res) => {
// console.log(req)
Record.findOne({
where: {
id: req.params.recordId
}
}).then(result => {
// console.log(req.body)
result.update({
embeddedMedia: req.body.embeddedmedia
})
}).then(() => {
console.log("sending list to view")
sendListDataToView({ params: {id: req.params.listId} }, res, 'view-list')
})
})
I'm having a few problems. First of all, this logic only executes a POST request for the item that the very last form on the page is for. Why is it that the console.log works for every single instance in my loop when iterating through all the document forms? From what I know, I think I need to use AJAX here somehow to execute all the POST requests. And the second main thing that I don't think is giving me problems at this point, but will once I get the first issue solved, is that my route is not written to handle a batch of requests like I need it to.
UPDATE
Upon a recommendation in comments, I decided try and write an Ajax request to post all of the forms to a separate route which will handle it from there. How do I pass an array of forms to the data parameter? I get the Uncaught RangeError: Maximum call stack size exceeded error this way:
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
$.ajax({
type: 'POST',
url: window.location.origin + $('h3')[0].innerText,
data: document.forms,
success: (data) => {
console.log(data)
}
})
})
})
After going through some other examples, I tried rewriting original submit script like this. And, in this case, it does not pick up the action attribute.
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
$('form').each(() => {
var that = $(this);
$.post(that.attr('action'), that.serialize())
})
})
})
So, I have finally come up with a solution, if anyone is interested. Perhaps not prettiest, but it works.
<script>
$(document).ready(() => {
$('#submit').click(function submitAllForms() {
var counter = 0;
var totalForms = $('form').length;
$('form').each((i, form) => {
const redirectIfDone = () => {
if (counter === totalForms) {
alert("all forms updated successfully")
window.location.replace(window.location.origin + '/list/' + $('h3')[0].innerText)
}
}
if (!($(form).find('textarea').val())) {
counter++
redirectIfDone();
return true;
} else {
$.ajax({
type: 'POST',
url: $(form).attr('action'),
data: $(form).serialize(),
success: (data) => {
counter++;
redirectIfDone();
}
})
}
})
})
})
</script>
Virtually no changes to the route. Overall, I'm still interested in seeing other possible solutions.
router.route('/expand-list-records-save/:listId/:recordId')
.post((req, res) => {
Record.findOne({
where: {
id: req.params.recordId
}
}).then(result => {
result.update({
embeddedMedia: req.body.embeddedmedia
})
res.end()
})
})