Writing HTML to an iFrame in React, getting [object Object] - javascript

I'm trying to create a basic WYSIWYG editor where I can click on elements in a sidebar and have those element written to an iFrame page.
My current setup has me creating an array of JSX elements that I then write to a react-iframe via a contentDocument write call.
The problem is that right now when I attempt to add these components, I get an [object Object] printed in the iFrame. Attempting to JSON.stringify() said object only gives me a literal printout of the object and not a render of the JSX element itself.
Please give me constructive criticism and any ideas on better ways to go about this- this has been my naïve attempt based on what I currently know.
Code:
SetIFrameInnerHTML(){
//get snapshot from React's state
const snapshot = this.state.PagesSnapshot;
//if the snapshot exists
if(snapshot){
const currPage = document.querySelector('#page_selector').value;
//Variable I'll be storing array to print to page will be
var pageTags = [];
//Fetching data from my dB and setting variables...
snapshot.forEach(function (childSnapshot){
let testValue = childSnapshot.val();
if(currPage == Object.keys(testValue.pages)[0]){
const currPage = document.querySelector('#page_selector').value;
console.log('break');
var tagType_pt1 = testValue.pages
var tagType_pt2 = tagType_pt1[currPage];
var tagType = tagType_pt2.tags[0].tag_type;
var tagStyle_pt1 = testValue.pages
var tagStyle_pt2 = tagStyle_pt1[currPage];
var tagStyle = tagStyle_pt2.tags[0].style;
var tagContent_pt1 = testValue.pages;
var tagContent_pt2 = tagContent_pt1[currPage];
var tagContent = tagContent_pt2.tags[0].content;
if(tagType == 'p'){
pageTags.push(<p style = {tagStyle}>{tagContent}</p>);
}else if(TagType == 'img'){
pageTags.push(<img src = {imageSrc}></img>);
}
}
});
let editorFrame = document.getElementById('iFrameId');
// also tried: editorFrame.postMessage(pageTags, 'http://localhost:8080/', false);
editorFrame.contentDocument.write(pageTags);
//also tried: editorFrame.contentDocument.write(JSON.stringify(pageTags));
}
}
//Rendering the iframe
render(){
return(
<div>
... some JSX tags
<Iframe
id = "VisualEditorWindow"
url = {this.props.CurrentEditPageHandle}
ref = {this.VisualLogic}
width = "calc(100vw - 500px)"
height = "90vh"
className = "iframe"
display="initial" />
</div>
);
}

charlieftl's idea worked:
One suggestion would be pass all that to a component you render off screen and use a ref or ReactDOM.findDOMNode to access the outer element and then get it's outerHTML to write to iframe

Related

Cannot get child element from parsed XML javascript (google script)

I am trying to get child node, but it prints null.
My code:
function findFile(ICO){
var fileName = ICO + '.xml';
const folderFiles = DriveApp.getFolderById('folderID').getFiles();
while (folderFiles.hasNext()) {
var folderFile = folderFiles.next();
if(folderFile.getName() == fileName){
return folderFile.getId();
break;
}
}
}
function filesearch(ICO){
var fileId = findFile(ICO);
var fileFound = DriveApp.getFileById(fileId).getBlob().getDataAsString();
var rawXml = XmlService.parse(fileFound);
return rawXml;
}
function parseXML(){
var text_ICO_txt = '27074358';
var docXml = filesearch(text_ICO_txt);
var root = docXml.getRootElement();
Logger.log(root);
var child1 = root.getChild('Ares_odpovedi');
Logger.log(child1);
}
It prints:
[Element: <are:Ares_odpovedi [Namespace: http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0]/>]
null
So I tried different variation of Ares_odpovedi (including the whole text after Element:), but child node is null.
XML file:
<are:Ares_odpovedi xmlns:are="http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0" odpoved_datum_cas="2022-05-09T15:00:10" odpoved_pocet="1" odpoved_typ="Vypis_VREO" vystup_format="XML" xslt="klient" validation_XSLT="http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_odpovedi.xsl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0 http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0/ares_answer_vreo.xsd" Id="ares">
<are:Odpoved>
<are:Pomocne_ID>0</are:Pomocne_ID>
<are:Vysledek_hledani>
<are:Kod>1</are:Kod>
</are:Vysledek_hledani>
<are:Pocet_zaznamu>1</are:Pocet_zaznamu>
<are:Vypis_VREO>
<are:Uvod>
<are:Nadpis>Výpis z veřejného rejstříku v ARES - elektronický opis</are:Nadpis>
<are:Aktualizace_DB>2022-05-09</are:Aktualizace_DB>
<are:Datum_vypisu>2022-05-09</are:Datum_vypisu>
<are:Cas_vypisu>15:00:09</are:Cas_vypisu>
<are:Typ_vypisu>aktualni</are:Typ_vypisu>
</are:Uvod>
<are:Zakladni_udaje>
...
Could you, please, hint me, how to deal with it?
Thank you in advance!
This answer supposes the modification of your showing script.
When I saw your XML data, it seems that the tag of Ares_odpovedi is the root element. So, for example, when you want to retrieve the element of Odpoved, please modify it as follows.
From:
var root = docXml.getRootElement();
Logger.log(root);
var child1 = root.getChild('Ares_odpovedi');
Logger.log(child1);
To:
var root = docXml.getRootElement();
Logger.log(root);
var child1 = root.getChild('Odpoved', root.getNamespace());
Logger.log(child1);
Reference:
getChild(name, namespace)

How to display HTML class of current node in JS

I have variables which:
display the result (result), and
reference the current node (thisNode).
What do I need to change in my code so that it would display the HTML class?
var thisNode = document.body.firstChild;
var result = document.getElementById("resultOfButton");
result.InnerHTML = thisNode.;
/* Here, in JS are there any ways like displaying the class name,
like nodeClass */
Please give recommendations for my code. There may be some errors. Thank you.
var thisNode = document.body.firstChild;
var result = document.getElementById("resultOfButton");
var block = false;
function buttonDown()
{
if(block == true)
{
thisNode = thisNode.parentElement.firstChild;
block = false;
}
thisNode = thisNode.nextSibling;
result.innerHTML = thisNode.nodeName;
if(thisNode == thisNode.parentNode.lastChild)
{
block = true
}
}
function buttonUp()
{
// not done now...
}
function buttonEnter()
{
thisNode = thisNode.firstChild;
result.innerHTML = thisNode.c;
}
function buttonBack()
{
// not done now...
}
I think you're asking for the className attribute. I copied your first sample and added some code so you can run it on this page. You'll get the second emoji replaced by the class name of the inserted element.
var thisNode = document.getElementById("thisNode"); // document.body.firstChild;
var result = document.getElementById("resultOfButton");
result.innerHTML = thisNode.className; /*Here, in JS are there any ways like displaying the class name, like nodeClass*/
<div id="thisNode" class="sample-class">🙂</div>
<div id="resultOfButton">🙃</div>
Quoting MDN:
"The className property of the Element interface gets and sets the value of the class attribute of the specified element."

Can I create a React Element from an HTML string or dom node/element

I'm retrieving an HTML string from SharePoint and need to parse and modify the data and build a react element to be displayed in my react application.
Basically, I have code (as string) returning to me in a format similar to:
"
<div>
<div data-sp-canvasdataversion="1">This could be a header</div>
<div data-sp-canvasdataversion="1"><img src="titleimage.jpg"></div>
<div data-sp-canvasdataversion="1">This is a link to a PDF</div>
</div>
"
and from that I need to cycle through the children and build a new React element containing some parts of what was returned and some new parts such as
<div>
<div data-sp-canvasdataversion="1">This could be a header</div>
<div data-sp-canvasdataversion="1"><img src="titleimage.jpg"></div>
<PDFViewer file={""pdfLink.pdf""}></PDFViewer>
</div>
I was originally using dangerouslySetInnerHTML which worked to simply display the data, but now i need to remove a chunk of the html, create a react element based on the data and inject the new element back into the code. Since i'm trying to insert a component now, vanilla html won't work.
I'm able to cycle through the children by converting it to a dom node but I can't figure out how to use a dom node or element as a React Element Child.
I've tried:
let element = React.createElement('div', {}, HTMLString);
let node = document.createRange().createContextualFragment(HTMLString).firstChild;
let element = React.createElement('div', {}, node);
let node = document.createRange().createContextualFragment(HTMLString).firstChild;
let node2 = document.createElement("div");
node2.appendChild(node);
node2 = node2.firstElementChild as HTMLDivElement;
let element = React.createElement('div', {}, node2);
None work as needed and give me the error Objects are not valid as a React child (found: [object HTMLDivElement]). or similar.
What i need is something like:
let element = React.createElement('div');
let node = document.createRange().createContextualFragment(HTMLString).firstChild;
node.childNodes.forEach(child => {
if(...//child IS NOT pdf)
element.appendChild(child)
else if(...//child IS pdf){
...
element.appendChild(<PDFViewer file="linktopdf.pdf">)
}
})
Then I would expect to be able to use that in render
render {
...
return(
<div className="container">
{element}
</div>
);
}
Please let me know if this is even possible and how. The only possible solution I could think of was maybe saving the child elements as strings and using dangerouslySetInnerHTML to generate all of them but I really want to get away from using dangerouslySetInnerHTML, especially like that.
For below React v17.0: try this react-html-parser
For React v17.0 and above: try this fork (thanks to #thesdev pointing that out)
I ended up doing what I didn't want to which is using dangerouslySetInnerHTML. If anyone comes up with a better solution, please feel free to share.
async getArticleComponents(data: any) {
if (!data)
return null;
let slides = []
let node = document.createRange().createContextualFragment(data).firstChild;
let children = node.childNodes;
for (let i = 0; i < children.length; i++) {
var element = (children[i] as HTMLDivElement);
var controlElement = element.attributes.getNamedItem("data-sp-controldata");
var jObj = controlElement ? JSON.parse(controlElement.value) : null;
if (jObj.controlType === 3) {
var childEle = element.firstElementChild;
var webPartData = childEle.attributes.getNamedItem("data-sp-webpartdata");
var wpJObj = webPartData ? JSON.parse(webPartData.value) : null;
if (wpJObj.title == "File viewer") {
let ext = (/.*\.(.*)/i).exec(wpJObj.properties.file)[1].toLowerCase();
if (ext === "pdf") {
var pdf = await fetch(`/api/SharePoint/GetFile?url=${encodeURIComponent(`${wpJObj.serverProcessedContent.links.serverRelativeUrl}`)}`)
.then(res => res.text())
.then(pdf => pdf ? `data:application/pdf;base64,${pdf}` : null);
slides.push(<PDFViewer key={jObj.id} file={{ pdf, fileName: "test" } as IPdfMeta} />);
}
} else
slides.push(<div key={jObj.id} dangerouslySetInnerHTML={{ __html: element.outerHTML }}></div>);
}
else if (jObj.controlType !== 0) {
slides.push(<div key={jObj.id} dangerouslySetInnerHTML={{ __html: element.outerHTML }}></div>);
}
}
return slides;
}

Can't fetch image URL and use it as CSS background image

I have a Unordered List wist List Items that are created using the Fetch method. The data is JSON data and needs to be relayed to an HTML document
I want the created list items to have a CSS-background based on the JSON data URL. It looks something like this;
fetch(url).then(function(response){
return response.json();
JSON.stringify(data);
}).then(function(data){
for(var i=0;i<data.length;i++){
var createListItem = document.createElement("li");
createListItem.className = "listItem";
var listItem = document.querySelector(".listItem");
var createHeaderItem = document.createElement("h3");
createHeaderItem.innerHTML = data[i].title;
ul.appendChild(createListItem);
createListItem.appendChild(createHeaderItem);
if (data[i] && data[i].media[0] && data[i].media[0].url) {
listItem.style.backgroundImage = "url('"+""data[i].media[0].url""+"')";
} else {
listItem.style.backgroundImage = "none";
}
}
});
I can't get the list item style background to relay the URL. What is the correct way of grabbing the data.media.url and using it as CSS list-item property?
It looks like you have excessive quotes
Try removing the quote after the first + and before the second +
listItem.style.backgroundImage = "url('"+""data[i].media[0].url""+"')";
To
listItem.style.backgroundImage = "url('"+ data[i].media[0].url +"')";
Edited:
for(var i=0;i<data.length;i++){
var createListItem = document.createElement("li");
createListItem.className = "listItem";
var createHeaderItem = document.createElement("h3");
createHeaderItem.innerHTML = data[i].title;
ul.appendChild(createListItem);
createListItem.appendChild(createHeaderItem);
var listItem = document.querySelector(".listItem");
if (data[i] && data[i].media[0] && data[i].media[0].url) {
listItem.style.backgroundImage = "url('"+ data[i].media[0].url +"')";
} else {
listItem.style.backgroundImage = "none";
}
What has changed?
You never declared
var ul = document.createElement("ul");
I'm going to assume this is global variable and declared outside this scope otherwise you have additional problems that needs addressing.
I've moved the following code (see below) further down the code. You attempted to access a DOM element using document.querySelector() on an element that has not yet been added to the DOM. As a result of this you always got null returned.
var listItem = document.querySelector(".listItem");
This excessive quote I raised earlier still stands.
As I have mentioned in my comments listItem is null. This is due to the fact that createListItem has yet to be added to the DOM and .querySelector(..) checks the current DOM tree when the function is called.
There is no need for listItem to exist since you're manipulating and appending to createListItem. So rather than trying to assign .style.backgroundImage = ".." to listItem add it to createListItem:
if (data[i] && data[i].media[0] && data[i].media[0].url) {
createListItem.style.backgroundImage = "url('"+data[i].media[0].url+"')";
} else {
createListItem.style.backgroundImage = "none";
}
Additionally if .listItem, the CSS class, does not have a default background-image of some sort you can omit the above else clause as it is unnecessary:
if (data[i] && data[i].media[0] && data[i].media[0].url) {
createListItem.style.backgroundImage = "url('"+data[i].media[0].url+"')";
}

How do I generate HTML output from a meteor template for a given contextual data object (version 0.8+)

I am trying to integrate meteor with another JS framework. I had things working fairly well prior to the latest Blaze upgrade (i.e. pre-version 0.8). In order to get this to work, I need to render a meteor template as an HTML string. Moreover, I need to be able to supply a data object to provide the values for the variables included in the template.
Pre 0.8, I could simply do the following:
var myTemplateName = 'someTemplateName';
var myTemplateVariableData = {myTemplateVariableData: {'xxx': 'some data}};
var myTemplateHTML = Template[myTemplateName].render(myTemplateVariableData);
Of course, that no longer works. However, I have to think there is some way to do this still even post-0.8. I have gotten fairly close with the following:
var myTemplateVariableData = {'xxx': 'some data};
var templateName = 'someTemplateName';
var myTemplateHTML = "";
var dr = new UI.DomRange; // domain range
var loadedTemplate = Template[templateName].extend(
{
data: function () {
return { myTemplateVariableData: myTemplateVariableData }
}
}
);
var renderedTemplate = loadedTemplate.render();
UI.materialize(renderedTemplate, dr, null, dr.component);
for (var member in dr.members) {
if (dr.members.hasOwnProperty(member)) {
var testHTML = dr.members[member].innerHTML;
if (testHTML) {
myTemplateHTML = myTemplateHTML + testHTML
} else {
myTemplateHTML = myTemplateHTML + dr.members[member].textContent
}
}
}
The problem with the result of this attempt is that if I try something like this:
{{#if myTemplateVariableData.xxx}}<span> {{myTemplateVariableData.xxx}}</span>{{/if}}
I will get the span showing up but no content other than the &nbsp. It seems as though when inside of an if block, it loses the context and can't see the myTemplateVariableData attribute on the 'this' object any longer.
I suspect there is an easier way to accomplish what I am trying to do here but I am out of ideas at present so I thought I'd post this here to see if anyone else had tried to do something similar.
My solution I think is a little more straight forward:
function html(contextObject, templateName) {
return Blaze.toHTML(Blaze.With(contextObject, function() { return Template[templateName]; }));
}
use:
template file
<template name="myTemplate">
<div id="{{myid}}"></div>
</template>
in some JS file
var myhtml = html({myid : 11}, "myTemplate") //<div id="1"></div>
To answer your question in the title, you could do it like this:
var templateName = 'myTemplate'
var context = {randomNumber: 527}
var div = document.createElement('div')
var component = UI.renderWithData(Template[templateName], context)
UI.insert(component, div)
var htmlString = div.innerHTML // <-- What you want.
But that's maybe not what you want?

Categories

Resources