Render an Element with React - reactjs

Honestly I don't think that this is the best Title, but I've no idea how explain it.
So sorry for it.
I'm trying to write a Component that parse all links(thar are not into a anchor tag) and emoji and render it like links or image.
For emoji I'm using this amazing component: https://github.com/banyan/react-emoji
It works well, but the problem is with the simple links...I don't have found a way for render it like links, instead of text of the link tag.
This is my code:
# #cjsx React.DOM
#Linkify = React.createClass
displayName: 'Linkify'
mixins: [ReactEmoji]
componentDidMount: ->
componentWillUnmount: ->
render: ->
<div>
{#parseLinks(#props.text)}
</div>
parseLinks: (text) ->
pattern = /(ht|f)tps?:\/\/[^\"<]*?(?=\s|$|<\/[^a]>)/gi
results = text.match(pattern)
new_text = text
if results and results.length > 0
for result in results
link_html = React.createElement('a', {href: result}, result)
new_text = new_text.replace(result, link_html)
return #emojify(new_text)
and if I wrote:
Hello search here google.com :)
I get:
Hello search here [object Object] :) (instead of :) I've the correct emoji image)
The problem is: why it don't show correctly the Object Link ? where I done wrong ?
Thanks for any help.

link_html = React.createElement('a', {href: result}, result)
new_text = new_text.replace(result, link_html)
You can't use String#replace to put an object (returned by React.createElement) into a string. It's like saying
var text = "one two three";
var el = {testing: true};
alert(text.replace("two", el));
Instead, you should return a ReactElement (created with JSX or React.createElement) that contains the associated text, but with the link in the correct place in the children.
Consider the output of
<span>Test google.com link</span>
which is
React.createElement("span", null,
"Test ",
React.createElement("a", {href: "google.com"},
"google.com"
),
" link"
)

Related

ReactJs: How to replace html and string template with a component?

I want to manage the content of the page from a content editor where I am getting page content from the API.
Check this screenshot.
I used two different react modules for this react-html-parser and react-string-replace but it is still not working.
Here is my code.
let pageData = '';
pageData = ReactHtmlParser(page.content);
// replacing contact us form with a contact us form component
pageData = reactStringReplace(pageData, '{CONTACT_US_FORM}', (match, i) => (
<ContactUsForm />
));
return <div>{pageData}</div>;
react-html-parser -> It is used to parse HTML tags which are in string format into tree of elements.
react-string-replace -> It is used to replace a string into react a component.
Note: If I use react-html-parser or react-string-replace individually then it works fine but it does not work together.
Any suggestion?
Depends on the expected structure of page.content. If it contains HTML you are right in using react-html-parser, which has a replace option.
import parse from 'html-react-parser';
const macro = '{CONTACT_US_FORM}';
const replaceContactUsForm = (domhandlerNode) => {
if (domhandlerNode.type === 'text' && domhandlerNode.data.includes(macro))
return <ContactUsForm />;
};
// ...
const elements = parse(page.content, { replace: replaceContactUsForm });
return <div>{elements}</div>;
Additionally, If the string {CONTACT_US_FORM} is embedded in text you could use react-string-replace to keep the rest of the text intact:
const replaceContactUsForm = (domhandlerNode) => {
if (domhandlerNode.type === 'text' && domhandlerNode.data.includes(macro))
return <>{reactStringReplace(domhandlerNode.data, macro, () => (<ContactUsForm />))}</>;
};
If page.content does not contain HTML you do not need react-html-parser. But judging from your screenshot some markup is probably contained.

Replace string with react component with .replace

I have a long text (string), I want the user to be able to highlight any word he wants, and make this word a Tag component.
function highlightingString() {
document.onmouseup = () => {
const myHighlightedString = window.getSelection().toString(); // Gives me the word I highglight
const startHighlightedString = docText.indexOf(myHighlightedString) // Give me the start index of my word in the document
const endHighlightedString = startHighlightedString + myHighlightedString.length // Give me the end index of my word in the document
newDocText = docText.replace(myHighlightedString, <Tag name={"Highlighted"}/>)
setOpenedFile(newDocText)
};
}
Tag component
function Tag({name}){
const color = colors[name];
return <div style={{backgroundColor: color, ...tagStyle}}>{name}<span style={{cursor: "pointer", paddingLeft: '10px'}}</span></div>
}
Example:
Initial document text: "Hello, I would like to order a pizza with peperoni"
If the user selects "pizza with", I would like to happen:
"Hello, I would like to order a Tag name={highlighted} /> peperoni" # Where Tag is simply adding a to the selected word
Instead, I get:
"Hello, I would like to order a [object Object] peperoni"
It seems that I am replacing correctly the string I highlight with the <Tag /> component, however I do not get why is not rendering the text within <span> but [object Object]
Well, replacing part of the string with an object will not magically insert that object into that string :) What it does is converting your object to its string representation (which is [object Object]) and then inserting that representation into the string.
What you want to do is to create a proper React component.
Something like this:
<>
{openFile.textBeforeHighlighted}
<Tag name={openFile.highlighted}/>
{openFile.textAfterHighlighted}
</>
while your handler function sets openFile to something like this:
setOpenFile({
textBeforeHighlighted:textBeforeHighlighted,
highlighted:highlighted,
textAfterHighlighted:textAfterHighlighted
});

Send LI value in another input value in React

I'm in a React Project, where after an axios.get, I get many LI inside an empty UL before the axios.get.
When the LI are displayed after the axios.get, I want on click on one LI, to send the LI value to an input value.
I know how to do this with Jquery, (examples here: http://jsfiddle.net/Ender/QdWxH/ & http://jsfiddle.net/kbFUu/1/), but I don't know how to do it in React... Can someone help me ?
In my src/App.js I got the function
getValueFromLi = (evt) => {
$('.App-Result-Li').on('click',function() {
var content= $(this).text();
$('.App-Form-Trips-cityDeparture').val(content);
});
}
and in the src/components/Results/index.js (don't worry, I did well with the props, etc.), I got the callback
<li
onClick={getValueFromLi}
data-value={city.local_name}
className="App-Result-Li"
>
Do you know what is wrong in what I did ?
My project is on Github : https://github.com/Versifiction/api-tictactrip
Files involved :
src/App.js (https://github.com/Versifiction/api-tictactrip/blob/master/src/App.js)
& src/components/Results/index.js (https://github.com/Versifiction/api-tictactrip/blob/master/src/components/Results/index.js)
Thank you so much !
maybe you could try like this:
<li
onClick={() => this.provideLocalName(city.local_name)}
data-value={city.local_name}
className="App-Result-Li"
>
also, you could parse the event object in your getValueFromLi function and do something with it then, e.g.
getValueFromLi = e => {
console.log('li element: ', e.target)
// do something with li, now you have reference to the dom node
}
<li
onClick={this.getValueFromLi}
data-value={city.local_name}
className="App-Result-Li"
>

Convert Quill Delta to HTML

How do I convert Deltas to pure HTML? I'm using Quill as a rich text editor, but I'm not sure how I would display the existing Deltas in a HTML context. Creating multiple Quill instances wouldn't be reasonable, but I couldn't come up with anything better yet.
I did my research, and I didn't find any way to do this.
Not very elegant, but this is how I had to do it.
function quillGetHTML(inputDelta) {
var tempCont = document.createElement("div");
(new Quill(tempCont)).setContents(inputDelta);
return tempCont.getElementsByClassName("ql-editor")[0].innerHTML;
}
Obviously this needs quill.js.
I guess you want the HTML inside it. Its fairly simple.
quill.root.innerHTML
If I've understood you correctly, there's a quill thread of discussion here, with the key information you're after.
I've quoted what should be of most value to you below:
Quill has always used Deltas as a more consistent and easier to use (no parsing)
data structure. There's no reason for Quill to reimplement DOM APIs in
addition to this. quill.root.innerHTML or document.querySelector(".ql-editor").innerHTML works just fine (quill.container.firstChild.innerHTML is a bit more brittle as it depends on child ordering) and the previous getHTML implementation did little more than this.
Simple, solution is here:
https://www.scalablepath.com/blog/using-quill-js-build-wysiwyg-editor-website/
The main code is:
console.log(quill.root.innerHTML);
This is a very common confusion when it comes to Quilljs. The thing is you should NOT retrieve your html just to display it. You should render and display your Quill container just the same way you do when it is an editor. This is one of the major advantages to Quilljs and the ONLY thing you need to do is:
$conf.readOnly = true;
This will remove the toolbar and make the content not editable.
I have accomplished it in the backend using php.
My input is json encoded delta and my output is the html string.
here is the code , if it is of any help to you.This function is still to handle lists though and some other formats but you can always extend those in operate function.
function formatAnswer($answer){
$formattedAnswer = '';
$answer = json_decode($answer,true);
foreach($answer['ops'] as $key=>$element){
if(empty($element['insert']['image'])){
$result = $element['insert'];
if(!empty($element['attributes'])){
foreach($element['attributes'] as $key=>$attribute){
$result = operate($result,$key,$attribute);
}
}
}else{
$image = $element['insert']['image'];
// if you are getting the image as url
if(strpos($image,'http://') !== false || strpos($image,'https://') !== false){
$result = "<img src='".$image."' />";
}else{
//if the image is uploaded
//saving the image somewhere and replacing it with its url
$imageUrl = getImageUrl($image);
$result = "<img src='".$imageUrl."' />";
}
}
$formattedAnswer = $formattedAnswer.$result;
}
return nl2br($formattedAnswer);
}
function operate($text,$ops,$attribute){
$operatedText = null;
switch($ops){
case 'bold':
$operatedText = '<strong>'.$text.'</strong>';
break;
case 'italic':
$operatedText = '<i>'.$text.'</i>';
break;
case 'strike':
$operatedText = '<s>'.$text.'</s>';
break;
case 'underline':
$operatedText = '<u>'.$text.'</u>';
break;
case 'link':
$operatedText = ''.$text.'';
break;
default:
$operatedText = $text;
}
return $operatedText;
}
Here's a full function using quill.root.innerHTML, as the others didn't quite cover the complete usage of it:
function quillGetHTML(inputDelta) {
var tempQuill=new Quill(document.createElement("div"));
tempQuill.setContents(inputDelta);
return tempQuill.root.innerHTML;
}
This is just a slight different variation of km6 's answer.
For Quill version 1.3.6, just use:
quill.root.innerHTML;
Try it online: https://jsfiddle.net/Imabot/86dtuhap/
Detailed explaination on my blog
This link if you have to post the Quill HTML content in a form
quill.root.innerHTML on the quill object works perfectly.
$scope.setTerm = function (form) {
var contents = JSON.stringify(quill.root.innerHTML)
$("#note").val(contents)
$scope.main.submitFrm(form)
}
I put together a node package to convert html or plain text to and from a Quill Delta.
My team used it to update our data model to include both Quill's Delta and HTML. This allows us to render on the client without an instance of Quill.
See node-quill-converter.
It features the following functions:
- convertTextToDelta
- convertHtmlToDelta
- convertDeltaToHtml
Behind the scenes it uses an instance of JSDOM. This may make it best suited for migration scripts as performance has not been tested in a typical app request lifecycle.
Try
console.log ( $('.ql-editor').html() );
Here is how I did it, for you Express folks. It seems to have worked very well in conjunction with express-sanitizer.
app.js
import expressSanitizer from 'express-sanitizer'
app.use(expressSanitizer())
app.post('/route', async (req, res) => {
const title = req.body.article.title
const content = req.sanitize(req.body.article.content)
// Do stuff with content
})
new.ejs
<head>
<link href="https://cdn.quilljs.com/1.3.2/quill.snow.css" rel="stylesheet">
</head>
...
<form action="/route" method="POST">
<input type="text" name="article[title]" placeholder="Enter Title">
<div id="editor"></div>
<input type="submit" onclick="return quillContents()" />
</form>
...
<script src="https://cdn.quilljs.com/1.3.2/quill.js"></script>
<script>
const quill = new Quill('#editor', {
theme: 'snow'
})
const quillContents = () => {
const form = document.forms[0]
const editor = document.createElement('input')
editor.type = 'hidden'
editor.name = 'article[content]'
editor.value = document.querySelector('.ql-editor').innerHTML
form.appendChild(editor)
return form.submit()
}
</script>
express-sanitizer (https://www.npmjs.com/package/express-sanitizer)
document.forms (https://developer.mozilla.org/en-US/docs/Web/API/Document/forms)
My view only has one form, so I used document.forms[0], but if you have multiple or may extend your view in the future to have multiple forms, check out the MDN reference.
What we are doing here is creating a hidden form input that we assign the contents of the Quill Div, and then we bootleg the form submit and pass it through our function to finish it off.
Now, to test it, make a post with <script>alert()</script> in it, and you won't have to worry about injection exploits.
That's all there is to it.
Here is a proper way to do it.
var QuillDeltaToHtmlConverter = require('quill-delta-to-html').QuillDeltaToHtmlConverter;
// TypeScript / ES6:
// import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html';
var deltaOps = [
{insert: "Hello\n"},
{insert: "This is colorful", attributes: {color: '#f00'}}
];
var cfg = {};
var converter = new QuillDeltaToHtmlConverter(deltaOps, cfg);
var html = converter.convert();
Refer https://github.com/nozer/quill-delta-to-html
For a jQuery-style solution that allows getting and setting the Quill value I am doing the following:
Quill.prototype.val = function(newVal) {
if (newVal) {
this.container.querySelector('.ql-editor').innerHTML = newVal;
} else {
return this.container.querySelector('.ql-editor').innerHTML;
}
};
let editor = new Quill( ... );
//set the value
editor.val('<h3>My new editor value</h3>');
//get the value
let theValue = editor.val();
quill-render looks like it's what you want. From the docs:
var render = require('quill-render');
render([
{
"attributes": {
"bold": true
},
"insert": "Hi mom"
}
]);
// => '<b>Hi mom</b>'
If you want to render quill using nodejs, there is a package quite simple based on jsdom, usefull to render backside (only one file & last update 18 days from now) render quill delta to html string on server
Just use this clean library to convert from delta from/to text/html
node-quill-converter
example:
const { convertDeltaToHtml } = require('node-quill-converter');
let html = convertDeltaToHtml(delta);
console.log(html) ; // '<p>hello, <strong>world</strong></p>'

Reactjs How to insert react component into string and then render

How to create a reactjs component that will render the props data with another component.
for example I have a sentence say "Hello guys this is {{name}}. How are you.". Now I want to replace the name with the reactjs component.
when I try to replace the name with the component it shows as [object object].
First Edit:
var sentence = "Hello guys this is {{name}}. How are you.";
var tag_values = {'name': 'any Name'}
TagBox will take sentence and tag_value as props and replace the tags with the Tag component. and render it
var TagBox = React.createClass({
render: function(){
// replacing the tags with Tag component
this.props.sentence = this.props.sentence.replace(tags_values['name'], <Tag \>)
return(
<div>
{this.props.sentence} //Issue: This will Print as "Hello guys this is [Object Object]. How are you."
// But this should print as "This will Print as Hello guys this is any Name. How are you."
// After clicking on "any Name" it should be replaced with input.
</div>
);
}
})
Tag Component will replace the tag with input box on double click. and again replace input box with data on enter.
This can be done using state.
var Tag = React.createClass({})
Okay, so assuming that's a string you have as input, you need to create an array.
var parts = str.split(/\{\{|\}\}/g);
// => ["Hello guys this is ", "name", ". How are you."]
The odd items are literal strings, and the even parts are the stuff between the brackets.
Now we'll create a helper function called mapAlternate. Which takes a function to call for odd elements, and a function to call for even elements in our array.
function mapAlternate(array, fn1, fn2, thisArg) {
var fn = fn1, output = [];
for (var i=0; i<array.length; i++){
output[i] = fn.call(thisArg, array[i], i, array);
// toggle between the two functions
fn = fn === fn1 ? fn2 : fn1;
}
return output;
}
Now we can do something like this in our component:
render: function(){
var parts = str.split(/\{\{|\}\}/g);
// render the values in <strong> tags
var children = mapAlternate(parts,
function(x){ return <span>{x}</span>; },
function(x){ return <strong>{x}</strong> });
return <div>{children}</div>;
}
Which gives us: "Hello guys this is name. How are you."
Have you heard of React String Replace ?
Here is a stateless component example:
import replace from 'react-string-replace';
const reg = /\{([a-z|A-Z|0-9|\.]+)\}/g;
const OutputComponent = props => {
var str = 'Hello {name}, this is a "Super" component: {Super}';
var output = replace(str, reg, prop => props.replacements[prop]);
return <div>{output}</div>;
}
// later
import Super from './Super.jsx';
const obj = {
Super: <Super />,
name: 'John'
}
return <OutputComponent replacements={obj} />;
I just fixed this issue with react-jsx-parser
Your Example would be:
import JsxParser from 'react-jsx-parser'
export default class TagBox extends React.Component {
render() {
const sentence = "Hello guys this is <Tag>name</Tag>. How are you." // simply include the component in your string
return(
<JsxParser components={{ Tag }} jsx={ sentence } /> // identify the component in your string to inject
)
}
}
Nothing from above doesn't worked for me unfortunately. Here is a useful stable solution regexify-string (npm)
npm install --save regexify-string
Works like a charm
regexifyString({
pattern: /\[.*?\]/gim,
decorator: (match, index) => {
return (
<Link
to={SOME_ROUTE}
onClick={onClick}
>
{match}
</Link>
);
},
input: 'Some initial string with [link]',
});

Resources