We are building an enterprise application using react, redux and react-redux stack. I got stuck in one of the designs and need your help.
We have got some html content, coming from the backend(PZN) and we don't wanna use dangerouslySetInnerHTML(as it's not the trusted source) , instead we would like to use iframe, where src attribute will point to locally stored one of the templates with some placeholders [values will be replaced after the backend call]. How do I bind values to the templates. do anybody know any client side templating engines like handlebars, EJS?
Ex:
const Frame = ({src}) => {
return(
<iframe src = {src} ref = {(frame) => this.frame = frame} />
)
The above thing holds good If I just wanna dump the html content into the iframe, but I want to bind the some data values into template before being rendered on the UI?
Related
I'm working on a Laravel app that I'm transitioning to React using a microfrontend(-like?) approach. I defined a helper function in Laravel that receives a component name and array of props and outputs the server-side rendered React HTML. I then call this function inside my views where needed.
Each page in my application defines a few variables that might affect the rendering both of these React components and also the Blade templates. So I define the variables in the view and send them to JavaScript land through a global window variable. But I also need these variables in the SSR helper.
Right now I have two ideas on how to do this:
Pass the variables as props in each call to my helper function. I want to avoid this as the variables will never change throughout the request lifecycle and all the #includes and #extends
Set the values using config helper before rendering the view. This seems kind of unidiomatic, as I think config should be used with more "static" values (that apply for the whole application and not particular pages), but I'm not very well versed in Laravel-world so this might actually be acceptable.
So right now I'm more inclined to 2, but I was wondering if there was a better option?
Some code:
my-template.blade.php
//these are the variables that I want to access in my helper
#extends('master.index', [
"_PAGE_CONFIG" => [
"page" => "blog",
"header" => ["search" => true]
]
]);
#section('content')
#include('some-template.that-also-has.ssr-components-in-it')
{!! ssr('blog/Blog', ["posts" => $posts]) !!}
#endsection
master/index.blade.php
<body>
#if($_PAGE_CONFIG["header"])
<header>{!! ssr('header/Header') !!}</header>
#endif
#yield('content')
<script>
//here I pass my variables to (client) JS land
window._PAGE_CONFIG = #json($_PAGE_CONFIG);
</script>
</body>
my-SSR-helper.php
function ssr($component, $props = []) {
/*
here I call a node server or script that handles the rendering for me,
but I want to pass it $_PAGE_CONFIG, which will be different in each page.
I could pass it in each ssr call in the template but this is what I want to avoid
as this function might be called several times down the #include/#extend chain
and $_PAGE_CONFIG never changes in any one page
(but might be different for different pages).
*/
}
If each page always gets the same values, then you should set an array somewhere into your config where the keys are the pages and the values are the props received. By the way, you would get:
function ssr($component, $page, $props = []) {
$pageProps = config('somewhere.'.$page);
}
I'm glad to see people using micro-frontends to use React to server-side rendering in a non-javascript framework like Laravel.
I recommend you take a look at this article Universal Rendering in Laravel using Vue.js and Ara Framework. Ara Framework supports also React, I think the server-side include approach is the best option to overcome the limitations of Blade.
This article can give you context about how this approach works.
https://itnext.io/strangling-a-monolith-to-micro-frontends-decoupling-presentation-layer-18a33ddf591b
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>
I am developing an application, where I've to load other angular(1.x) application inside current page (As per project requirement). I am trying to achieve it using shadow DOM concept (This could be done using iframe, but I am looking for better approach). I've tried below code:
var templateUrl = "angular-app.html",
templateReceivedCallback = function (response) {
var templateHolder = $("#template-holder"),
div = document.getElementById('template-holder'),
shadowRoot = div.attachShadow({
mode: 'open'
});
shadowRoot.innerHTML = response;
};
$.get(templateUrl, templateReceivedCallback);
I am expecting that the angular application should be loaded inside template holder element:
<div id="template-holder"></div>
The other app is loading in the div which I mentioned, but expressions are not compiled as expected. Example - Angular app having variable this.greeting = "Welcome!" and I am expecting this:
Welcome!
But it rendered as:
{{greeting}}
Is there any better way to achieve this? If yes, please share running example with me.
Thanks.
I think it's not possible because Angular parsing engine won't look inside the Shadow DOM.
As a workaround, you could put the {{greeting}} in the normal DOM.
If you want to use Shadow DOM, you will always be able to access it with <slot> tags.
I am trying to find the equivalent of .Net usercontrol in the html5 SPA world. Does react component fall under this or angular directives. I want to create self contained sections of page made of multiple rendering components put together (grid,chart etc) that talks to a socket. And I want to reuse those by pointing to a different data source.
An example would be a chart and a grid (with underlying data) together with a button to flip between chart and grid. How will I make this as one component that can be used multiple times in same application and also shared to different applications.
Yes, You can use Reactjs or Angularjs, I would recommend Reactjs in case if the sections' state are changing many times, in reactjs every component have a state, Reactjs knows when to re-render the component because it is able to observe when this data changes. Dirty checking is slow regarding the reactjs.
In other word Reactjs can give you much better performance, but note that Reactjs is only for the UI, no routing or other like in Angularjs.
I hope that helps, good luck
I've done this exact thing with a simple combination of JS objects, jQuery and Handlebars. No need for Angular et al if you don't want them (and nothing wrong with them if you do). I've defined my components as JS Objects:
/**
* A data grid.
*/
function Grid(columns, $container) {
this.columns = $.extend([], columns);
...
this.render($container);
}
/**
* Render the grid into a container.
* #private
*/
Grid.prototype.render = function($container) {
Rendering the view is done with Handlebars:
<div class="grid">
<header>
<div class="label">
{{#each columns}}<div data-id="{{id}}" data-index="{{#index}}" style="width:{{boxWidth}}px">{{name}}</div><nav></nav>{{/each}}
</div>
The Model is obtained by ajax calls:
/**
* Handle a grid filter change.
* #private
*/
MasterCompanyList.prototype.onFilter = function(e, filter) {
App.loading.start();
App.ajax({
url: 'ws/admin/mcl',
data: filter.filter,
context: this,
success: function(json) {
this.grid.setData(json);
App.loading.done();
}
});
};
All at a simpler level than Angular etc (I've used Angular commercially for a few years), but I find this KISS approach gets me where I need to be without having to rely on a heavier framework - and if I want to manipulate something jQuery styles then off I go.
Angular and React can do this, but React is based on a component design, so it would be perfect for what you're trying to achieve.
Within my Concrete5 there's a package that contains many single pages, which actually make the core functionality (community connections).
There's one particular page that contains search functionality. Is there any way to take the central part of that page and somehow display it on the homepage, in a div element or similar?
You can do this with jquery .load()
On the single page, wrap the content you want to import in a div with a unique id.
On the home page, add a div to import the content into
// Get the URL of the page
var url = "relative/path/to/page;
// Load the new page into the temp container
// Replace #wrapper with the selector of the element you want to import
$("#import-content").load(url + " #wrapper");
You should be able to do this by creating a new public function in the single_page's controller that returns whatever you want it to (data that you can then render in HTML, or you could have the controller method itself render an "element" with some data, or just create an HTML string in the controller method itself if you want to totally violate the MVC pattern).
Then you should be able to retrieve that data or markup from your other page like so:
$myController = Loader::controller('/path/to/singlepage'); //<--NOTE this is a C5 URL path (e.g. "slug"), not a server file path!
$myMarkup = $myController->myCustomFunctionIWroteToReturnSomeStuff();
echo $myMarkup;