Visual builder to work with manually hard-coded templates/components - javascript

I wonder, is it possible to create a visual no-code builder to work with JS components (e.g. React JSX) if them are manually hard-coded before?
Let me explain what I mean.
Suppose a simple React component pages/index.js previously written by a developer manually:
function HomePage() {
return <div>Welcome to Next.js!</div>
}
export default HomePage
How can we change such a component in the front-end using a visual builder?
For example, we want to add a new HTML element (e.g. H1) inside the existing div.
As I can understand, the builder first needs to know in which JS file the HTML markup is stored to update it. So we can add id="homepage" to the div first and then store a relation as a config like
{"homepage": "pages/index.js"}
And now if we add a new element inside <div id="homepage">, the builder adds the element to the DOM of the div at the client, then takes the whole updated DOM of the div and writes back to the file index.js according to the config
Ok, but the file contains not only HTML markup - it's JS (React) code.
How to keep all the JS code e.g. function HomePage(), return, export default and so on ?
As an option, we can separately load all the JS code as HTML including non-HTML code as #text nodes. Then update the DOM and re-write everything back to the file.
But it sounds complicated and may cause unexpected issues.
So what solution would be the best?
Or maybe there is a ready React-specific solution?
Or maybe it's a bad idea at all to parse and re-write manually hard-coded components by visual builder and the only solution is to store everything as JSON like "homepage":{"div", {"class":""}, "Welcome..."} which is more easy for re-writing ? (but requires a new render)

It depends
I believe the answer that you are looking for very much depends on the level of flexibility that you want your no-code builder to have.
Depending on that, your project could benefit of the trade-offs and advantages of different solutions.
Let's briefly remember that basically a React component will need some props that then will be taken through a render template and output a working HTML. This is assuming a basic case where you don't need your react components to be smarter. Additionally, JSX is just sugar coating over function calls, so you could basically just compose functions to output a React component independently of using the JSX syntax. Hence no need to declare HTML, just changing the output of your no-code tool to JS instead of HTML.
For example, if you can modify how the no-code tool render, you can specify that when moving an element inside another you basically:
Highly Flexible & Customisable
In a highly flexible setup, I will recommend going through the last option you numbered, having a Data-Driven UI is the most common of the cases for complex systems. For example, Figma has an option to export the designs as react components, you can read how they do it here. Basically they take the tag output from figma which is a JSON of tags and create some React componets using templates. If you define "you own UI language" you could have quite a good control over what blocks you can build and define the way of interacting with them (e.g. adding a img component could be quite easy if you know the props and what it needs to render, then creating a React template for it is easy).
Limitations: you require to plan quite well the API of the parser and interaction between different sets of items.
Simple no-code builder
For simpler scenarios you could go with the first approach that you mention, you won't even need to add ids, some tools like React Developer Tools can already inspect the VirtualDOM to understand which part of the render belongs to which React Element (using react internals, which could take some time to understand, but for example you can inspect in the rendered how they use the data-reactid for identification). Knowing this, you can already define the template functions for the render() method (so the JSX output), and separate it code wise, so that when you generate the code, the HTML template is split from the React code as much as possible.
Silly example of how it could look:
// htmlBlockTemplate.js
export const helloPageTemplate = (props) =>
`<div> <h1>${props.title}</h1> </div>` // This can be generated from the `no-code`
// page.jsx
export const Page = (props) => {
return helloPageTemplate(props)
}
Using functions it could look like:
const Page = (props) =>
return React.createElement('div', null,
React.createElement('h1', title: prop.title, `The title ${title}`)
);
}
Limitations: eventho you could, adding custom components (like another React Component or a web component), it becomes more difficult since you will also have to deal with the import graph and probably.
Inevitably you will need to tweak how the render of the component works (rather by creating a parser from your UI language, or changing how the react component is written. If you have a good control of AST, then writing a parser for either of the cases should not be a problem.

Related

Web components loading unknown component

I am creating an application where I want to load the different components from a string (which will be represented in my database).
So I've written a test (before getting the data) where I test the option for loading these dynamically and without me knowing exactly which component to load at a given time:
So far I've succeeded with the dynamic import:
import(`${this.sdkbtnString}`);
Now I've tried a few methods to place a tag that I don't really know what is ahead of time. For instance I attempted:
<${this.sdkiconbtnString}></${this.sdkiconbtnString}>
This naive approach did not work.
Then I attempted to see if I could get something from using:
const t = async import(`${this.sdkbtnString}`);
However I can't really use this for anything as it just returns a module definition.
So how can I load a tag where I don't know exactly what the tag should be. Also what do you feel about this approach is there a better way?

Using existing cljs components in a React project?

https://www.reddit.com/r/Clojure/comments/4el0xi/how_to_use_an_existing_reactjs_component_with/
There is this existing post about using existing ReactJS components in a CLJS/Reagent project. I'm looking to do the opposite. I have a bunch of CLJS components and would like to compile them into a ui library of some sort so that they can be used by React developers. That is, if I have a button CLJS component, I would like to be able to render that Button using < Button /> or mylib.Button(_) etc.. in a React/js app file.
I have read this - https://shadow-cljs.github.io/docs/UsersGuide.html#target-node-library - extensively but it's not quite working out. I've been using ":target :node-library" and I can get simple functions (that return strings/numbers, for example) to compile and work in my app, etc.. but it doesn't work for entire components. For example, my cljs button component takes in :
defn button [props & children]
but when I try to pass in these parameters (I call {lib.button({}, {})} in my App.js file), I get errors like "No protocol method IMap.-dissoc defined", because I'm trying to pass JS objects into CLJS-only functions, I believe. Not sure how to resolve this..
I can explain more on this if it would help clarify. It would also be super helpful if anyone had a reference demo project or any resources they could link me to.
I only have a few suggestions:
You can try to build a new sample project to consume your library with lein new figwheel myproject and use JavaScript interop to move one step at a time closer to the native JS way of using your library.
You can create an interface namespace that can consume JS objects and wrap these into Clojure data structures to sort out the protocol errors you're seeing, eg. functions that take a props parameter and pass down (js->clj props) to the rest of the code underneath.
For the authoritative source, check the Reagent docs, especially this: http://reagent-project.github.io/docs/master/InteropWithReact.html#creating-react-components-from-reagent-components

Use of Template with HTML Custom Elements

I just started learning about the HTML custom elements, and through reading a series of intros, tutorials, and documentation, I think I have a good handle on how it works, but I have a philosophical question on the proper way to use or not use the <template> tag.
Custom elements give you the ability to encapsulate new functionality, simplifying the structure of your HTML document, and allowing you to simply insert a <my-custom-element>...</my-custom-element> tag instead of <div class="my-custom-element"><span class="part1">...</span><span class="part2">...</span></div>.
The class definition for the element then sets up the structure and functionality of that element. A bunch of the tutorials then describe how to use <template>...</template> and <slot>...</slot> to set up the contents of the custom element. You would then have to then include the template code in every HTML document in which you want to use the element rather than setting it up in the custom element class's constructor. Doesn't this run counter to the fact that custom elements help simplify and encapsulate functionality in a way that makes them more portable? Or am I misunderstanding the proper usage and/or placement of the template within the document?
Looking through SO, the closest I can find to addressing this is this question:
How to stamp out template in self contained custom elements with vanilla js?
But the answer essentially sidesteps this all together and says "Don't use <template>," and so doesn't really clear up my confusion.
Actually <template> elements can be imported from another document via HTML Imports, along with the Javascript code that will define the custom element:
<link rel="import" src="my-custom-element.html">
...
<custom-element></custom-element>
So it doesn't need to be included in a every HTML document. This post shows a minimal example.
HTML Imports are implemented only in Chrome and Opera. If you want to use them in the with Firefox and Safari you'll need to use the HTML Imports polyfill.
On the other hand and for the moment, Mozilla and Apple don't intend to implement HTML Imports natively in their respective browsers. Therefore they recommend to define custom elements with pure Javascript modules (with import or <script src="...">), and promote template literals strings instead, which offer some advantages (variables, functions), but are sometimes more complicated to code in an IDE (because of their string representation).
Maybe in the future standard HTML modules will be adopted by all browsers, and <template> will come back in the spotlight...
Note that without HTML Imports you can still import yourself some HTML documents with fetch():
fetch( "template.html" )
.then( stream => stream.text() )
.then( text =>
customElements.define( "c-e", class extends HTMLElement {
constructor() {
super()
this.attachShadow( { mode: 'open'} )
.innerHTML = text
}
} )
)
Update 2019
HTML Imports won't be supported natively after Chrome 73. You should then use the other solutions listed above (the polyfill, an alternate module loader, JS import, or a direct download with fetch).
Disclaimer: I'm an author of the rich-component library mentioned below.
After some time of experimenting with custom elements and recently raising a full blown project based solely upon them I'd like to share my insights on this:
any component tiny as it is, is a candidate to grow to some beast
HTML part of it may grow to a point where it is very non-convenient to keep it within JS
do use template, built and parsed once and from that point cloned and injected into the shadow root - this is the same best practice as to use document fragment instead of mutating a living DOM
if the template contents should be changed from component's instance to instance - some kind of data binding framework may be used, and if minimalist approach on those is taken - it might still be easier and more performant to deal with a cloned-from-template document fragment than operate on string or template literals
In order to not write the same dozens of lines over and over again I've prepared rich-component library, which:
normalizes some API for template provisioning and all those 'clone template, create shadow, inject template's content into it' lines of repeating code
known to fetch html contents when html URL is provided
caches the templates so the fetch is done only once

AngularJS Directive into React App

We are looking at writing a new app using React but will need some existing Angular Directive that are specific for our business, for example a modified Angular Date Picker for example. There are many components so we won't be able to rewrite them all.
I am wondering if anyone has experience or knows the effort or feasibility of this?
The only article I've managed to find on this so far has been. Most resources I find mention going the other way from an Angular App with added React. http://softeng.oicr.on.ca/chang_wang/2017/04/17/Using-AngularJS-components-directives-in-React/
There's a library called angular2react that makes possible reuse angularjs code inside react components. You can see if it fits your needs. :)
I have written a simple demo app showcasing how to achieve it: react-angularjs-bridge
The basic steps are as follows:
Create a react component that is going to host the angular component in the DOM. This component should always render a simple html element (eg. a HTMLDivElement) so that react does not get confused when reconciling the virtual DOM with the real DOM.
After the react component was mounted componentDidMount, create a shadow root under the rendered HTMLDivElement and initialise the angular application under it. This will ensure that the angularJS component is encapsulated and is not affected by the react application and vice-versa
Click here for a demo. Check how the react application styles do not impact the styles of the angular component.
Independently of Angular, ref is a way to go: it gives to access to an underlying DOM node and the ability to manipulate the DOM inside that node in any way you like, including AngularJS. Your changes will stay - at least until react clears the component altogether.
You can create a ref in react by
<divOrAnyOtherDom ref={((el) => {this._elem=el;}).bind(this)}></...
(for the full description and alternate ways to the same end see here: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs)
Anyway, as you now have a DOM node, you can start working with it, i.e. adding angular to the mix inside componentDidMount. Sadly, this is not really obvious - here's a way that has worked for me in the past (moduleName needs to be a module you have put in place already with your logic):
let baseElement = angular.element(this._elem);
let templateElement = angular.element(templateSource);
angular.injector([ 'ng', moduleName ]).invoke(function($compile, $rootScope) {
let cmp = $compile(templateElement);
scpe = $rootScope.$new(false);
scpe.varname = whateverYouHave; # point is: you can set scope variables
cmp(scpe);
baseElement.append(templateElement);
scpe.$digest();
});
Now you have angular inside the DOM node react gave you.
(I've taken this from a slightly different integration: Vaadin to AngularJS, but the principle is the same: Vaadin, just like react, gives you a DOM node. When you're interested in the full code: https://github.com/akquinet/vaangular/blob/master/vaangular/src/main/resources/de/akquinet/engineering/vaadin/vaangular/angular/NgTemplate.js)
Plus: For any callbacks from the angular component, you'll likely have to use setState et al.

React - Parse HTML string and insert components at particular places within it

I'm working on an app for a client who wants to display Zendesk Support article data with richer content than the API/tool supports. The API, powered by a basic WYSIWYG editor on Zendesk, is able to serve up an HTML blob with <p> and <img> and <a> tags. But it's unable to, say, easily embed a video, show an image gallery, or link to another location in the app without necessitating a hard refresh.
So we decided to build out a small amount of DSL syntax whereby the client could type something that would be parsed on our app and turned into the richer content. For example:
[[ video src="youtube.com/whatever" ]]
might, on our app, be parsed out, and replaced with an iframe embedding that video directly in the article.
I've got the above example working just fine, using all sorts of fun/problematic RegExp magic.
I'm aware that parsing HTML with RegExp is a major anti-pattern, and I'm getting around it by not really parsing HTML per se -- I'm just looking for [[...]]).
Basically, I parse each code out, determine what to replace it with, pass back that component, and then call ReactDOMServer.renderToString(replacementEl) to insert the resulting HTML into the article HTML string, which is then rendered to the page using dangerouslySetInnerHtml.
This is working great, despite all the obvious messiness issues. (I know, but the client wants a very feature-rich solution, and wants to use the Zendesk API).
Here's the problem: This solution only works when rendering HTML. It doesn't work when I try to render, for example, a <Link> (from React Router) or a home-made <Gallery /> component.
So I'm trying to figure out if there's a good way to do this. One that ideally doesn't involve going full RegExp on the HTML blob (which is obviously a mess).
I'm guessing that the solution will involve removing the codes, converting the code-free HTML string into actual React components (as opposed to just spitting the string into another element), and then, based on some sort of ledger of where the short codes go in this new React component, inserting the contents into the page.
But I'd appreciate some suggestions if anyone has encountered a similar problem. I want to do this in a way that isn't totally crazy and brittle, though it's obviously an inherently messy problem.
EDIT: To clarify, when I say it doesn't work, what I mean is that the JavaScript associated with the React components doesn't run. If I replace a code with a <Link to="whatever" />, for example, ReactDOMServer.renderToString successfully turns that component into its final HTML (an <a> tag), but nothing happens on click of that tag, because the JavaScript that's actually changing browserHistory on a regular click just isn't run.
So maybe the more pointed question is:
Is there a way to insert React Components into an HTML string in such a way that they retain their JavaScript functionality and don't just become HTML?

Categories

Resources