ReactJS dealing with server-side loaded DOM - dom

I'm just about to experiment with reactjs I'm new to it. Is reactjs capabile to add a component to existing DOM.
So I have already server-side created DOM and on the flow reactjs should add a component which should render inside body but keep what already is there.
<script type="text/jsx">
var VisualEditor = React.createClass({
render: function() {
return (
<div>Hello, world!</div>
);
}
})
var initVisualEditor = <VisualEditor params={true} />
React.render(initVisualEditor , document.body);
</script>
this one is removing everything inside body and is returning
<div>Hello, world!</div>

React won't add a new div.
The render function is where the component will be rendered and replaces the current code already there.
I recommend you make a div with an id in the template and then render to that in React.render.
Template:
<body><div id="app"></div></body>
React:
React.render(
<Component />,
document.getElementById('app')
)
It's also bad practice to render to the body.

Related

Need proper way to render jsx component inside Leaflet popup when using geojson pointToLayer function

Hi is there any way to pass jsx component to bindPopup function so I can push redux commands on button click?
pointToLayer={(
geoJsonPoint: Feature<Point, DeviceProperties>,
latlng,
) => {
const marker = L.marker(latlng);
marker.setIcon(
markerIcon({ variant: geoJsonPoint.properties.relation }),
);
const sddds = (
<div className="font-quicksand">
<h2>{geoJsonPoint.properties.id}</h2>
<h2>{geoJsonPoint.properties.name}</h2>
<p>{geoJsonPoint.properties.description}</p>
<p>{geoJsonPoint.properties.ownerId}</p>
<a
onClick={() => {
dispatch(setDevice(geoJsonPoint.properties));
}}
>
Open device details
</a>
</div>
);
marker.bindPopup(renderToString(sddds));
return marker;
}}
I know I can use react leaflet component but that way I cant pass props into every marker options (I mean marker as layer).
So this has been discussed a bit. There is an issue in the react-leaflet repo discussing this, whose conclusion is to simply use vanilla JS within the bindPopup method to create your popup. I don't like this solution at all, especially when you're trying to use very react oriented event handlers (like react-redux actions) from within a popup.
The question React-leaflet geojson onEachFeature popup with custom react component was asked, which you may have read, as you use react's renderToString method in your code. But as you've probably discovered, this does not maintain any interactivity or JS that your JSX may include. The answerer there came up with the idea of using a modal instead of a popup, but that doesn't exactly answer your question or truly using JSX in a popup based off of a point-layer geojson.
Ultimately, you will not be able to return JSX from the pointToLayer function that is interactive. I think this would be a nice feature that react-leaflet doesn't currently implement. Within the closure of the pointToLayer function, there's no good way to directly write fully functional JSX.
I played with this for a bit, trying to harness pointToLayer and save the feature of each iteration to state, and then render a Marker with Popup from that, but it got me thinking - why bother? Just ditch the GeoJSON component altogether and render your Markers and Popups directly from the JSON object. Like this:
{myGeoJson.features.map((feature, index) => {
return (
<Marker
key={index}
position={L.latLng(feature.geometry.coordinates.reverse())}
>
<Popup>
<button
onClick={() => { yourReduxAction() }}
>
Click meeee
</button>
</Popup>
</Marker>
);
})}
Working sandbox
In this way, you need to work a little harder by manually transforming your GeoJSON into Markers with Popups, but not nearly as hard as trying to bend over backwards by going from JSX (<GeoJSON />) to vanilla JS (pointToLayer) back to JSX (<Popup />).
These are two solutions I have come to and want to share if someone is having same problem.
My problem with using leaflet-react Popup component is that it will not pass geojson properties to marker layer when I just map over geojson object because react-leaflet Marker does not have api for feature like geojson layer does and I need to access those properties via marker layers in other parts of map.
Solution 1:
Use ReactDOM.render() inside pointToLayer method, react will show warning about pure functions but it will work. You just shoud not render imported component because it will complain about store and redux provider, instead paste component code inside render. If you want to avoid warnings create another function / hook and render code inside its useEffect() to container (div or something).
Here is example:
const popup = L.popup();
const marker = L.marker(latlng);
const container = L.DomUtil.create('div');
render(
<div>
<h2>{props.id}</h2>
<h2>{props.name}</h2>
<p>{props.description}</p>
<p>{props.ownerId}</p>
<a onClick={() => dispatch(setDevice(geoJsonPoint.properties))}></a>
</div>,
container,
);
popup.setContent(container);
marker.bindPopup(popup);
return marker;
With custom hook / function:
const useRenderPopup = (props) => {
const container = L.DomUtil('div');
const dispatch = useAppDispatch()
useEffect(() => {
render(
<div>
<h2>{props.id}</h2>
<h2>{props.name}</h2>
<p>{props.description}</p>
<p>{props.ownerId}</p>
<a onClick={() => dispatch(setDevice(props.geoJsonPoint.properties))}></a>
</div>,
container,
);
},[])
return container;
}
and just call this function like popup.setContent(useRenderPopup(someprop)), this way there will be no warning.
Solution 2:
Render everything static with renderToString() and other stuff that need to trigger redux update attach event listeners.
const popup = L.popup();
const marker = L.marker(latlng);
const link = L.DomUtil.create('a');
const container = L.DomUtil.create('div');
const content = <DeviceSummary {...geoJsonPoint.properties} />;
marker.setIcon(markerIcon({ variant: geoJsonPoint.properties.relation }));
link.addEventListener('click', () =>
dispatch(setDevice(geoJsonPoint.properties)),
);
link.innerHTML = 'Show device details';
container.innerHTML = renderToString(content);
container.appendChild(link);
popup.setContent(container);
marker.bindPopup(popup);
return marker;
Here DeviceSummary component is static so I render it as a string and later append link with redux callback added as event listener to it.
(both solutions except custom function example goes into pointToLatyer method inside geoJSON layer)

Angular 2: change DOM elements outside the root AppComponent

I have an observable -- loading$ -- that outputs true when I want to show a loading overlay in the UI, and false when it's time to remove that overlay. The visibility is controlled with a CSS class.
When the Observable emits true, I want to add the CSS class to the <body>, and remove it on false.
html
<body class="">
<my-app>Angular app goes here</my-app>
</body>
As you can see, the <body> is outside of my Angular 2 app, so I cannot use property binding to change the class. This is what I currently do:
AppComponent.ts
loading$.subscribe(loading =>{
if(loading) document.querySelector('body').classList.add('loading');
else document.querySelector('body').classList.remove('loading');
});
This works well. The loading class is added/removed when the loading$ observable emits true/false. The problem is that I'd like to run my app in a web worker which means no access to the DOM. Plus, Angular recommends against manipulating the DOM directly.
How can I use Angular APIs to change <body>?
Angular 2, typescript 2, rxjs 5 beta 12
PS: This question looks promising, but the key link is dead. I've also seen a couple of suggestions that worked with Beta releases but are now obsolete (example)
If you want an uninterrupted animation by DOM replacement in the middle of bootstrapping the app then use the following approach.
Put the overlay after my-app element.
<my-app></my-app>
<div id="overlay"></div>
Add #HostBinding to class.ready in main component app.component.ts:
#HostBinding('class.ready') ready: boolean = false;
Change the property when the data is loaded with the initial call (splash is still visible when your screen is not fully rendered).
constructor(private contextService: ContextService) {
someService.initCall().then(r => this.ready = true);
}
Use CSS to hide the loader:
my-app.ready + .overlay {
animation: hideOverlay 1s forwards;
}
#keyframes hideOverlay {
0% {
opacity: 1;
}
100% {
opacity: 0;
visibility: hidden;
}
}
#ktretyak 's solution got things to work for me. Basically, instead of using <body> I put the div that takes the loading class within <my-app>
index.html
<body>
<my-app>
<div id="overlay" class="loading"></div>
</my-app>
</body>
CSS
#overlay {
position:fixed;
top:0;
left:0;
bottom:0;
right:0;
display:none;
}
#overlay.loading {
display:block
}
Thanks to the CSS styles, and the fact that loading is enabled to start, the overlay is shown while the Javascript loads and Angular bootstraps.
Once Angular is loaded though, everything within <my-app> will be replaced with AppComponent template. As I explained in the comments beneath the OP, I need ongoing access to this loading overlay, because I show it during the loading of new routes (as users navigate from one routed component to another). So I had to include the same overlay div in the template
app.component.html
<div id="overlay" [class.loading]="showLoading"></div>
<router-outlet></router-outlet>
Now, when my loading$ observable fires, I change a class property showLoading and Angular takes care of updating the overlay since it is now part of AppComponent
app.component.ts
// set to true to show loading overlay. False to hide
showLoading:boolean = true;
ngOnInit(){
// show/hide overlay depending on value emitted
loading$.subscribe(loading => this.showLoading = loading);
}
Try to do like this:
<my-app>
<div class="your-class-for-loading"></div>
</my-app>
When my-app is ready, div will be automatically removed.

Reactjs together with TinyMCE editor code plugin

I'm using Reactjs together with the tinyMCE 4.1.10 html editor (together with the code plugin) and bootsrap css + js elements. A fairly working setup after a few quirks with the editor have been removed (manual destruction if the parent element unmounts)
Now the question: The textarea input of the code plugin does not receive any focus, click or key events and is basically dissabled. Setting the value via javascript works just fine, but it does not function as a normal html input.
It is opened as the following:
datatable as react components
opens bootsrap modal as react component
initializes tinymce on textareas inside of the modal
loads the code plugin (which itself then is not accepting any kind of input anymore)
My initilization of the editor looks like this:
componentDidMount: function(){
tinymce.init({
selector: '.widget-tinymce'
, height : 200
, resize : true
, plugins : 'code'
})
}
My guess would be, that react.js is somehow blocking or intersepting the events here. If I remove the react modal DOM, it is just working fine.
Does anybody has an idea, what is causing this or how to simply debug it further?
Thx a lot!
if you are using Material UI. disable Material UI Dialog's enforce focus by adding a prop disableEnforceFocus={true} and optionally disableAutoFocus={ true}
What does your html/jsx look like in your component?
My guess is that react might be treating your input as a Controlled Component
If you're setting the value attribute when you render, you'll want to wait, and do that via props or state instead.
Alright, so it turned out that bootstrap modals javascript is somehow highjacking this. In favor of saving some time I decided not to dig realy into this but just to create my own modal js inside of the jsx.
Aparently there is also React Bootstrap, but it looks at the moment to much beta for me in order to take this additional dependency in.
The final code looks like this, in case it becomes handy at some point:
Modal = React.createClass({
show: function() {
appBody.addClass('modal-open');
$(this.getDOMNode()).css('opacity', 0).show().scrollTop(0).animate({opacity: 1}, 500);
}
, hide: function(e){
if (e) e.stopPropagation();
if (!e || $(e.target).data('close') == true) {
appBody.removeClass('modal-open');
$(this.getDOMNode()).animate({opacity: 0}, 300, function(){
$(this).hide();
});
}
}
, showLoading: function(){
this.refs.loader.show();
}
, hideLoading: function(){
this.refs.loader.hide();
}
, render: function() {
return (
<div className="modal overlay" tabIndex="-1" role="dialog" data-close="true" onClick={this.hide}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button type="button" className="close" onClick={this.hide} data-close="true" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 className="modal-title" id="myModalLabel">{this.props.title}</h4>
</div>
<div className="modal-body" id="overlay-body">
{this.props.children}
<AjaxLoader ref="loader"/>
</div>
</div>
</div>
</div>
);
}
})
Best wishes
Andreas
Material UI: disable Dialog's enforce focus by adding a prop disableEnforceFocus={true} and optionally disableAutoFocus={ true}

how to create dojo data onclick event for dojo dynamic tree in zend framework for programatic approach

i am new to Zend framework and dojo.i have created dynamic tree structure using dojo in zend framework but i want to make on click of each folder and element of tree structure to naigation to another form by writing a function .Pleas check my code and help me i have gone through some dojo on click event link and could not solve ..
<html>
<head>
<title> Tree Structure </title>
<link rel="stylesheet" href=/dojo/dijit/themes/ claro/claro.css" />
<script type="text/javascript" src="/ dojo/dojo/dojo.js"
djConfig="parseOnLoad:true, isDebug:true" >
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.TabContainer")
dojo.require("dijit.form.Button");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dijit.tree.ForestStoreModel");
dojo.require("dijit.Tree");
dojo.require("dojo.parser");
function myTree( domLocation ) {
var store = new dojo.data.ItemFileReadStore({url: "http://localhost/CMTaSS_module1.0/public/dojo/cbtree/datastore/Family-1.7.json"});
var treeModel = new dijit.tree.TreeStoreModel({
store: store,
query: { name:'John'}
});
var tree = new dijit.Tree( {
model: treeModel,
id: "mytree",
openOnClick: true
});
tree.placeAt( domLocation );
}
var tree_obj = new dijit.Tree({
model: treeModel
},
"tree_obj");
dojo.connect(tree_obj, 'onClick', function(item, node, evt){
console.log("Item", item);
console.log("Node", node);
console.log("Event", evt);
//console.log('node: ' +tree_obj.getLabel(node));
//console.log('event: ' +tree_obj.getLabel(evt));
console.log('identifier: ' + tree_obj.getLabel(item))
});
</script>
</head>
<body class="claro"><br><br><br>
<div id="CheckboxTree">
<script type="text/javascript">
myTree("CheckboxTree");
</script>
</div>
</body>
</html>
Looks like your code sample is not formatted correctly as some of the logic is outside the myTree function. I used jsbeautifier.org to confirm this.
Other notes...
You should wait until dojo is ready. Either use dojo.addonload or, create a widget and reference that widget in the html portion of your code. Widgets are amazing and are what make dojo great, so getting a grasp on how they work will pay dividends.
Also note that if creating a widget programmatically (new dijit.Tree), you should call startup on it. This is not needed when creating it declaratively (inline html).
I hope this helps.

Determine if a dijit's DOM has finished loading

Is there a way to query a dojo dijit to tell if the dijit's DOM has finished loading?
I believe if the dijit's "domNode" property is set, the DOM for the widget has been created. The widget may or may not be attached to the larger DOM, that can be a separate step. Checking domNode.parentNode as being a real element might help, but it is no guarantee that parentNode is also in the live document.
I believe something like this might work, although I didn't test it :
if (yourWidget.domNode) {
// here your widget has been rendered, but not necessarily its child widgets
} else {
// here the domNode hasn't been defined yet, so the widget is not ready
}
Dijit widgets' rendering is handled through extension points, called in that order :
postMixinProperties
buildRendering
postCreate <== at this point, your widget has been turned into HTML and inserted into the page, and you can access properties like this.domNode. However, none of the child widgets has been taken care of
startup : this is the last extension point called, after all the child widgets have been drawn
(This is the explanation of the widgets' lifecycle on "Mastering Dojo").
EXAMPLE :
<html>
<head>
<script src="path/to/your/dojo/dojo.js" djConfig="parseOnLoad: true, isDebug: true"></script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dojox.lang.aspect");
dojo.require("dijit.form.Button");
// Define your aspects
var startupAspect = {
before : function() {console.debug("About to execute the startup extension point");},
after : function() {console.debug("Finished invoking the startup extension point");},
};
function traceWidget(theWidget) {
// Attach the aspect to the advised method
dojox.lang.aspect.advise(theWidget, "startup", startupAspect);
}
</script>
</head>
<body>
<button dojoType="dijit.form.Button" type=button">
dijitWidget
<script type="dojo/method" event="postCreate">
traceWidget(this);
</script>
<script type="dojo/method" event="startup">
console.debug("Inside startup");
</script>
</button>
</body>
</html>