Thymeleaf - How to interact and reload Javascript? - javascript

Without page refresh, I am calling an ajax request that goes to the controller and brings a Thymeleaf fragment,
On button click, I request the controller using ajax to get the fragment. Every Time I click on the button same fragment gets called but with a different <p> tag innerHTML.
<div id="express" th:fragment="Display">
<p id="app">Include details about your goals</p>
</div>
For the first time, js works very well, split into an array and add span tag but the issue with the new content which is dynamically replaced.
The issue is on the javascript side when the new fragments with different innerHTML inside of the <p> tag come the Javascript did not work. I need to refresh the page to re-run the js which I don't want to refresh.
here is the js
let comp = document.getElementById("app").innerHTML;
let struct = comp.split(" ");
document.getElementById("app").innerHTML = struct.reduce((acc, word, index) => {
return `${acc} <span class="word">${word}</span>`;
}, "");
const handleClick = () => {
axios.post(url)
.then(response => {
console.log(response.data);
$("#express").replaceWith($recievedHTML);
})
.catch(error => {
console.log(error);
});
}

Let's say you have your HTML page, which has your JS file or you have added your JS inside <script> tag directly. Something like ...
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<script src="/path/lib/jquery/3.5.1/jquery.min-dc5e7f18c8d36ac1d3d4753a87c98d0a.js" type="text/javascript"></script>
<script src="/path/js/your_file.min-cf4fcae72d4468307341eac1012bb6d8.js" type="text/javascript"></script>
</head>
<body>
<div id="express" th:fragment="Display">
<p id="app">Include details about your goals</p>
</div>
</body>
</html>
Javascript for this page may look like ...
$(function() {
function my_load_function() {
// do your loading procedures here
// for example I just copied your inline code...
let comp = document.getElementById("app").innerHTML;
let struct = comp.split(" ");
document.getElementById("app").innerHTML = struct.reduce((acc, word, index) => {
return `${acc} <span class="word">${word}</span>`;
}, "");
}
function my_do_something_else_function() {
// dosomething else over here
}
const handleClick = () => {
axios.post(url)
.then(response => {
console.log(response.data);
// what is '$recievedHTML'? should it be response.data?;
// ayway, you said replacement works;
$("#express").replaceWith($recievedHTML);
// after this call any function again
my_load_function();
my_do_something_else_function();
})
.catch(error => {
console.log(error);
});
};
// call your loading function first time
my_load_function();
});
I have put my additional explanation in the comments of JS code. Please note, I just write it directly in SO and didn't even try to run, so there are maybe some typos.

Related

Cannot run js script after fetch it (vanilla js)

Hello I've got a problem with fetch data in my javascript. I want to fetch api but everytime when I want to do this, somethink goes wrong. First I want to fetch html template, and this is working fine but when I want to use JS script from this file then it shows that 'function is not defined' I was trying many ways to do that. Below is code what doesn't work
var dataset;
fetch('../templates/mainPage.html')
.then(response => response.text())
.then(data => {
dataset = data;
document.getElementById('mainContent').innerHTML = dataset;
//var script = document.createElement('script');
//script.src = '../js/mainTmpl.js';
//document.body.appendChild(script);
//document.body.innerHTML += '<script defer src="../js/mainTmpl.js"></script>';
//script.addEventListener('load', logDataset);
logDataset();
});
and below is template what I want to use (HTML template and JS script is added to main HTML but that show me 'logDataset is not defined'
<div class="content">
<div class="main-container">
<span class="main-text" id="main-text">{{TEST}}</span>
<div class="blue-line"></div>
<span class="sub-text" id='sub-text'></span>
</div>
</div>
<script>
window.logDataset = function () {
var thathref = window.location.pathname;
document.getElementById('mainContent').innerHTML = dataset;
var mainText = document.getElementById('main-text');
var subtext = document.getElementById('sub-text');
console.log('test');
fetch('http://localhost:8080/'+thathref).then(
function(u){ return u.json(); }
).then(function(data){
var data1 = JSON.parse(JSON.stringify(data));
mainText.innerHTML = data1[0].text;
subtext.innerHTML = data1[1].text;
console.log(data1[0].text);
}
);
}
I've comment different methods that I tried to have working fine but it's still bad (I have to have JS script in other files)
Below screen of error Error
Error2
https://stackblitz.com/edit/js-1clyno?file=index.html
12345678901236789012345678901

Asp.net Project - Javascript button is clickable but not carrying out the function

I've been trying to integrate an api into a project that I have been working on with some friends but I'm having difficulty with getting the "ok" button to actually execute the function. It's supposed to allow you to upload a photo, click ok, and then it returns data about the plant. The "choose files button works, but the ok button doesn't.
Since the API sample was already created I tested it in a separate solution and was able to get it to work which leads me to believe that I've made a mistake in the code somewhere else or maybe there's something blocking the program from talking to API's web address. But for some reason it doesn't work within the project that I'm trying to integrate it into. (ASP.NET razor page).
I've also tried making a new button and moving the javascript tag into the header and other spots but that didn't work either, and I've run out of ideas to try. I have omitted the api key itself below for the sake of privacy. I'd really appreciate any help on the subject!
#{
ViewData["Title"] = "Identify a Plant";
}
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<form>
<input type="file" multiple />
<!--<button type="button">OK</button> -->
<button type="button">OK</button>
</form>
<script type="text/javascript">
document.querySelector('button').onclick = function sendIdentification() {
const files = [...document.querySelector('input[type=file]').files];
const promises = files.map((file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
const res = event.target.result;
console.log(res);
resolve(res);
}
reader.readAsDataURL(file)
})
})
Promise.all(promises).then((base64files) => {
console.log(base64files)
const data = {
api_key: "Die8ewFGvpw5JrRTuOEjgGR10uL--",
images: base64files,
modifiers: ["crops_fast", "similar_images"],
plant_language: "en",
plant_details: ["common_names",
"url",
"name_authority",
"wiki_description",
"taxonomy",
"synonyms"]
};
fetch('https://api.plant.id/v2/identify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
})
};
</script>
</body>
</html>
I think you are better off to give the button a "ID" and you don't.
and I never really did like this idea of "selecting" some button and then hoping we can attached a click event to that button. So, I always preferred that you have a button. You place a button. You specify a click event for that button. You have to really struggle to figure out which button that selector going to pick up - which one does it like?
And then when you click on that button, the code or function runs. It just a lot easier to follow.
So, your button thus is this:
<form id="form1" runat="server">
<div>
<br />
<input id="myfiles" type="file" multiple="multiple" />
<!--<button type="button">OK</button> -->
<button id="MyButton" type="button" onclick="sendIdentification()" >OK</button>
</div>
</form>
<script type="text/javascript">
function sendIdentification() {
alert('start');
const files = [...document.querySelector('input[type=file]').files];
etc.
The problem is that selector for the click event is subject to the order of the controls and things on the page - might not be picked up correct.
So, just drop in a button. State what that button supposed to do on some click event, and this should help here.
With the querySelector method you add the onClick event on a first button within the document. Since the _Layout.cshtml is rendered first, my first assumption is that you have a button in that view? What about giving an id to the button and adding the onClick event like this:
document.getElementById("myButton").onclick = function sendIdentification() {
//the code
};

How do I read the data from text file into a variable so I can change the color based on the value?

The goal of my project is to read a stream of constantly changing data from a text file into a html file and then changing the font color.
I have tried multiple ways of reading in a text file such as using an iframe and attempting to use the fetch command but was unable to successfully use these to complete my goal.
<body>
<script type="text/javascript">
function print() {
fetch("test.txt")
.then(function(response) { return response.text; })
.then(function(text) {
var num = parseInt(text);
if(num>250){
num=fontcolor("green")
}
else()
{
num=fontcolor("red")
}
});
}
setInterval(print, 1000); //run function print() every 1 second
</script>
<META HTTP-EQUIV="refresh" CONTENT="1">
</body>
The outcome should be an html file that when opened in a browser will display the number in the text file and change the color based on the number given.
You can not fetch text file in local machine for security reason.
You can refer this link for update security for
Disable same origin policy in Chrome
You can host your file to free host. I corrected your .then(response => response.text())
Corrected
<body>
<script type="text/javascript">
function print() {
fetch("test.txt")
.then(response => response.text())
.then(text => {
var num = parseInt(text);
if(num>250){
console.log(num);
document.body.style.backgroundColor = "green";
}
else
{
//num=fontcolor("red")
console.log(num);
document.body.style.backgroundColor = "blue";
}
})
}
setInterval(print, 1000); //run function print() every 1 second
</script>
<META HTTP-EQUIV="refresh" CONTENT="1">
</body>
http://viethien.000webhostapp.com/fetchfile.html

How to use the same HTML render different content via different urls?

I have an HTML page and multiple URLs as follows
var app = angular.module('app', []);
app.controller('index', ($scope,$http) => {
$http.get('/get_js').then((res) => {
$scope.cat = res.data;
}, () => {
alert('database error');
});
});
/get_js
/get_node
/get_ng
......
I expect if I query different url, such as $http.get('/get_js') or $http.get('/get_node') or $http.get('/get_ng') it will render different content on the same HTML.
For example, I have several links. If I click on 'a'link, HTML will render some content via '/get_js'. If I click on 'b'link, HTML will render some content via '/get_ng' and so on. Each time, I will click one link.
Could anyone tell me how can I do that?
Thank you very much!
PS: I am using an express server to host the content.
Then you can take a function and make appropriate api call in that function when a link is clicked by sending the parameter.
Steps:
Write a funciton which calls each api dynamically depending on the
parameter.
Send the api url as a parameter from the respective anchot tag to
that function.
Make the api call in that function and show the response in html
Here is how the function may look, I commented your API call.
$scope.linkClicked = function(link)
{
// $http.get('/get_js').then((res) => {
// $scope.data = res.data;
// }, () => {
// alert('database error');
// });
$scope.data = link + ' clicked';
}
Code:
var app = angular.module('myApp', []);
app.controller('index', function($scope) {
$scope.linkClicked = function(link)
{
// $http.get('/get_js').then((res) => {
// $scope.data = res.data;
// }, () => {
// alert('database error');
// });
$scope.data = link + ' clicked';
}
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="index">
<a ng-click="linkClicked('get_js')"> Link1 </a>
<a ng-click="linkClicked('get_node')"> Link2 </a>
<a ng-click="linkClicked('get_ng')"> Link3 </a>
<p ng-if="data"> {{data}} </p>
</div>
</body>
</html>
Please run the above snippet
Here is a working DEMO

React: Script tag not working when inserted using dangerouslySetInnerHTML

I'm trying to set html sent from my server to show inside a div using dangerouslySetInnerHTML property in React. I also have script tag inside it and use functions defined in same inside that html. I have made example of error in JSFiddle here.
This is test code:
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
var Hello = React.createClass({
displayName: 'Hello',
render: function() {
return (<div dangerouslySetInnerHTML={{__html: x}} />);
}
});
I checked and the script tag is added to DOM, but cannot call the functions defined within that script tag. If this is not the correct way is there any other way by which I can inject the script tag's content.
I created a React component that works pretty much like dangerouslySetInnerHtml but additionally it executes all the js code that it finds on the html string, check it out, it might help you:
https://www.npmjs.com/package/dangerously-set-html-content
Here's a bit of a dirty way of getting it done ,
A bit of an explanation as to whats happening here , you extract the script contents via a regex , and only render html using react , then after the component is mounted the content in script tag is run on a global scope.
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
var extractscript=/<script>(.+)<\/script>/gi.exec(x);
x=x.replace(extractscript[0],"");
var Hello = React.createClass({
displayName: 'Hello',
componentDidMount: function() {
// this runs the contents in script tag on a window/global scope
window.eval(extractscript[1]);
},
render: function() {
return (<div dangerouslySetInnerHTML={{__html: x}} />);
}
});
ReactDOM.render(
React.createElement(Hello),
document.getElementById('container')
);
I don't think you need to use concatenation (+) here.
var x = '<html><scr'+'ipt>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</scr'+'ipt><body><p onClick="pClicked()">Hello</p></body></html>';
I think you can just do:
var x = '<html><script>alert("this.is.sparta");function pClicked() {console.log("p is clicked");}</script><body><p onClick="pClicked()">Hello</p></body></html>';
Since it's passed to dangerouslySetInnerHTML anyway.
But let's get back to the issue. You don't need to use regex to access the script tag's content. If you add id attribute, for example <script id="myId">...</script>, you can easily access the element.
Let's see an example of such implementation.
const x = `
<html>
<script id="myScript">
alert("this.is.sparta");
function pClicked() {console.log("p is clicked");}
</script>
<body>
<p onClick="pClicked()">Hello</p>
</body>
</html>
`;
const Hello = React.createClass({
displayName: 'Hello',
componentDidMount() {
const script = document.getElementById('myScript').innerHTML;
window.eval(script);
}
render() {
return <div dangerouslySetInnerHTML={{__html: x}} />;
}
});
If you have multiple scripts, you can add a data attribute [data-my-script] for example, and then access it using jQuery:
const x = `
<html>
<script data-my-script="">
alert("this.is.sparta");
function pClicked() {console.log("p is clicked");}
</script>
<script data-my-script="">
alert("another script");
</script>
<body>
<p onClick="pClicked()">Hello</p>
</body>
</html>
`;
const Hello = React.createClass({
constructor(props) {
super(props);
this.helloElement = null;
}
displayName: 'Hello',
componentDidMount() {
$(this.helloElement).find('[data-my-script]').each(function forEachScript() {
const script = $(this).text();
window.eval(script);
});
}
render() {
return (
<div
ref={helloElement => (this.helloElement = helloElement)}
dangerouslySetInnerHTML={{__html: x}}
/>
);
}
});
In any case, it's always good to avoid using eval, so another option is to get the text and append a new script tag with the original's script contents instead of calling eval. This answer suggests such approach
a little extension for Dasith's answer for future views...
I had a very similar issue but the in my case I got the HTML from the server side and it took a while (part of reporting solution where backend will render report to html)
so what I did was very similar only that I handled the script running in the componentWillMount() function:
import React from 'react';
import jsreport from 'jsreport-browser-client-dist'
import logo from './logo.svg';
import './App.css';
class App extends React.Component {
constructor() {
super()
this.state = {
report: "",
reportScript: ""
}
}
componentWillMount() {
jsreport.serverUrl = 'http://localhost:5488';
let reportRequest = {template: {shortid: 'HJH11D83ce'}}
// let temp = "this is temp"
jsreport.renderAsync(reportRequest)
.then(res => {
let htmlResponse = res.toString()
let extractedScript = /<script>[\s\S]*<\/script>/g.exec(htmlResponse)[0];
// console.log('html is: ',htmlResponse)
// console.log('script is: ',extractedScript)
this.setState({report: htmlResponse})
this.setState({reportScript: extractedScript})
})
}
render() {
let report = this.state.report
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<h2>Welcome to React</h2>
</div>
<div id="reportPlaceholder">
<div dangerouslySetInnerHTML={{__html: report}}/>
</div>
</div>
);
}
componentDidUpdate() {
// this runs the contents in script tag on a window/global scope
let scriptToRun = this.state.reportScript
if (scriptToRun !== undefined) {
//remove <script> and </script> tags since eval expects only code without html tags
let scriptLines = scriptToRun.split("\n")
scriptLines.pop()
scriptLines.shift()
let cleanScript = scriptLines.join("\n")
console.log('running script ',cleanScript)
window.eval(cleanScript)
}
}
}
export default App;
hope this is helpful...
Just use some known XSS tricks. We just had a case where we had to inject a script and couldn't wait for the release so here goes our loader:
<img src onerror="var script = document.createElement('script');script.src = 'http:';document.body.appendChild(script);"/>

Categories

Resources