react-angularjs-adapter
v1.1.1
Published
Mount AngularJS components in React and vice versa, with support for React contexts
Maintainers
Readme
react-angularjs-adapter 
Mount AngularJS (AngularJS 1 only) components in React, and React components in AngularJS. Supports React 19 and React contexts.
Based on react2angular and angular2react, but with added support for React contexts and newer versions of React, along with a few minor bugfixes. The support for contexts allows you to have a component hierarchy like React Provider > AngularJS Component(s) > React Consumer and the consumer will be able to access the provider's context even with the AngularJS components in-between.
Usage
First install the library:
npm install react-angularjs-adapterThen use one of the two exported functions:
- angular2react - Convert a component from AngularJS to React
- react2angular - Convert a component from React to AngularJS
angular2react
This function converts a component from AngularJS to React.
Function signature
function angular2react<Props extends Record<string, unknown>>(
componentName: string,
component: angular.IComponentOptions,
$injector?: angular.auto.IInjectorService,
): React.FunctionComponent<Props>;Usage
Start with the AngularJS component definition you want to convert. For example:
const angularComponent: angular.IComponentOptions = {
bindings: {
fooBar: "<",
baz: "<",
},
template: `
<p>FooBar: {{$ctrl.fooBar}}</p>
<p>Baz: {{$ctrl.baz}}</p>
`,
};
angular.module("myModule", []).component("angularComponent", angularComponent);You will also need to call the setDefaultInjector function with a reference to the $injector for the AngularJS application your component is registered in. This is necessary so that angular2react can compile your component. This only needs to be done once:
import { setDefaultInjector } from "react-angularjs-adapter";
angular.module("myModule").run(["$injector", setDefaultInjector]);Then, use angular2react to convert your component to React:
import { angular2react } from "react-angularjs-adapter";
// Define the Prop types based on the component's bindings
interface Props {
fooBar: number;
baz: string;
}
// Create the React component
const ReactComponent = angular2react<Props>("angularComponent", angularComponent);
// Then in your JSX:
<ReactComponent fooBar={42} baz="lorem ipsum" />;Caveats
- The Angular app must be bootstrapped before attempting to render any converted components in React.
- If your page contains multiple bootstrapped AngularJS applications, you should pass the correct
$injectoras the third parameter toangular2react, instead of setting a default one with thesetDefaultInjectorfunction. - Only one-way bindings (
<and@) are supported, because React props only allow passing data from parent to child. Instead of two-way bindings, consider using callback functions bound with<. Note that such callbacks will be run from outside the context of AngularJS, so you may need to use$scope.$applyto see the changes. - While you can use
childrenas a binding, you cannot pass elements as children, only primitive data. For example, you couldn't do something like:
But you could do:<MyAngularButton> <span>Button Text</span> </MyAngularButton>
And<MyAngularButton>Button Text</MyAngularButton>"Button Text"will be passed to the AngularJS component via thechildrenbinding.
react2angular
This function converts a component from React to AngularJS.
Function signature
function react2angular<Props extends object>(
Component: React.ComponentType<Props>,
bindingNames: (keyof Props)[] = [],
injectNames: (keyof Props)[] = [],
): angular.IComponentOptions;Usage
Start with the React component you want to convert, for example:
interface Props {
fooBar: number;
baz: string;
$location: angular.ILocationService;
}
function ReactComponent(props: Props) {
return (
<div>
<p>FooBar: {props.fooBar}</p>
<p>Baz: {props.baz}</p>
<p>Location: {props.$location.absUrl()}</p>
</div>
);
}And expose it to AngularJS using react2angular:
import { react2angular } from "react-angularjs-adapter";
const angularComponent = react2angular(ReactComponent, ["fooBar", "baz"], ["$location"]);
angular.module("myModule", []).component("angularComponent", angularComponent);The second argument of the react2angular function is a string array of all the binding names for the component, which will be passed to the React component as props. The third (optional) argument is a string array of any AngularJS dependencies you want injected, which will also be passed to the React component as props.
Now, you can use the component just like any other AngularJS component:
<angular-component foo-bar="42" baz="'lorem ipsum'"></angular-component>Caveats
- All bindings on the component will be one-way '
<' bindings, so if you want to pass a raw string, make sure to wrap it in quotes, likebaz="'lorem ipsum'"above. To achieve two-way data transfer, use callback functions. - You can't use transclusion to pass child elements, all data must be passed via the bindings. For example, you couldn't do something like:
Instead, you would have to pass the label as an attribute via the bindings:<my-react-button>Button Text</my-react-button><my-react-button label="'Button Text'"></my-react-button>
React Context Example
Whenever you use a react2angular component, it will search up the DOM tree for an ancestor angular2react component. If one is found, the library will create a React portal connecting the React components, allowing you to use the context from React ancestors even when there are AngularJS components in-between. Here is an example to illustrate this:
import React from "react";
import { createRoot } from "react-dom/client";
import angular from "angular";
import { react2angular, angular2react, setDefaultInjector } from "react-angularjs-adapter";
// Define a React context, a provider component, and a consumer component
const context = React.createContext({ value: NaN, increment: () => {} });
function ReactProvider(props: React.PropsWithChildren<{}>) {
const [value, setValue] = React.useState(0);
return (
<context.Provider value={{ value, increment: () => setValue(value + 1) }}>
<div style={{ border: "2px solid blue", padding: "10px" }}>
<p>Hello from React provider!</p>
{props.children}
</div>
</context.Provider>
);
}
function ReactConsumer() {
const ctx = React.useContext(context);
return (
<div style={{ border: "2px solid green", padding: "10px" }}>
<p>Hello from React consumer!</p>
<p>
Value: {ctx.value} <button onClick={ctx.increment}>increment</button>
</p>
</div>
);
}
// Convert the consumer to AngularJS so it can be rendered in angularComponent
const angularReactConsumer = react2angular(ReactConsumer);
// Define an AngularJS component which renders the converted React consumer
const angularComponent: angular.IComponentOptions = {
template: `
<div style="border: 2px solid red; padding: 10px">
<p>Hello, from AngularJS!</p>
<angular-react-consumer></angular-react-consumer>
</div>`,
};
// Convert angularComponent to React so it can be rendered as a child of the provider
const ReactAngularComponent = angular2react("angularComponent", angularComponent);
// Set up and bootstrap angular, and make injector available to angular2react
angular
.module("example-app", [])
.component("angularComponent", angularComponent)
.component("angularReactConsumer", angularReactConsumer);
const $injector = angular.bootstrap(document.documentElement, ["example-app"]);
setDefaultInjector($injector);
// Render everything
createRoot(document.getElementById("root")!).render(
<ReactProvider>
<ReactAngularComponent />
</ReactProvider>,
);When you run this app, you can see that the context is passed from the the provider, through the AngularJS component, and to the consumer, which is able to both read and update it:
Credits
This library is based on and/or inspired by:
