Sorry, this is my first ever question, and I'm probably just dumb and don't understand something easy.
I have a webpage made in HTML and React there is a text box and button. I am trying to call a JSX function on click of the button. When you click the button, it is supposed to run some JSX code that will get something from the GitHub API and return it as HTML. That is why I am using JSX and not JavaScript. I found this code on another question and I modified it. I am new to JavaScript and JSX. When I put something in the textbox and click the button, the console logs: myFunction is not defined.
Is there any way to fix this?
I have a separate file called index.jsx that has all the code inside. Here it is:
/**
* See https://developer.github.com/v3/repos/branches/#get-branch
*
* Example Github api request:
* https://api.github.com/repos/ta-dachi/eatsleepcode.tech/branches/master
*/
class LatestCommitComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
author: "",
branch: "",
date: "",
sha: "",
link: ""
};
}
componentDidMount() {
// Replace this with your own repo
// https://api.github.com/repos/:owner/:repo/branches/master
fetch(
// This right below is what I am trying to replace from the text box
"https://api.github.com/repos/datkat21/datkat21.github.io/branches/master"
)
.then(response => {
response.json().then(json => {
console.log(json);
this.setState({
author: json.commit.author.login,
authorurl: json.commit.author.url,
avatar: json.commit.author.avatar_url,
branch: json.name,
date: json.commit.commit.author.date,
link: json._links.html,
authorpage: json.commit.author.html_url,
commit_url: json.commit.html_url,
commit_title: json.commit.commit.message,
});
});
})
.catch(error => {
console.log(error);
});
}
render(){
return (
<div>
// This part shows the response from the GitHub API as HTML shown in the browser
<div>{this.state.author}<br></br><a href={this.state.authorpage}><img src={this.state.avatar} alt="Loading..." class="img" /></a></div>
<div><a href={this.state.link}>Branch: {this.state.branch}</a></div><br></br>
<div>Message: {this.state.commit_title}</div>
<div>Date: {this.state.date}</div>
<div></div>
</div>
);
}
;}
ReactDOM.render(<LatestCommitComponent />, document.getElementById("root"));
Sorry if the code is too long.
I don't know how to call that file on click of the button. If anyone could help, that would be good!
Thanks.
Edit: Here is the HTML code:
<head>
<title>Begin</title>
<link type="text/css" rel="stylesheet" href="../../css/style.css" />
<script type="text/jsx" src="../render.jsx"></script>
<script type="text/jsx" src="index.jsx"></script>
<script type="text/javascript">
function myFunction() {
var GitUrl = document.getElementsByName("GitUrl")[0].value;
render(); // The function returns the product of p1 and p2
}
</script>
<script type="text/jsx" render() { render; return ( <div>
<div>{this.state.author}<br></br><a href={this.state.authorpage}><img src={this.state.avatar} alt="Loading..." class="img" /></a></div>
<div><a href={this.state.link}>Branch: {this.state.branch}</a></div><br></br>
<div>Message: {this.state.commit_title}</div>
<div>Date: {this.state.date}</div>
<div></div>
</div>
);
}
</script>
</head>
<body>
<form action="begin-create-done.html" method="get">
User/Repo URL:<br><br> <input type="text" name="GitUrl">
<br>
</form>
</body>
<button onclick="myFunction()">Get data</button> <!--button that should run script-->
</html>
Sorry if I'm just dumb and dont understand something. I'm trying to call the script when you click on the button as defined in the HTML code
while I don't see your button in your code, within your class you can create a function called fetchGithub (or something like that) then you add a property to your button that MIGHT look like this
<button onClick={fetchGithub}>Fetch Github</button>
Inside the fetchGithub function you can make a fetch call to the API and get your data. Then you can setState with the results and display it on your page.
edit: english
Related
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
};
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.
I've got a single page application inside of my larger application that will send data to the DB, and will also display that same table's data. Currently, I have AJAX dynamically sending the data. However, to get the data just inserted to appear in the table that I want it to, I have to refresh. I've been trying things all morning, but below is the current state of things.
The View:
<html>
<head>
<!--I took some stuff out to make it easier to look at -->
</head>
<body onresize="resizeRecalc()">
<div class="container-fluid">
<div class="row header">
<div class="col-12">
<img src="{{ URL::asset('images/takeStatsLogo.png') }}" id="header-logo" />
</div>
</div>
<div class="mainArea row">
<div class="left col-8">
<div onclick="playPause()" class="embed-responsive embed-responsive-16by9">
<video id="gameFilm" src="{{ URL::asset('images/basketball.mp4') }}" preload="metadata"></video>
</div>
<div class="timebar">
<span class="timeItem" id="timestamp"></span>
<div onclick="changeVidTime()" onmousemove="moveLine(event)" onmouseout="REmoveLine()" id="outerBox"> <div id="progressBox"> <div id="placeMarker">
</div></div></div>
<span class="timeItem" id="duration-place"></span>
</div>
<!-- This is a key part -->
<div id="statList">
#include('partials.statList')
</div>
</div>
<div id="right" class="right col-4">
<!--Checking if we should make the user select starters. If we have them, no need to do that...-->
#if ($game->starters != null)
#include('partials.areStarters')
#else
#include('partials.noStarters')
#endif
</div>
</div>
</div>
<script>
//Add Stat Form
//This part here will add the stats, but it won't refresh them!
$('input#addStatButton').click( function() {
$.post( '{{action("StatController#store")}}', $('form#new_stat').serialize(), function(data) {
$('#statList').load('/take-stats/{{$game->id}}');
},
'json' // I expect a JSON response
);
clearStat();
});
</script>
<script src="{{ URL::asset('js/takeStats/genJavascript.js') }}"></script>
<script src="{{ URL::asset('js/takeStats/videoJS.js') }}"></script>
<script src="{{ URL::asset('js/takeStats/dataJS.js') }}"></script>
</body>
</html>
Here is the controller method:
public function loadStatList($id) {
$userType = Auth::user()->user_type;
if(Auth::check() && Game::where('id', '=', $id)->exists() && ($userType == 'statistician' || $userType == 'admin')) {
$game = Game::find($id);
$players = $game->team->users->where('user_type', 'player');
$stats = Stat::orderBy('video_timestamp', 'desc')->where('game_id', $game->id)->get();
$statMeta = Stat_Meta::all()->where('type', 'recorded');
return view('partials.statList', compact('game', 'players', 'stats', 'statMeta'));
} else {
abort(404);
}
}
I might be missing something but I thought this would do what I am trying to achieve.
I figured it out!Thank #Get Off My Lawn for giving me a bit of a hint that I couldn't just use the #include. I went ahead and figured out how to pre-render the HTML and then bring it in. It is actually not that hard. The idea here is to use a JQuery function to do an AJAX POST upon hitting submit, then use .done to get then new full webpage. After you have that (you can console.log it to see what you're working with at that point, it will be the entire webpage) you can just get the specific div you want to refresh from the .get you performed, and stick it in the same div. Here is the code:
HTML/#include:
<div id="statList">
#include('partials.statList')
</div>
The AJAX/JQuery:
$('input#addStatButton').click( function() {
$.ajax({
type: 'POST',
url: '{{action("StatController#store")}}',
data: $('form#new_stat').serialize(),
})
.done(function(refresh) {
clearStat();
$.get('{{action("StatController#show", [$game->id])}}', function(data) {
var newData = $("#statList" , data)
$( "#statList" ).html( newData );
//console.log(newData);
});
});
});
I'M SO HAPPY Y'ALL!!!
As discussed this is not an answer on your question but a simple explanation you asked in the comments. And it can help somebody else
Laravel and JQuery
How powerfull :-)
First i will try to fit this as much as possible to your needs with the information your provided.
Secondly jquery contains some cool ass functions a lot of people don't know about.
As you described you have a single page website or something like that. That means you have 1 route to show the single page i suggest /take-stats/{{$game->id}}.
In your controller and i use as example the GameController you have something like the following.
class GameController
{
public function __construct()
{
}
//the single page view
public function index()
{
//your singlepage logic here....
return view('games.index'); //something like this?
}
public function create() //this is where you post to
{
//logic to store the game stats...
//this is where you return a succes message or something.
//lets return the index instead :-)
//dont't return $this->index! use redirect to the route.
return redirect()->route('the.route.to.your.index');
}
}
As you see above, we return the single page in the post response. SSo when you post to the store method, and it succeeds it returns the index page.
Now the jquery.
$('input#addStatButton').on( function() {
//this is where to do the post.
$.post(`{{ route('to.your.store.route') }}`, $('form#new_stat').serialize(), (response) => {
//clear the stats because the post is succeeded in here
//also reload the content. The response contains the new html content
//now what we can do is replace the whole content....
//something like $(`html`).html('response);
//or we get the content we need from the response and this is where jquery comes in handy. The response is simply a html response so jquery can create a new dom from it.
let statsList = $(response).find(`#statslist`).html(); //create a dom element of the response.
$(`#statslist`).html(statslist); //put the filtered html in the current list.
//thats all :-)
}).fail(() => {
// a fail save. When the post fails it will come in here.
}).always(() => {
clearStats();
//this is called always even when it fails. You can clear the stats or something in here.
});
});
A short description :
Onclick post button, post to post.route
Controller method does logic and returns as success the index url.
jquery parses the html response and replaces the original content.
done.
I hope this helps you or somebody else. When using a structure like above this code is simply cleaner and faster for it only executes one request.
I am new to React JS and written some React JS code directly to HTML and it is working fine.
Now when I converted this inline code to JS using babel converter using Online Babel Converter and link the Converted JS to HTML, it is showng the blank the UI with no error or warning on browser console window.
I've written the inline babel script in <script type="text/babel> ... </script> tag
Note: I converted the inline code with default selected options in Online Babel Converter, Evaluate in Settings, stage-2 & react in Presets
Edit: Added some portion of code
<!DOCTYPE HTML>
<html>
<body>
<script type="text/babel>
class App extends React.Component {
createCircles = () => {
let circles = [];
for(let i = 1; i <= this.props.count; i++){
circles.push(<div className = "smallCircle" id={'circle'+i} key={i}><code className="circles" id={'id'+i}>{i}</code></div>);
}
return circles;
}
render(){
return (
<div id="circles">
<div className = "bigCircle" id="bigCircle">
<img id="bigCircleImage" src="http://localhost" />
</div>
<div className = "smallCircles">
{this.createCircles()}
</div>
</div>
);
}
}
function AppLoader(){
return (
<App />
);
}
ReactDOM.render(<AppLoader />, document.getElementById('root'));
</script>
<div id="root"></div>
</body>
</html>
Agreed with #JoeWarner answer to not to use extra AppLoader function if you are not returning more than one component.
Coming to the question, I saw that you written the script before the div tag of id root. After converting your script, import the script below the tag to see the changes
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);"/>