Embed a dynamic HTML page in reactjs application - reactjs

I am migrating a JSF app to Node/React js. One of the requirements is to show an html page that is dynamically
requested from a third party in one of the on the page. The third party html is complex - it has its
own javascript and css links to render a canvas. Let me know if this is feasible.
I have
checked in debugger that the requested html arrives successfully.
made sure that it is a valid html by copying it to a file and opening it in different browsers
made sure that all the referred scripts and css are accessible
made sure there are no CORS error
tried wrapping the html in tag
Please see below compressed code. It compiles, rest of the page is rendered but div with third party html
remains blank. If I inspect the div I can see the html but I think none of the javascript code is executed.
class STMDrwaingtDiv extends React.Component
{
constructor(props)
{
//Just create some state variables to track the http request status and
//store the response text
}
getPrevDrawing(caller)
{
//make http request
//caller.setState({cnvHtml:"<object> "+prevDrawing+" </object>"});
//I have tries wrapping the html in <object> tag - does not work
caller.setState({cnvHtml:prevDrawing});
}
componentDidMount()
{
this.getPrevDrawing(this);
}
render()
{
//display progessbar handle errors etc.
else
{
return <div dangerouslySetInnerHTML={{__html:this.state.chartHtml}} />;
}
}
}

https://www.w3schools.com/tags/tag_iframe.asp
Use an <iframe> instead of a <div>. Iframes are meant to be used to embed HTML documents (web pages) inside of other HTML documents! Setting the src attr of the iframe to the 3rd party URL should embed their HTML automatically in your page.

Related

How are Javascript widgets made without iFrames?

I have a chat widget that I want to embed it other people's websites. It looks just like Intercom and all the other chat popups. I want to make the chat popup stick to the bottom-right hand corner of the screen regardless of where you scroll. However, when I import the chat app as an iframe and give it position: fixed; bottom: 0px; right: 15px;, the iframe does not go to where I expect it to go.
I realize that iframes are suboptimal for embedded JS widgets, and all the best embedded apps are importing .js files from file storage. After searching online for hours I have yet to find an explanation/tutorial on how to make those JS files that hook onto a and render the widget. How do you even make one of those pure javascript apps, and what are they called? (Not web components I assume, because there have been widgets for a long time).
Sorry if this question is kinda noob. I never knew this was a thing until I tried implementing it myself. Can anyone point me in the right direction on how to get started making JS web widgets? Thank you! (Maybe a ReactJS to VanillaJS converter would be super cool)
A pure Javascript App is called a SPA - Single Page Application - and they have full control over the document (page). But since you ask about embeding a widget, I don't think that is what this question is about (there are tons of info. on the web on SPAs).
I was going to suggest that going forward you do this using Web Components - there are polyfills available today that make this work on nearly all browsers - but since your question mentioned that you wanted to know how it is done without it, I detail below one of my approaches.
When creating a pure JS widget you need to ensure that you are aware that a) you do NOT have control over the global space and b) that it needs to play nice with the the rest of the page. Also, since you are not using Web Components (and are looking for a pure javascript (no libs)), then you also have to initialize the widget "manually" and then insert it to the page at the desired location - as oposed to a declaritive approach where you have an assigned HTML tag name for your widget that you just add to the document and magic happens :)
Let me break it down this way:
Widget Factory
Here is a simple Javascript Widget factory - the create() returns an HTML element with your widget:
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div")
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
To create a new widget (HTML Element) using the above you would:
const myWidgetInstance = Widget.create("chat-12345");
and to insert this widget into the page at a given location (ex. inside of a DIV element with id "chat_box", you would:
document.getElementById("chat_box").appendChild(myWidgetInstance);
So this is the basics of creating a Widget using the native (web) platform :)
Creating a reusable/embeddable Component
One of the key goals when you deliver a reusable and embeddable component is to ensure you don't rely on the global space. So your delivery approach (more like your build process) would package everything together in a JavaScript IIFD which would also create a private scope for all your code.
The other important aspect of these type of singleton reusable/embeddable components is that your styles for the Element needs to ensure they don't "leak" out and impact the remainder of the page (needs to play nice with others). I am not going into detail on this area here. (FYI: this also the area where Web Component really come in handy)
Here is an example of a Chat component that you could add to a page anywhere you would like it to appear. The component is delivered as a <script> tag with all code inside:
<script>(function() {
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div");
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
const myWidgetInstance = Widget.create("chat-12345");
const id = `chat-${ Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) }`;
document.write(`<div id="${ id }"></div>`);
document.getElementById(id).appendChild(myWidgetInstance);
})();</script>
So you could use this in multiple places just by droping in this script tag in the desired locations:
<body>
<div>
<h1>Chat 1</h1>
<script>/* script tag show above */</script>
</div>
...
<div>
<h1>Chat 2</h1>
<script>/* script tag show above */</script>
</div>
</body>
This is just a sample approach of how it could be done. You would have to add more in order to support passing options to each widget (ex. the chat id), defining styles as well other possible improvements that would make the runtime more efficient.
Another approach
You could add your "script" once and wait for the rest of the page to load, then search the document for a "known" set of elements (ex. any element having a CSS Class of chat-box) and then initialize a widget inside of them (jQuery made this approach popular).
Example:
Note how data attributes can be used in DOM elements to store more data specific to your widget.
<div class="chat-box" data-chatid="123"></div>
<script>(function() {
const Widget = Object.create({
create(chatId) {
const wdg = document.createElement("div");
wdg.classList.add("chat-box");
wdg.innerHTML = `<h1>Chat: ${ chatId }</h1>`;
// Load your chat data into UI
return wdg;
}
});
const initWhenReady = () => {
removeEventListener("DOMContentLoaded", initWhenReady);
Array.prototype.forEach.call(document.querySelectorAll(".chat-box"), ele => {
const myWidgetInstance = Widget.create(ele.dataset.chatid);
ele.appendChild(myWidgetInstance);
});
};
addEventListener('DOMContentLoaded', initWhenReady);
})();</script>
Hope this helps.
The best way to create Javascript widget without third-party library is to create Custom Elements.
The following link : Custom Elements v1 is a good introduction to this technology.
See a minimal example below:
class Chat extends HTMLElement {
connectedCallback () {
this.innerHTML = "<textarea>Hello</textarea>"
}
}
customElements.define( "chat-widget", Chat )
<chat-widget>
</chat-widget>

how i can read meta tag from template before load it on $routeChangeStart in angularjs

i have angular app and server send to me dynamic html when routing to some page and this page have metatag for some use. i have to chenge url if the meta tag is not set before thmplate is load. how i can read meta tag befor load template in $routeChangeStart
So what i can understand via your statement, is you want to get the meta information before loading the next page. Below is snippet, you will need to modify according to your requirement. If you are using Angular UI routing then this should work.
$scope.$on('$routeChangeSuccess', function (event) {
$http.get('url').then(function(response) {
html = response.data; // Check the meta information and then
//either move or stop propagation.
});
});
Hope this helps.

Angular ngInclude - Does it make multiple requests for content?

I am trying to work out how some code is working, it uses angular throughout the front end.
When the page first loads a div with ng-include should be in the dom but its src url is a call to a js function that returns undefined on page load.
<div id="test_div" ng-include="getUrl()"></div>
When I check the dom the entire element is not on the page. After a search button is pressed some other calls happen and the call to getUrl() will return a valid url from that point on, which returns html content from the server.
It is only then that the div appears in the dom and it now has a class added to it of class="ng-scope".
I dont understand how this is happening, does ng-include continue to request a resource until it is avilable?
I couldnt find this information in the documentation.

Stripe Checkout Button Does Not Display When Using Angular Routes

I'm currently trying to implement the standard Stripe Payments checkout dialogue. When I drop in the short <script> include described in the docs (https://stripe.com/docs/checkout) the button that should display is not being rendered.
When I put it in my top-level index.html file the button DOES display. When I put it into a partial that gets displayed when hitting a particular route it does not. I assume this is because it is not executing the Javascript because it's not happening at page load when it's in a route.
Is there anything I can do to get this to work in a route or should I just implement a custom form that points to the stripe.js library? Thanks.
The issue is that the js does not fire, as you suggested. A solution is to simply include the Stripe checkout.js in your index.html file, and then trigger the Stripe popup to open with your controller (or elsewhere).
In your index.html (or equivalent)
<script src="https://checkout.stripe.com/checkout.js"></script>
<!-- Angular script(s) -->
In your controller (or elsewhere)
var handler = StripeCheckout.configure({
key: 'pk_test_6pRNASCoBOKtIshFeQd4XMUh',
image: '/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function(token) {
// Use the token to create the charge with a server-side script.
// You can access the token ID with `token.id`
}
});
handler.open({
name: 'Stripe.com',
description: '2 widgets',
amount: 2000
});
// handler.close();
This is an adaptation per the Stripe docs at: https://stripe.com/docs/checkout#integration-custom

Angular ngView with CKEditor disappearing text

cutting right to the chase here heh
Building an admin backend system with angular for a client. This site and backend are built with Node and Angular. I've got a post editor setup, so that when you click a link to edit a post, locationProvider and ngRoute go to work and swap out the partial with a new controller, and updates the url.
Here at this point, everything shows up perfectly.
It's when I click on Cancel (which is a standard link going back to the post list view) or submit (which works it's magic and then redirects to the same url as cancel) and this works.
It's when I click on the link to edit the same post again. The content that's supposed to be in the CKEditor is blank. However when using Chrome Dev Tools I can see that tw original input field has the content in it correctly, but the iframe has nothing in it at all but the CKEditor Chrome.
As for code,
In my controller it looks like this:
$scope.post = null;
$http.get('/api/post/'+$routeParams.id)
.success(function(data,status,headers){
$scope.post = data;
});
$(function() {
CKEDITOR.replace("content");
});
$scope.submit = function() {
// submission code here
};
Pretty basic here, just want to get the basics out of the way first.
Is the problem with how I'm loading CKEditor?
CKEDITOR.replace("content") would replace the html element with CKEditor instance. Assuming "content" is some div id or text area in your html.
To set the content you need to use
CKEDITOR.instances["content"].setData("Contents to be displayed in text area");

Resources