I have a piece of code that server sends to react component and this piece of code is set as a component property. This code includes a div tag and a script tag inside it. I need to execute and run this code in React component. Dangerously Set innerHTML doesn't work for me - js code isn't running in this case. Are there any variants to make this work?
Thanks in advance.
You can still use eval() for this.
But you need to be aware of the security implications it has. Especially you need to make sure you are using secure connections to your server and don't publish malicious code to your users.
React does not recommends use innerHTML. But if you very need to, you can use
function createMarkup() { return {__html: 'First ยท Second'}; };
<div dangerouslySetInnerHTML={createMarkup()} />
More info https://facebook.github.io/react/tips/dangerously-set-inner-html.html
Related
Long story short I have strings that contain HTML which are downloaded before a production build from a secure server. The framework we're using is React with Gatsby and typically you would just do the following:
<div dangerouslySetInnerHTML={{__html: '<p>My Stuff</p>'}}></div>
Which works fine, the main problem is for SEO purposes we want the html to be compiled into html rather than being rendered with Javascript.
Seeing as it's already HTML is there a way I can disable the protection inside react so these strings aren't escaped by default and become regular HTML in the production build?
Not practically, no. It's important to distinguish a <div> in a .jsx file from a <div> in html. If you look at the compiled output of the jsx, it will reveal what's really going on. If you transpile a react component containing <div>Hello world</div>, you'll get a function call along the lines of _react.createElement("div", null, "Hello world"). Anything that is put into the body of the div will also be inserted into it as JSX elements. The dangerouslySetInnerHtml prop is there to tell React that the value is actual html that you trust. However, even if there were an option other than dangerouslySetInnerHtml, I don't believe it would solve your problem as anything inside your react is going to have to wait for client side rendering.
Now, I'm no SEO expert, but if it's crucial to have content on the page before you render it, you may be able to send in the original html in a hidden element. If your original markup has <div id="app">Some seo stuff</div>, React will replace the content of the div when it renders it.
I think what you need is SSR; React allows this feature
You could install a server adapter (maybe expressjs) and use it to render a first screen (which is most likely SEO related) and once the client side is ready to render, it would hydrate the pages. Since you have the HTML gotten from a server, I think this is best route to go
var stringToHTML = function(htmlContent) {
var dom = document.getElementById('integrationMessage');
dom.innerHTML = htmlContent;
return dom;
};
stringToHTML(res.data.message);
<div id = "integrationMessage"></div>
I'm learning React: totally newbie.
If I save in DB the HTML directly from draft.js (or it's variants always based on it) and then in a view page of my React SPA I retrieve HTML from DB through my API:
QUESTIONS:
how can I render that HTML?
dangerouslySetInnerHTML? Or maybe one of this (what do you suggest?)?
https://github.com/wrakky/react-html-parser
https://github.com/pveyes/htmr
https://github.com/remarkablemark/html-react-parser
https://github.com/utatti/react-render-html
https://github.com/aknuds1/html-to-react
I read words like "sanitize", "securing HTML". But how, there is a library?
I need to secure html from draft-js when I save it in DB or after, when I'm rendering it?
Draft-JS doesn't allows you to directly generate HTML from the present EditorState and that's a good thing. Since you are not dealing with "Raw HTML" you don't have to deal with XSS attacks as Draft Editors internal state won't get altered if someone inserts a script in the Editor.
Draft JS allows you the export the current Editor state so that you can store it easily. It can be done using
import {convertToRaw} from 'draft-js';
and in your onChange Handler you can simply do
const editorJSON = JSON.stringify(convertToRaw(editorState.getCurrentContent()));
You can the store this JSON as you like for future use.
Now for Rendering this you have two options :
Generate HTML from the stored EditorState.
This can be done using Libraries like https://www.npmjs.com/package/draft-js-export-html. You might want to avoid this as the next option in my opinion is way better.
Use this EditorState as the Default value for a read only DraftJS Editor Component.
You are going to need convertFromRaw from DraftJS Library and then you make a Nice StateLess React Component like this
import React from 'react';
import {Editor, ConvertFromRaw} from 'draft-js';
const ReadOnlyEditor = (props) => {
const storedState = ConvertFromRaw(JSON.parse(props.storedState));
return (
<div className="readonly-editor">
<Editor editorState={storedState} readOnly={true} />
</div>
);
}
And Now you can simply use this to display your contents. You can also pass your decorators and custom mapping functions, typically everything you pass to the normal Editor and can render the content without loss of style and tedious dealing with HTML.
DO NOT Believe USERS!
The first thing you should care of is "Do NOT believe your USERS".
If your 'HTML' is rendered by your server and can't be modified by user, it's totally OK.
Because your rendered/saved HTML is totally safe and managed by yourself, and if it's assured as "SAFE" HTML, whether you put it(html) into DOM or not is not the problem at all.
But the problem is, most of WYSIWYG editors - like draft.js- makes "HTML" files not TEXT. I think your worry comes from here too.
Yes, It's dangerous. what we can do is NOT rendering HTML directly but "selective" HTML rendering.
Dangerous tags : <script>, <img>, <link>, etc.
you can remove those tags but it can be much safer when you decide which tags will you allow, like this:
Safe tags: <H1> - <H6> / span / div / p / ol ul li / table...
and you SHOULD remove those HTML element's attributes, like, onclick="", etc.
because it could be abused by users too.
Conclusion:
So what can we do when we use WYSIWYG editors?
There are 2 big strategies:
Generate "Safe" HTML when safe to Database.
Generate "Safe" HTML before putting it into DOM.
(3. Generate "Safe" HTML before sending html to client, but it is not in your case!)
Choose first one if you want to sure Database's text are totally safe.
First one must be processed in your server(not browser/client!), and you can use many solutions like BeautifulSoup in python, or sanitize-html in nodejs.
Choose second one if your web-app is complicated, and most of your service's business logic is running on front-end side.
Second one is using HTML escaping package just before mounting HTML into DOM. and still sanitize-html can be good solution. (surelly there's more great solutions!)
You can decide which tags/attribute/values in HTML.
Links
https://github.com/punkave/sanitize-html
in 2022 for me, it works a bit different way
import { convertFromRaw, Editor, EditorState } from 'draft-js';
function Comments() {
const itemText= EditorState.createWithContent(convertFromRaw(JSON.parse(item.content)));
return <Editor editorState={itemText} readOnly={true} />
}
and to the DB it was saved like that
JSON.stringify(convertToRaw(editorState.getCurrentContent()))
the difference is that for me without EditorState it doesn't work, I believe that I am not alone in that
I am using an API get call to fetch the HTML code from database and render the html.
The generated HTML is as below: -
<span (click)="triggerClick()" data-link-id="960" data-link="8" class="level_modal">individuals</span>
triggerClick() function is defined in the ts file but the same is not triggered with dynamically generated html code.
Reason
It's not going to work. The language you use to write Angular templates in is not HTML. It's a custom Angular syntax that purposely looks like HTML to make experience of writing code better. Those templates are then compiled into highly optimized JavaScript functions.
A string you get from your API is received during the run-time of your application, which means that Angular's compiler has already done its job (it has compiled the code which is why you can run your application at all). At this point, Angular's compiler is not available anymore by default. Including the compiler in the final bundle of your app is a performance issue because the compiler code has a large size footprint. This is the reason why AOT compilation (ahead of time) is the default and recommended way of publishing Angular apps for production.
Solution
Your API should not return HTML at all. The whole idea behind single page applications (SPA) is for them to consume and API which is language- and platform-agnostic. It simply works with data, which is today mostly encoded via JSON notation. It's the only language API is supposed to understand.
From what I can gather from the code example you've posted here, you're interested in data of this form:
{ linkId: 960,
link: 8,
text: individuals }
Create an API endpoint which returns data in this form, and then use Angular to loop over the data and create the template which you need.
<span *ngFor="let item of items"
(click)="triggerClick()"
[attr.data-link-id]="item.linkId"
[attr.data-link]="item.link"
class="level_modal">
{{ item.text }}
</span>
Of course, you probably do not need all those data- attributes anyway. This is because Angular application should focus on the model. Model is the source of truth which is used to create the template. If you need additional information about each item (such as link and linkId), just keep in the memory as an object. No need to encode it into HTML just to read it later again.
When you write
(click)="doSomething()"
You want to say
Launch the method doSomething when I click on this
But what you actually say is
Please Angular, compile this code so that when I click on this, it launches doSomething
Because as you may know, you're writing Typescript, not Javascript. So your code will be compiled to be served, thus transforming this into
onclick="doSomething()"
BUT WAIT, THERE'S MORE
When you compile your code, it's actually minified and uglified. So this will give you something along the lines of
onclick="a()"
And thus, making you lose any logic behind this.
So what's the solution ?
The solution is to bind the click event to a global function. There's two ways of doing this :
Function in your component (use it if you want to use logic from your component)
Global function (use it if you want this function to be called in several components)
I'll explain the first use-case, for the second I'll make it short : create a JS file as an asset, put your function in it, and call it on click.
For the second one, it's a bit tricky.
First, inject ngZone into your component : this allows you to handle code outside of Angular
constructor(private zone: NgZone) {}
Then, create a global function (I'll make it Typescript and linting compliant)
ngOnInit() {
this.zone.run(() => {
window['doSomething'] = () => {
console.log('Look mom, I did something !');
};
});
}
After this, delete the function once you leave the component
ngOnDestroy() { delete(window['doSomething']); }
Finally, change your HTML from this
(click)="doSomething()"
To this
onclick="window.doSomething()"
I have problem. Server generate html code and returns to me, with script tags, i needed render this into my React component. I must render the html code (like dangerouslySetInnerHTML), and run script tags. You do not know the solution? Sorry for my english.
You can render the script tags from componentWillMount or componentDidMount, but not dangerouslySetInnerHTML for security purposes. You could easily strip out <script> tags with regular expressions and run that from one of the said methods.
Is it possible to create javascript elements like OpenStreetMap or jQuery inside a vaadin application?
Because vaadin websites are created by programming in java and letting the compiler autocreate the DOM and JavaScript out of it?
So, is it possible at all?
You can create such an integration with AbstractJavaScriptComponent
The basic idea here is to subclass this class, annotate with #JavaScript to pull in the needed JS libs. Then write at least a global function, that sets up your lib in the DOM (you will have a <div> at your disposal). Your component can hold state, the server side can call defined functions on the client (while sending e.g. state) and the client can call server functions (params passed as JSON).
The Wiki has an example how to include such a component
There are some easy and cheap solutions which are not the best in the long term, but they may be enough:
1)
If you just want to render some html you cant insert it as the value of a label and change its Content Mode to html.
https://vaadin.com/book/-/page/components.label.html
2)
If you just want to execute some javascript code after a ui event you can call Javascript.getCurrent().execute(javascriptCode).
https://vaadin.com/book/vaadin7/-/page/advanced.javascript.html
Notice that if you are trying to do some re-usable components, this is not the right answer