Programmatically generated JSX string - reactjs

I have a text file that I'd like to convert to a JSX string using Regex and display on the browser as if it's a JSX markup manually entered in the render return () block.
Let's say I create a string
generatedString = '<span class="tag" style={divStyle}>Test string</span>'
and want to have it displayed in the browser by
render() {
<div> {generatedString} <div>
}
The browser shows
"<span_class="tag"_style={divStyle}>Test string</span>"
as a text string, instead of "Test string" with the style applied to it.

You can use the dangerouslySetInnerHTML property on the div tag to render HTML as you like. This would still however be just displayed as HTML and not as a jsx tag, like this:
render() {
return <div dangerouslySetInnerHTML={generatedString} />;
}
If you anyhow generate the JSX string yourself, then why not choose create a class that renders those properties, like in the following fiddle
var GeneratedComponent = React.createClass({
propTypes: {
content: React.PropTypes.object
},
render() {
var content = this.props.content;
if (!content || !content.tag) {
return null;
}
return React.createElement(content.tag, content);
}
});

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.

Add custom CSS class via variable to returned jsx

I've created a custom component which essentially returns html markup in order to display content based on the values passed to the component. Here's a simplified version for brevity:
interface ContainerProps {
position?: string;
content?: string;
class?: string;
}
const CardContainer: React.FC<ContainerProps> = ({ position = "right", content = "n/a", class = "" }) => {
if ( position.trim().toLowerCase() === "right" ) {
return <><div className="ion-float-right" >{content}</div><div className="clear-right"></div></>
} else if ( position.trim().toLowerCase() === "left" ) {
return <div className="ion-float-left">{content}</div>
} else {
return null
}
};
export default CardContainer;
This works great, but I now need to be able to pass a css class name to the component. However, I can't work out how to add the "class" prop to the returned html/jsx.
I tried various code such as below. However, in all cases the code was output as actual html rather than the value of the prop:
return <div className="ion-float-left" + {class}>{content}</div>
return <div className="ion-float-left {class}" >{content}</div>
return <div className="ion-float-left class">{content}</div>
I also tried a few other random things in desperation and these typically cause a compilation error. What is the best way to achieve the intended result eg:
return <div className="ion-float-left myClassNameHere">{content}</div>
its like inserting a string inside another or adding them together. You can use classname={"yourclasse" + theDynamicClass} or className={yourClass ${dynamicClass}} (inbetween ``)
return <div className=` ion-float-left ${class}``>{content}

Vue input tag element - how to fill it with tags properly?

I am using vue element <input-tag>, and I need to make edit page for one entity which contains tags. So the <input-tag> field should be populated based on the current values ​​in the entity.
I managed to do that, but the whole JSON object appears in <input-tag> field (example: {"id":2, "tagName": "vuejsproblem", "newsId": 1}), not only tagName as it should be.
Also, in console I got error, I don't know why, because this.tags is obviously not "":
[Vue warn]: Invalid prop: type check failed for prop "value". Expected Array, got String with value "".
found in
---> <InputTag>
This is code of my vue page:
<template>
<div class="pt-5">
<form #submit.prevent="editNews">
<div id="app">
<label for="tags">Tags</label>
<input-tag placeholder="Add Tag" v-model.trim="tags"></input-tag>
<br>
</div>
<button type="submit" class="btn btn-primary mt-2">Submit</button>
</form>
</div>
</template>
<script>
export default {
name: "EditNews",
data() {
return {
tags: '',
}
},
beforeMount () {
this.$axios.get(`/api/news/${this.$route.params.id}`,{
id: this.$route.params.id,
}).then((response) => {
this.tags = response.data.tags;
});
/* this.tags.forEach(element => element.tagName);
*/
},
methods: {
editNews() {
this.message = '';
if(this.tags.length > 1) {
console.log(this.tags)
this.$axios.post(`/api/news/${this.$route.params.id}`, {
tags: this.tags,
}).then(
data => {
this.message = data.message;
},
error => {
this.message = error.response.data
alert(this.message);
});
}
},
},
}
</script>
<style scoped>
</style>
As it is said here, tags must be Array. So,
define tags as Array in data() like:
data() {
return {
tags: [],
}
}
Also response.data.tags must be Array something like [ "Jerry", "Kramer", "Elaine", "George" ] as here.
You can convert response.data.tags to Array which contains only tagName by doing this: this.tags = response.data.tags.map(x => x.tagName)
Based on what I could understand from your question (if I got it right), you can do that using the render helper from vuejs, depending on which version you're trying to achieve this v2 or v3
Back in v2 you could do something like:
https://github.com/vubular/elements/blob/master/src/text/Plain.vue
using the this._v you can bind directly the content from the slot of the component or you could wrap with any html tag before passing the content in for example this._v('<span>'+this.content+'</span>'), you can also define tag as prop and then render prop instead of hardcoded tags.
Meanwhile in v3 you can return the h helper imported from vue:
render() { return h('span', {}, this.transformedContent); },. Again you can accept the span/tag as prop in child component context.
When doing so you don't need to define a <template> for your component so that you can render the component using vue helpers (where it then handles the DOM interactions).

Span tag and content not rendering inside quill editor

I have recently started exploring quill to implement a rich text editor. I want to render following html content in quill editor:
Second span
However when rendered, span tag is removed and its content is wrapped inside P tag and rendered. I learnt that as part of optimization quill removed span tags. But I really want span tag to render so I added a SPAN blot extending BlockEmbed. However after adding SPAN blot, nothing rendered in the editor. I don't understand what I am doing wrong; Here is the SPAN Blot:
let quill = require("quill");
let BlockEmbed = quill.import("blots/block/embed");
class SpanBlot extends BlockEmbed {
public static create(value: any): any {
let node = super.create(value);
node.setAttribute("data-embed-id", value);
return node;
}
public static value(node: any): any {
return node;
}
public static formats(node: any): any {
let format: any = {};
if (node.hasAttribute("data-embed-id")) {
format.height = node.getAttribute("data-embed-id");
}
return format;
}
public formats(): any {
let formats = super.formats();
formats["span"] = SpanBlot.formats(this.domNode);
return formats;
}
}
SpanBlot.blotName = "Span";
SpanBlot.tagName = "SPAN";
SpanBlot.class = "social";
export { BlockEmbed, SpanBlot };

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