I'm getting an HTML string from an AJAX request, that looks something like that:
<div> <SpecialComponent/> <SecondSpecialComponent/></div>
What i need, is to be able use this string in a React component, as it were valid JSX.
I tried using the dangerouslySetInnerHTML as instructed in this discussion:
how-to-parse-html-to-react-component
I've also tried React libraries like react-html-parse and html-react-parser. No success
When i was working with AngularJS(1), i was using some small directive that would take care of that situation. I need to achieve the same with React.
Any ideas?
EDIT: if anybody is interested, i found a library that takes care of the issue:
https://www.npmjs.com/package/react-jsx-parser
You're not supposed to be doing things this way. Although React components look like html tags when using JSX, they are actually either classes or functions. What you're trying to do is equivalent to trying to parse:
"<div>document.write('foo')</div>"
You're literally mixing a javascript function name in string form with html markup in the same string. You will have to parse the string to separate the React component from the surrounding html markup, map the React components in string form to their actual React counterparts, and use ReactDOM.render to add the component to the page.
let mapping = [
{"name":"<SpecialComponent/><SecondSpecialComponent/>","component":<SpecialComponent/><SecondSpecialComponent/>},
{"name":"<someOtherComponent/><someOtherComponent/>","component":<someOtherComponent/><someOtherComponent/>}
];
let markup = "<div><SpecialComponent/><SecondSpecialComponent/></div>";
let targetComponent;
mapping.forEach((obj) => {if(markup.indexOf(obj.name)>-1){
targetComponent=obj.component;//acquired a reference to the actual component
markup.replace(obj.name,"");//remove the component markup from the string
}
});
/*place the empty div at the target location within the page
give it an "id" attribute either using element.setAttribute("id","label") or
by just modifying the string to add id="label" before the div is placed in
the DOM ie, <div id='label'></div>*/
//finally add the component using the id assigned to it
ReactDOM.render(targetComponent,document.getElementById("label"));
Never tried this before. I wonder if this will actually work :)
my suggestion is you do something like the following.
first set a state variable to equal the HTML you want to return
this.setState({html: response.data.html})
then in your return you can do the following
return (
{this.state.html}
);
Related
I have response data in form of:
'<b>How can I waive the underage fee?</b><br>\n You … have marked your age correctly before searching.'
So the question is, how can I use that data in React Component?
I have to use it between two divs. Given data is string form of html syntax.
If you want to display this html content in a component, you can use dangerouslySetInnerHTML this way:
<div dangerouslySetInnerHTML={{__html:data}} />
This will inject the HTML in you DOM. As mentioned in the doc, be aware of risks involved with injecting some unknown HMTL.
Source : https://fr.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
I spent two hours on i think should be a simple solution thus the html badness. How would i be able to display my tags?
The html badness
The api call i make
The interfaces i put my data into(everything work except the arrays)
The function that calls the api and gets the data that my html needs
According to your PhotoDetails interface, tags is of type Tag, not Tags. This means that detail.tags will return you a single Tag. If you want that to be an array your interface should be changed like so:
export interface PhotoDetails {
...
tags: Tag[];
...
}
The Tags interface is most likely redundant, and you can get rid of it. When you want an array of Tag, just declare the type as Tag[].
I am writing a page using Vue.js and am attempting some custom components. I've started with a very simple one:
Vue.component('single-field', {
template: '<b>{{ key }}:</b> {{ value }}',
props: ['key', 'value']
});
This works fine on its own: it is intended to take two values and return them in a key: value format. Initially, when I was passing the value as an element from the Vue data object (specifically, pickup.dateTime it wasn't working. It's easier to show than explain:
<div id="app">
<single-field key="Date & Time" value="pickup.dateTime"></single-field>
</div>
I was able to fix this by binding the value (notice the colon by value):
<single-field key="Date & Time" :value="pickup.dateTime"></single-field>
I then wrote a second component which is designed to take an object and a title and return each element of the object as a set of <single-field> components, with a title above them. Here is what I wrote:
Vue.component('field-block', {
template: '<h2>{{title}}</h2>\
<div v-for="(p-key, p-value) in parent">\
<single-field key="p-key" value="p-value"></single-field>\
</div>',
props: ['parent', 'title']
});
Now, I think there must be something wrong with the way I am binding (or not binding?) which is causing the data to be displayed in a strange way. Here is my HTML:
<div id="app">
<single-field key="DateTime" :value="pickup.dateTime"></single-field>
<single-field key="Name" :value="pickup.name"></single-field>
<field-block title="Address" :parent="pickup.address"></field-block>
</div>
The field pickup.address is an object, so I was hoping that the component would iterate through the elements and return them as single fields, but what it's currently doing is just returning the variable names, like p-key: p-value. I tried doing a bind on both of them in the component definition (on both because they are now both variables being passed whereas previously I was using a static string for the key field):
<div v-for="(p-key, p-value) in parent">
<single-field :key="p-key" :value="p-value"></single-field>
</div>
But what this returns is NaN: NaN, and I can't figure out why it would do that. I don't fully understand the bind operation, but I expect that it only binds to data in the data option when defining the view, which is not where p-key and p-value are defined. How would I get the variables to 'carry through' from the outer component to the inner ones? Thanks.
Okay this one was tricky.
Several problems with your code:
Your components should have only one root element. So pack your components into a <div>.
Then, - is not a valid character to put into a javascript variable name. So you can't write (p-key, p-value) in parent, write something else, like (pKey, pValue) in parent.
On the other hand, html properties are not case-sensitive, so you should actually write key-name (for example) instead of keyName, but only for the property names. This limitation does apply only in your html files (not in your string templates).
Finally, here is a working js-fiddle.
https://jsfiddle.net/6juwLd3b/
Additionally, I would advise you that you look at your console while developing. Many error where displayed that could have lead you to a working version.
I was reading Angular 2 documentation where I found:
Passing the $event is not a good practice
Typing the event object reveals a significant objection to passing the
entire DOM event into the method: the component has too much awareness
of the template details. It can't extract information without knowing
more than it should about the HTML implementation. That breaks the
separation of concerns between the template (what the user sees) and
the component (how the application processes user data).
For some reason I can't exactly fit my head around what it says. Seems like there are no explanation available either anywhere.
Can someone explain the exact meaning of this in simpler terms (with an example, if possible)?
Look at the examples the docs already provide for before and after:
#Component({
selector: 'key-up1',
template: `
<input #box (keyup)="onKey($event)">
<p>{{values}}</p>
`
})
export class KeyUpComponent_v1 {
values = '';
onKey(event: KeyboardEvent) {
this.values += (<HTMLInputElement>event.target).value + ' | ';
}
}
Here the component has to know that the event has a target, which is an input element, which has a value. Now v2:
#Component({
selector: 'key-up2',
template: `
<input #box (keyup)="onKey(box.value)">
<p>{{values}}</p>
`
})
export class KeyUpComponent_v2 {
values = '';
onKey(value: string) {
this.values += value + ' | ';
}
}
Here the template is responsible for extracting the correct value from its own structure, and all the component knows is that it's getting a string. Now imagine:
You need to change the input element to a select, to restrict the range of inputs. What has to change in each version? Just the template, or does the component have to change too?
You want to test the handling method. How easy is this for each version? Can you just pass in a string, or do you have to build an event with an element of the appropriate structure?
This is what separation of concerns really means - how far does a change propagate? How much does each piece of your system need to know about the other pieces to continue working? Per Wikipedia, for example:
Of special value is the ability to later improve or modify one section
of code without having to know the details of other sections, and
without having to make corresponding changes to those sections.
However, as snorkpete suggests, note that the reservation around passing $event to component methods only applies when $event refers to DOM events. In the case where you're using an EventEmitter to raise your own events, $event will (most likely) be an object from the business domain, and is perfectly fine to use as is.
Following up on this queston/answer (second answer) Dynamically loading templates in Meteor.js
I have set up a helper in the current displayed template and i am returning the template dynamically. This works great.
Template.template1.helpers({
dynamicView: function(){
return Template['dynamic_template']();
}
})
This ends up showing my html in template 1:
Questions:
How do i pass data to this Template as it's being created, can i do something like this:
Template['dynamic_template'](data1, jsonData2);
I want the dynamicView helper in template1 to also be dynamic in the sense that it can have a different template based on some other criteria. Can i use a Session here and change the dynamicView return statement to something like this:
dynamicView: function(){
return Session.get('dynamicTemplate');
}
and somewhere else Session.set('dynamicTemplate', Template['dynamic_template']()); This works, but is it recommended. It's just a string but i am concerned about performance problems and the size of my template being passed reactively
OK I guess ill have to split my answer up:
With 1.
The templates are compiled handlebars templates so you just need to provide a context to them so the handlebars data can be filled up:
data = {name1:value1, name2:value2}
return Template['dynamic_template'](data);
So that {{name1}} and {{name2}} get filled up with value1 and value2 respectively.
With 2.
Yes that should work, you can pass off any data that will give off HTML as a result. For a very detailed videocast have a look at the EventedMind screencast on template functions: http://www.eventedmind.com/posts/meteor-rendering-template-functions
The template system's use case might not be this exactly. You might want to use Meteor.render instead, but it depends on what you want to do.
Despite the Session name, its just a reactive javascript variable so it should be fine with larger strings too to the same that would be used in an ordinary javascript variable