I'm creating an Angular 2 application that uses CodeMirror as a source code editor. I'd like to add a line widget to it, which requires that I provide a DOM node to CodeMirror. I'd like to put some complex logic that depends on application data in this widget, so it will need to be an Angular component, however I can't figure out how to render an Angular element in a DOM Node (or whether it is even possible). Normally I would do it using a ViewContainerRef, but that doesn't work in this case.
Is there any way to achieve this using Angular 2?
You can always use ng2-codemirror (https://www.npmjs.com/package/ng2-codemirror), that already takes care of that for you.
In anycase, if you need to get a DOM node from an Angular 2 component you can do something like this:
<div #element>
</div>
And then, on your typescript file you can:
import * as ng from "angular2/core";
export class MyComponent {
#ng.ViewChild("element", {read: ng.ElementRef})
element: ng.ElementRef;
ngAfterViewInit(){
this.element.nativeElement; // you dom node is here!
}
}
Related
Is it possible to render AngularJS component inside React component which is rendered from an AngularJS component.
I have a specific scenario where:
<angularjs-component-1>
<react-component>
<angularjs-component-2>
</angularjs-component-2>
</react-component>
</angularjs-component-1>
Inside of AngularJS app I have access to a library of react components which are in separate repo where they are exported and assigned to a window object so that they can be consumed inside of AngularJS app.
Inside of the angularjs-component-1 I have an access to a ref of react-component where I have to inject my angularjs-component-2.
I can append text to react-component from angularjs-component-1, but not sure how to inject another angularjs component/directive inside of it.
I don't have much experience with AngularJS and I am not sure if I am able to access angularjs-component-2 from angularjs-component-1 in order to pass it to the react component.
I don't think I can use angular2react since my react components are in separate repo, exported and assigned to window object so that they can be consumed in angularjs.
UPDATE: Solution found
<angularjs-component-1> has method which is passed to <react-component> that method is called from react and it passes ref to a div in which <angularjs-component-2> will be placed. Logic inside of that method is something like this:
function sendRef(ref) {
var $angularComponent2 = angular.element('<angularjs-
component-2 ng-model="propName"></angularjs-component-2>');
var reactComponent = angular.element(ref.current);
reactComponent.append($angularComponent2);
reactComponent.injector().invoke(function($compile) {
var scope = angular.element($angularComponent2).scope();
// if you need to assign some values to angular-component-2 scope
// this is the place to do it
scope.propName = $scope.someValue;
$compile($angularComponent2)(scope);
});
}
Hope this helps someone.
Important note:
I am aware of createRoot and root.unmount()! Unfortunately (If I understand this correctly) they should be used just once in the application for mounting the react application.
Problem description:
In our app we have a modal component that is rendered dynamically and added to the body of the html via ReactDOM.render(). When this modal is hidden, we unmountComponentAtNode().
Unfortunately, after upgrading to react18, unmountComponentAtNode becomes deprecated and the new unmount is (in my understanding) for the root only. The same problem is about if I try to modify the ReactDOM.Render() for createRoot. Then we would have 2 roots in the app which is wrong.
What is the proper way to attach the modal to the body element (next to root!) and unmount it after it should be destroyed? The implementation is a little bit "weird" (partially in jsx, partially not...) and I would like to avoid refactoring the whole component as there will be a lot of refactoring already in the code... So I would like to focus on refactoring this component (into jsx one) later. Now I have to figure out only the rendering / unmounting. I have been thinking about using Portals, but anyway I have to create that elements somehow and render them into the DOM where portals does not help me a lot.
Calling the createRoot and then render on the root in this modal component fires an error You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it. which is obvious. But there is no "useRoot()" hook or anything like that. Should I store the returned object (root) in some context or somewhere to use it later? Or what should be the best option to call the render? :/
I know how I should do that with classical functional component... But maybe there is some way that I can just refactor a piece of the code instead of the whole component and all its usecases. Maybe there is something I am not aware of (there is definitely thousands of things I am not aware of :D) that should simplify my life...
function modal() {
return (
<div>
...
</div>
)
}
Modal.show = () => {
modalEl = document.createElement('div');
util.destroy(el) => {
ReactDOM.unmountComponentAtNode(el);
el.remove();
}
const childs = props.childs;
REactDOM.render(childs, modalEl);
}
When I was thinking about portals, I thought I will just rewrite the last line of ReactDOM.render to portal like createPortal(childs, modalEl), unfortunately this does not render anything (except modalEl, but no childs inside). The childs are of type ReactNode (using typescript) and they are not empty (because of ReactDOM.render works without any problem).
I do want to create a React component tree in memory (not in the DOM) with the same behavior a React Portal has, that is, it should use contexts from the parent component tree.
So is it possible to create something like Portal but instead of render it on a DOM node, "render" it in memory (outside the DOM)?
Note that what I need is the component life-cycle of some components in the tree while the visual part will be hidden, but in my case if I put the component tree in the DOM within a hidden div the performance is not good and then I want to test a different approach.
Two possibilities come to my mind:
1) create a portal to a DocumentFragment
When you use ReactDOM.createPortal, you could point it to a documentFragment in memory rather than an element on the page. The nodes that get created should then be appended to that fragment in memory. Whether or not this meets your criteria or not i'm not sure, but it would look something like this:
class Example extends Component {
constructor (props) {
super(props);
this.fragment = document.createDocumentFragment();
}
render() {
return ReactDOM.createPortal(
this.children,
this.fragment
);
}
}
2) Create a custom reconciler.
If you want the data to be output in some particular format instead of being DOM nodes, you could create a custom reconciler. This is non-trivial thing to do and uses the react-reconciler package, which the react team describes as "experimental", but it would give you very precise control of what to do with the results of the render.
If this is something you want to do you can read some at the react-reconciler readme, and it may be useful to look at the configuration files that are used for other reconcilers (see the "host config" links near the bottom of the readme)
The goal is to create a W3C web component in React which supports arbitrary DOM nodes as children.
The initial markup in the browser should be like this:
<custom-button>
some <u>styled</u> text here
</custom-button>
I would then:
call customElements.define() to register my React code as the implementation of the custom-button,
inside the implementation create a shadow root inside <custom-button>,
afterwards call ReactDOM.render(<CustomButton ...>, shadowRoot); to populate this shadow root
The DOM structure in the browser is now:
<custom-button>
#shadow-root
<div class="CustomButton">
<!-- x -->
</div>
some <u>styled</u> text here
</custom-button>
But this is not really the desired results; I need the original content of <custom-button> to render inside <div class="CustomButton"> now.
I am aware of the React children prop, but as I understand it, it will only work for children that were also declared inside the React implementation and not with arbitrary DOM nodes that were created on the surrounding web component element.
On the other hand I read that Angular implements a concept they call "transclusion" in which they provide slots which DOM children web component will be mapped into. Is there anything similar in React?
One quick "workaround" that I could think of in React is to:
obtain the ref of the top-level JSX tag,
find the shadow root in ref.parentNode
iterate through all children and re-attach them to ref as new parent
This would only cover the initialization though. If, at runtime, some other script tried to append more children to <custom-button>, or re-order or delete previously inserted children, this would probably fail.
I have just realized that the <slot /> of the web components standard solves the problem.
So, to reflect the content of the <custom-element> inside the React component, the following is completely sufficient:
render() {
return (
<div className="customElement">
...
<slot />
...
</div>
);
}
I am trying to learn component based frameworks for frontend apps. Currently, I am using RiotJS but it applies to any framework that uses the same concepts (React, Angular 2.0 etc).
In a basic MVC frontend frameworks (e.g AngularJS), the controllers and router were very connected to each other. But with a component based framework, the line between router and controllers is much wider. And this is what confuses me the most.
Here is one example of an app that I am trying to build:
I have three main UI elements: Navigation Bar, Content Area, and Signin Form. So, I created three components: my-navbar, my-content, my-signin. I was able to create multiple routes per component. So for example, if there is a route changes, the navbar updates the active "module." Making this was easy because all I am doing is changing class of a list item.
Now, I want to load other tags inside <my-content></my-content>. In AngularJS, I was always changing the view completely (using ui-router). How can I achieve that in a component based framework. Let's say that I have 2 more components called my-content-users-list-view, my-content-users-detail-view. How can I add them to the component my-content based on the route? Do I just add it like document.innerHTML += '<my-content-users-list-view></my-content-users-list-view>?
I know most of my syntax is RiotJS but I will understand it if you write it in another framework's syntax.
Thank you!
Essentially, yes, you could just append your tag as a DOM node and then call Riot to mount it:
riot.route('/awesome-route', () => {
const tag = 'your-awesome-tag';
const options = { ... };
const elem = document.createElement(tag);
// TODO empty your content container using pure DOM or jQuery to get rid of the previous route's view...
document.querySelector('#content').appendChild(elem);
riot.mount(elem, tag, options);
});