inline javascript callback onclick using mustache templating - javascript

Screenshot of UI
I'm having an issue invoking the inline javascript inside the mustache template file (.hjs).
when I click "Verify", the script tag and console logs do not run. It is not pulling the input code I type into the input box either.
For context: I am sending the mustache template (html) from my node server to an iFrame on the front end (React). I want the template to interact with the user and send an API call to my server and verify the 2FA.
I am sending variables to the javascript through {{ var }}, which is standard for mustache.
My thoughts: this code works in a regular index.html file.
any help or tips appreciated! I can try any suggestions locally to debug further.
<!DOCTYPE html>
<html>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<body>
<div id="inline-widget" style="margin:0 auto;width: 360px;padding:5px">
<p style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Roboto','Oxygen','Ubuntu','Cantarell','Fira Sans','Droid Sans','Helvetica Neue',sans-serif;color:#48545d;font-size:14px;line-height:125%;margin:10px auto 20px;text-align:center">
Please complete your purchase by entering the 4 character code at the end of your recent charge description.
</p>
<img style="width: 350px;text-align:center;border:1px solid black" src="https://d2xxy1rwbjzckp.cloudfront.net/verification.jpeg" alt="Example"></img>
<p style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Roboto','Oxygen','Ubuntu','Cantarell','Fira Sans','Droid Sans','Helvetica Neue',sans-serif;color:#48545d;font-size:11px;line-height:125%;margin-bottom:10px auto 20px;text-align:left">
Code = 3122 in this example
</p>
<p id="error-message" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Roboto','Oxygen','Ubuntu','Cantarell','Fira Sans','Droid Sans','Helvetica Neue',sans-serif;color:#48545d;font-size:11px;line-height:125%;margin-bottom:10px auto 20px;text-align:center;color:red"></p>
<div class="input-group mb-3">
<input id="2faCode" type="text" class="form-control" placeholder="4 digit code" aria-describedby="basic-addon2"/>
<div class="input-group-append">
<button class="btn btn-outline-secondary" id="verifyButton" type="button">Verify</button>
</div>
</div>
</div>
<script>
const button = document.getElementById('verifyButton');
button.addEventListener('click', async _ => {
try {
const verifyCode = document.getElementById('2faCode').value;
console.log('start!: ', verifyCode);
const response = await fetch({{ callbackUrl }}, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
orderId: {{ orderId }},
verificationCode: {{ verifyCode }}
})
});
const contentType = response.headers.get("content-type");
if (contentType === 'text/html; charset=utf-8') {
const textResponse = await response.text();
document.getElementById("inline-widget").innerHTML = textResponse;
} else {
const parsedResponse = await response.json();
document.getElementById("error-message").innerHTML = parsedResponse.message;
}
} catch(err) {
document.getElementById("error-message").innerHTML = err;
console.error(`Error: ${err}`);
}
});
</script>
</body>
</html>

Related

How to pass raw html from jsx to Api2pdf REST API

I'm trying to find the best way to pass my html table data (React JSX) to the api2pdf REST API https://www.api2pdf.com/
I was unable to do so, which led me to reconstruct my whole html data separately then upload it
const fetchReportPdf = async () => {
await fetch("https://v2018.api2pdf.com/chrome/html", {
method: "post",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "*Auth key placed here*",
},
body: JSON.stringify({
html: `
<html style="color: green" lang="en">
<head>
<title>Daily Report for ${newDate}</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous"
/>
</head>
<body>
<div className="DailyReportTable_Container ">
<table
class="table table-bordered border-dark table-sm"
style="width: 900px"
>
<tbody>
<tr>
<th colspan="6" class="text-center">
CIVIL WORKS FOR CONSTRUCTION OF THWAKE DAM EMBARKMENT AND
ASSOCIATED WORKS
</th>
</tr>
<tr>
<th>INSPECTOR</th>
<td colspan="1">${report.User}</td>
<th>SECTION:</th>
<td colspan="4" style="padding-left: 10px">${
report.Section
}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
`,
fileName: `${newDate} ${report.Section} ${report.Shift}.pdf`,
options: {
textAlign: "left",
height: "11in",
},
}),
})
.then((res) => res.json())
.then((res) => {
document.getElementById(
"Download_Button"
).innerHTML = `<a className="Download_Button" href="${res.pdf}" >Download PDF</a>`;
});
};
This is the table, combined with fetched data from MongoDB which is stored in state then used in the html code above. Example is the ${report.User}
Placing this fetchReportPdf function in an onClick button generates a response with a download link which i can then access and save the pdf file
Using this method has been useful so far, but now i need to map data from an array in my state.
Mapping it directly inside the html code does not work. So i tried to place a script tag and execute the code from here
<script type="module">
${function rocktripFn() {
const rockTripArray = report.rocktrip;
const newArr = rockTripArray.map((item, index) => {
return `${item.Number_Of_Trips} trips made for Rock Type ${item.RockType}`;
});
const rocktrip = document.querySelector("#rockTripUL");
newArr.forEach((sub) => {
const item = document.createElement("li");
item.innerHTML = sub;
rocktrip.appendChild(item);
});
}}
rocktripFn();
</script>
report.rocktrip is the state holding the array that i want to map. But the pdf is blank in that section. The script only returns an output when i replace report.rocktrip with the actual array e.g.
{Number_Of_Trips: '45', RockType: '3A'},
{Number_Of_Trips: '32', RockType: '3B'}
But it can't map data directly from my state
I'm probably going about all this the wrong way when i'm certain there is a simpler way of doing this, how can i simplify this process with cleaner code?
Posting the jsx code directly from my component would definitely be ideal, but i don't know how.
Solution was to actually use useRef hook to obtain the innerHTML of the targeted div.
import useRef from "react";
const reportRef = useRef();
const fetchReportPdf = async () => {
const reportHTML = reportRef.current.innerHTML;
await fetch("https://v2018.api2pdf.com/chrome/html", {
method: "post",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: "Auth key placed here",
},
body: JSON.stringify({
html: `
<html style="color: green" lang="en">
<head>
<title>Daily Report for ${newDate}</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous"
/>
</head>
<body>
<div className="DailyReportTable_Container ">
${reportHTML}
</div>
</body>
</html>
`,
fileName: `${newDate} ${report.Section} ${report.Shift}.pdf`,
options: {
textAlign: "left",
height: "11in",
},
}),
})
.then((res) => res.json())
.then((res) => {
document.getElementById(
"Download_Button"
).innerHTML = `<a className="Download_Button" href="${res.pdf}" >Download PDF</a>`;
});
};
<table className="table-responsive" ref={reportRef}>

Passing in values to a JSON Post request

My original issue was it was getting the value when the user clicked on the compose button but not the send button.
I have now changed it to use a different new function when the user clicks send. However, now it's not doing anything.
Can someone tell me what I'm doing wrong? I used the onclick method in the HTML and then created the function on my Javascript page.
HTML:
<div id="compose-view">
<h3>New Email</h3>
<form id="compose-form"
method="POST">
{% csrf_token %}
<div class="form-group">
From: <input disabled class="form-control" value="{{ request.user.email }}">
</div>
<div class="form-group">
To: <input id="compose-recipients" class="form-control">
</div>
<div class="form-group">
<input class="form-control" id="compose-subject" placeholder="Subject">
</div>
<textarea class="form-control" id="compose-body" placeholder="Body"></textarea>
<input type="submit" id="sendEmail" class="btn btn-primary"/>
</form>
</div>
JS:
const element = document.getElementById('sendEmail');
element.addEventListener('click', function() {
fetch('/emails', {
method: 'POST',
body: JSON.stringify({
recipients: 'card51short#gmail.com',
subject: "buglets",
body: 'Hes a fat one'
})
})
.then(response => response.json())
.then(result => {
// Print result
console.log(result);
});
});
}
OK, in such cases you need to do an investigation of your code:
Check if fred is actually a string, and not undefined. Also, make it an explicit constant
const fred = document.querySelector('#compose-subject').value // changed
console.log(fred); // new
fetch('/emails', {
method: 'POST',
body: JSON.stringify({
recipients: 'card51short#gmail.com',
subject: fred,
body: 'Hes a fat one'
})
})
.then(response => response.json())
.then(result => {
// Print result
console.log(result);
});
If it's all good, do the next check of what is returned by the fetch:
const fred = document.querySelector('#compose-subject').value
fetch('/emails', {
method: 'POST',
body: JSON.stringify({
recipients: 'card51short#gmail.com',
subject: fred,
body: 'Hes a fat one'
})
})
.then(response => { // edited
console.log(response); // new
return response.json() // edited
}) // edited
.then(result => {
// Print result
console.log(result);
});
You should find what's not working by the end of this process
"value" attribute returns undefined for non input tags.
Try using innerHTML instead:
fred = document.querySelector('#compose-subject').innerHTML;
test your value for fred by cosole.log:
console.log(fred);

Vue not working correctly with the html (refreshing when I press a button when it isn't supposed to)

I am having a problem where my vue doesn't work correctly.When I press the button it clears out the input(it shouldn't) and does nothing.The variables codigo and payload do not show anything in the screen. Even when I change them via console. It first was having the issue where the 'app' tag wasn't being found by the script even with it on the bottom. To solve it I had to add the line Vue.config.silent=true which made the warning disappear but the code still doesn't work. I am new to vue and web design so expect basic mistakes. I am running it in the 'node' docker image container.
<!DOCTYPE html>
<html>
<head>
<title>Vue test</title>
</head>
<script src="https://cdn.jsdelivr.net/npm/vue#2/dist/vue.js" ></script >
<script>
Vue.config.silent = true;
</script>
<body>
<h2>Manipulador de Recursos</h2>
<br>
<div id='app'>
<form>
URL do Recurso: <input type="text" v-model="recurso" size=50><br><br>
Repr. do Recurso: <input type="text" v-model="repr" size=100><br><br>
Metodo HTTP:
<button v-on:click="doGet">GET</button>
<button v-on:click="doPost">POST</button>
<button v-on:click="doPut">PUT</button>
<button v-on:click="doDelete">DELETE</button> <br><br><br>
</form>
<b>Retorno:</b><br><br>
Codigo HTTP: <span v-bind:id="codigo"></span>
<br><br>
Payload: <span v-html="payload"></span>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script defer >
var myapp = new Vue({
el: "app",
data: {
"codigo":"",
"payload":"",
},
methods:{
// GET
"doGet" : function() {
console.log("GET")
this.clear();
var url = this.recurso;
axios
.get(url)
.then(function (response) {
this.codigo = response;
this.payload =response.data ;
console.log (response);
})
.catch(function (error){
this.codigo = error;
})
},
// POST
doPost : function() {
console.log("POST")
this.clear();
var url = this.recurso;
var data = this.repr;
axios
.post(url, data)
.then((response) => {
this.codigo = response;
this.payload =response.data ;
console.log(response)
})
.catch((error) => {
this.codigo = error;
})
},
//(...)
}
})
</script>
</html>
You don't want to prevent the click action, as suggested above, but the submit action of the form.
<form #submit.prevent="doGet()">
<!-- form stuff -->
</form>
At first, your methods must be inside methods property:
var myapp = new Vue({
//.......
data: {
codigo: "",
payload: ""
},
methods: {
doGet: function() { /*.......*/},
doPost: function() { /*.......*/}
}
})
and in this example your buttons can do default action (the form sending) so it may be necessary: v-on:click.prevent="doGet()".
This solved it Simple vue app doesn't show anything :
Because you didn't render anything in your root component.
In your index.html, render the app component:
<div id="app"> <app> <!-- html code, buttons etc--> </app> </div>
Adding the tags.
Along with this: adding type="button" to the buttons because since they where inside a forms they refreshed the page.
And finally I added:
document.addEventListener("DOMContentLoaded", function(event) {
Around my var myapp = new Vue({

LoopBack v3 PUT request responding with HTTP 400

I want to display the list of record into webpage . I am using vuejs for front end development and mysql for backend .I created this applications by using LoopBack. I have some list of records inside the mysql database but the problem is when i run the web page , its does not display the records and when i want to insert new records , i am getting errors on this line ..
**(index):96 PUT http://localhost:3000/api/Account/ 400 (Bad Request)
storeAccount # (index):96
submit # VM346:3
invokeWithErrorHandling # vue.js:1863
invoker # vue.js:2188
original._wrapper # vue.js:7541**
When i clicked the index.js ,its showing error in this line
fetch(API, {...
Here is code for server.js file.
// Copyright IBM Corp. 2016. All Rights Reserved.
// Node module: loopback-workspace
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
var loopback = require('loopback');
var boot = require('loopback-boot');
var app = module.exports = loopback();
app.start = function() {
// start the web server
return app.listen(function() {
app.emit('started');
var baseUrl = app.get('url').replace(/\/$/, '');
console.log('Web server listening at: %s', baseUrl);
if (app.get('loopback-component-explorer')) {
var explorerPath = app.get('loopback-component-explorer').mountPath;
console.log('Browse your REST API at %s%s', baseUrl, explorerPath);
}
});
};
// Bootstrap the application, configure models, datasources and middleware.
// Sub-apps like REST API are mounted via boot scripts.
boot(app, __dirname, function(err) {
if (err) throw err;
// start the server if `$ node server.js`
if (require.main === module)
app.start();
});
Here is my html code .
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="AccountApp">
<h1>Account List</h1>
<table>
<thead>
<tr>
<th>Id</th>
<th>Email Address</th>
<th>Created Date</th>
<th>Updated Date</th>
<td> </td>
</tr>
</thead>
<tbody>
<tr v-for="account in accounts">
<td #click="editAccount(account)" class="accountItem" title="Click to Edit">{{account.id}}</td>
<td>{{account.email}}</td>
<td>{{account.createdAt}}</td>
<td>{{account.lastModifiedAt}}</td>
<td #click="deleteAccount(account)" class="deleteAccount" title="Click to Delete">Delete</td>
</tr>
</tbody>
</table>
<form #submit.prevent="storeAccount">
<p>
<label for="email">Email</label>
<input type="text" id="email" v-model="account.email">
</p>
<p>
<label for="createdAt">Created At</label>
<input type="text" id="createdAt" v-model="account.createdAt">
</p>
<p>
<label for="lastModifiedAt">Last Modified At</label>
<input type="text" id="lastModifiedAt" v-model="account.lastModifiedAt">
</p>
<input type="reset" value="Clear" #click="reset">
<input type="submit" value="Save User 🐱">
</form>
</div>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<script>
const API = 'http://localhost:3000/api/Account/';
let AccountApp = new Vue({
el: '#AccountApp',
data: {
accounts: [],
account: {
id: '',
email: '',
createdAt: '',
lastModifiedAt: ''
}
},
created: function () {
this.getAccounts();
},
methods: {
getAccounts: function () {
fetch(API)
.then(res => res.json())
.then(res => this.account = res);
},
storeAccount: function () {
let method;
console.log('storeAccount', this.account);
// Handle new vs old
if (this.account.id === '') {
delete this.account.id;
method = 'POST';
} else {
method = 'PUT';
}
fetch(API, {
headers: {
'Content-Type': 'application/json'
},
method: method,
body: JSON.stringify(this.account)
})
.then(res => res.json())
.then(res => {
this.getAccounts();
this.reset();
});
},
deleteAccount: function (c) {
fetch(API + c.id, {
headers: {
'Content-Type': 'application/json'
},
method: 'DELETE'
})
.then(res => res.json())
.then(res => {
this.getAccounts();
});
// call reset cuz the cat could be 'active'
this.reset();
},
editAccount: function (c) {
/*
This line was bad as it made a reference, and as you typed, it updated
the list. A user may think they don't need to click save.
this.cat = c;
*/
this.account.id = c.id;
this.account.email = c.email;
this.account.createdAt = c.createdAt;
this.account.lastModifiedAt = c.lastModifiedAt;
},
reset: function () {
this.account.id = '';
this.account.email = '';
this.account.createdAt = '';
this.account.lastModifiedAt = '';
}
}
});
</script>
Here is the screenshot when i run the applications .
Here is the screenshot on Networks tab ..

Can't delete newly created list item without refreshing the page and trying again ES6

Some context: I'm trying to finish building out the delete functionality of my minimal note-taking app.
Every time I create a new note, it will appear at the end of my list of notes. However, if I try to delete the newly created note, it won't work. I have to refresh the page and try again for it to work.
I keep getting these two errors:
"Uncaught TypeError: Cannot read property 'parentNode' of null at HTMLUListElement."
"DELETE http://localhost:3000/api/v1/notes/undefined 404 (Not Found)"
Otherwise, I'm able to delete any other note with no problem.
Here is my js code:
// display list of notes on the side
const noteContainer = document.querySelector(".column is-one-quarter")
const noteList = document.querySelector(".menu-list")
fetch('http://localhost:3000/api/v1/notes')
.then(function(response) {
return response.json();
})
.then(function(notes) {
notes.forEach(function(note) {
noteList.innerHTML += `<li id="list-item" data-id=${note.id}><a id="note" data-id=${note.id} class="menu-item">${note.title}</a><i id="delete" data-id=${note.id} class="fas fa-minus-circle has-text-grey-light hvr-grow"></i></li>`
})
})
// display details of each note
const noteDetail = document.querySelector(".note-detail")
noteList.addEventListener('click', function(event) {
if (event.target.className === "menu-item") {
fetch(`http://localhost:3000/api/v1/notes/${event.target.dataset.id}`)
.then(function(response) {
return response.json()
})
.then(function(note) {
noteDetail.innerHTML = `<h1 contenteditable="true" id="title" data-id=${note.id} class="subtitle is-2">${note.title}</h1><p contenteditable="true" id="body" data-id=${note.id} class="subtitle is-6">${note.body}</p><a id="save" data-id=${note.id} class="button is-small">Save</a>`
})
}
})
// i should be able to edit the title and body of a note when i click
// on it and it should save when i click on the button.
noteDetail.addEventListener('click', function(event) {
if (event.target.id === "save") {
const noteId = event.target.dataset.id
const editTitleInput = document.querySelector(`h1[data-id="${noteId}"]`)
const editBodyInput = document.querySelector(`p[data-id="${noteId}"]`)
const singleNote = document.querySelector(`a[data-id="${noteId}"]`)
fetch(`http://localhost:3000/api/v1/notes/${noteId}`, {
method: "PATCH",
headers: {
'Content-Type': 'application/json',
'Accepts': 'application/json'
},
body: JSON.stringify({
title: editTitleInput.innerText,
body: editBodyInput.innerText
})
}).then(function(response) {
return response.json()
}).then(function(note) {
singleNote.innerText = editTitleInput.innerText
})
}
})
// when i click on the button, a form with a title and body input
// should display on the right.
const newNoteButton = document.querySelector("#create")
newNoteButton.addEventListener('click', function(event) {
fetch("http://localhost:3000/api/v1/notes")
.then(function(response) {
return response.json()
})
.then(function(note) {
noteDetail.innerHTML = `<input id="title" class="input subtitle is-5" type="text" placeholder="Title">
<textarea id="body" class="textarea subtitle is-5" placeholder="Body" rows="10"></textarea><a id="add" class="button has-text-black" style="margin-left: 594px;">Add Note</a>`
// when i click on 'add button', a new note with a title and body
// should be created and added to the list of notes.
const noteTitleInput = document.querySelector("#title")
const noteBodyInput = document.querySelector("#body")
const addNoteButton = document.querySelector("#add")
addNoteButton.addEventListener('click', function(event) {
// event.preventDefault()
fetch('http://localhost:3000/api/v1/notes', {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Accepts': 'application/json'
},
body: JSON.stringify({
title: noteTitleInput.value,
body: noteBodyInput.value
})
}).then(function(response) {
return response.json()
}).then(function(note) {
noteList.innerHTML += `<li data-id=${note.id}><a id="note" data-id=${note.id} class="menu-item">${note.title}</a><i id="delete" class="fas fa-minus-circle has-text-grey-light hvr-grow"></i></li>`
})
})
})
})
// i should be able to delete a note when i click on the button.
noteList.addEventListener('click', function(event) {
// event.preventDefault()
if (event.target.id === "delete") {
const noteId = event.target.dataset.id
// const noteListItem = document.querySelector("#list-item")
const noteListItem = document.querySelector(`li[data-id="${noteId}"]`)
const singleNote = document.querySelector(`a[data-id="${noteId}"]`)
fetch(`http://localhost:3000/api/v1/notes/${noteId}`, {
method: "DELETE",
})
// debugger
// lastNote = noteList.lastElementChild
// noteList.removeChild(lastNote)
// singleNote.parentElement.remove()
noteListItem.parentNode.removeChild(noteListItem)
noteDetail.innerHTML = ""
}
})
Here is my html code:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css">
<link href="css/hover.css" rel="stylesheet" media="all">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
<link rel="stylesheet" href="css/note.css">
<meta charset="utf-8">
<title></title>
</head>
<body>
<h1 class="title is-1">Jot</h1>
<div class="columns">
<div class="column is-one-quarter">
<p class="menu-label" style="font-size:15px;">
Notes <i id="create" class="fas fa-plus-circle has-text-grey-light hvr-grow" style="margin-left: 10px; width: 20px; height: 30px; font-size: 24px;"></i>
</p>
<ul class="menu-list">
</ul>
</div>
<div class="column is-three-fifths">
<div class="note-detail">
</div>
</div>
<div class="column">
</div>
</div>
<script src="index.js"></script>
</body>
</html>
Any help would be greatly appreciated. :)
You're nesting strings on these two lines:
const noteListItem = document.querySelector(`li[data-id="${noteId}"]`)
const singleNote = document.querySelector(`a[data-id="${noteId}"]`)
Your template literal is creating a string and you're putting that inside of quotes. For example, if your noteId is say 12. your code is ending up like this:
const noteListItem = document.querySelector("li[data-id="'12'"]")
const singleNote = document.querySelector("a[data-id="'12'"]")
I'm not 100% sure that's your issue but it's the first thing that popped out to me.
You can check out MDN to brush up on your Template literals (Template strings).

Categories

Resources