Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/bubblydoo/angular-react
Use React in Angular and Angular in React, easily
https://github.com/bubblydoo/angular-react
angular microfrontend react
Last synced: 1 day ago
JSON representation
Use React in Angular and Angular in React, easily
- Host: GitHub
- URL: https://github.com/bubblydoo/angular-react
- Owner: bubblydoo
- License: mit
- Created: 2022-05-24T10:39:43.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-11-05T21:30:18.000Z (2 months ago)
- Last Synced: 2025-01-01T03:18:24.519Z (8 days ago)
- Topics: angular, microfrontend, react
- Language: TypeScript
- Homepage: https://dev.to/bubblydoo/transitioning-from-angular-to-react-without-starting-from-scratch-j66
- Size: 2.03 MB
- Stars: 31
- Watchers: 6
- Forks: 9
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# React in Angular and Angular in React
This is a small Angular library that lets you use React components inside Angular projects.
```html
```
```tsx
function ReactComponent({ text }) {
return
}
```## Installation
```bash
npm i @bubblydoo/angular-react
``````ts
import { AngularReactModule } from '@bubblydoo/angular-react'@NgModule({
...,
imports: [
...,
AngularReactModule
]
})
```## Features
### `ReactWrapperComponent`
Use this component when you want to use React in Angular.
It takes two inputs:
- `component`: A React component
- `props?`: The props you want to pass to the React componentThe React component will be first rendered on `ngAfterViewInit` and rerendered on every `ngOnChanges` call.
```ts
import Button from './button'@Component({
template: ``
})
class AppComponent {
Button = Button
}
```### `AngularWrapper`
Use this component when you want to use Angular in React.
It takes a few inputs:
- `component`: An Angular component
- `inputs?`: The inputs you want to pass to the Angular component, in an object
- `outputs?`: The outputs you want to pass to the Angular component, in an object
- `events?`: The events from the Angular component to listen to, using `addEventListener`. Event handlers are wrapped in `NgZone.run`
- `ref?`: The ref to the rendered DOM element (uses `React.forwardRef`)```tsx
import { TextComponent } from './text/text.component'function Text(props) {
return (
console.log('clicked') }}/>
)
}
```### `useInjected`
The Angular Injector is provided on each React component by default using React Context. You can use Angular services and other injectables with it:
```tsx
import { useInjected } from '@bubblydoo/angular-react'const authService = useInjected(AuthService)
```### `useObservable`
Because consuming observables is so common, we added a helper hook for it:
```tsx
import { useObservable, useInjected } from '@bubblydoo/angular-react'function LoginStatus() {
const authService = useInjected(AuthService)const [value, error, completed] = useObservable(authService.isAuthenticated$)
if (error) return <>Something went wrong!<>
return <>{value ? "Logged in!" : "Not logged in"}>
}
```### Global React Context
If you want to have a global React Context, you can register it as follows:
```ts
// app.component.tsconstructor(angularReact: AngularReactService) {
const client = new ApolloClient()
// equivalent to ({ children }) => {children}
angularReact.wrappers.push(({ children }) => React.createElement(ApolloProvider, { client, children }))
}
```In this example, we use `ApolloProvider` to provide a client to each React element. We can then use `useQuery` in all React components.
This is only needed when your host app is an Angular app. If you're using Angular-in-React, the context will be bridged.
### Refs
You can get a ref to the Angular component instance as follows:
```tsx
import { ComponentRef } from '@angular/core'const ref = useRef>()
```
To get the component instance, use `ref.instance`. To get a reference to the Angular component's HTML element, use `ref.location.nativeElement`.
To forward a ref to a React component, you can simply use the props:
```tsx
const Message = forwardRef((props, ref) => {
return{props.message}
})@Component({
template: ``,
})
export class MessageComponent {
Message = Messagemessage = 'hi!'
ref(div: HTMLElement) {
div.innerHTML = 'hi from the callback ref!'
}
}
```### Reading React contexts in Angular
```tsx
@Component({
selector: "inner",
template: `number: {{ number$ | async }}`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
class InnerComponent {
number$ = this.contexts.read(NumberContext)constructor(@Inject(InjectableReactContextToken) public contexts: InjectableReactContext) {}
}function App() {
const [number, setNumber] = useState(42)
return (
setNumber(number + 1)}>increment
)
}
```### Using templates
#### `useToAngularTemplateRef`: to convert a React component into a `TemplateRef`
```tsx
import { useToAngularTemplateRef } from "@bubblydoo/angular-react"@Component({
selector: 'message',
template: `
`,
})
class MessageComponent {
@Input() tmpl: TemplateRef<{ message: string }>
@Input() message: stringconstructor(public injector: Injector) {}
}function Text(props: { message: string }) {
return <>{props.message}>
}function Message(props: { message: string }) {
const tmpl = useToAngularTemplateRef(Text)const inputs = useMemo(() => ({
message: props.message,
tmpl,
}), [props.message, tmpl])return
}
```Note: `useToAngularTemplateRef` is meant for usage with `[ngTemplateOutletInjector]="injector"`. If you can't use that, use `useToAngularTemplateRefBoundToContextAndPortals` instead.
#### `useFromAngularTemplateRef`: to convert a `TemplateRef` into a React component
```tsx
function Message(props: {
message: string
tmpl: TemplateRef<{ message: string }>
}) {
const Template = useFromAngularTemplateRef(props.tmpl)return
}@Component({
selector: "outer",
template: `
{{ message }}
`,
})
class MessageComponent {
Message = Message@Input() message!: string
}
```## Developing
You can test the functionality of the components inside a local Storybook:
```bash
yarn storybook
```If you want to use your local build in an Angular project, you'll need to build it:
```bash
yarn build
```Then, use `yarn link`:
```bash
cd dist/angular-react
yarn link # this will link @bubblydoo/angular-react to dist/angular-react
# or `npm link`
```In your Angular project:
```bash
yarn link @bubblydoo/angular-react
# or `npm link @bubblydoo/angular-react`
````node_modules/@bubblydoo/angular-react` will then be symlinked to `dist/angular-react`.
You might want to use resolutions or overrides if you run into NG0203 errors.
```json
"resolutions": {
"@bubblydoo/angular-react": "file:../angular-react/dist/angular-react"
}
```## Usage notes
### `this` is undefined when passing an Angular component method as a React prop
Angular component methods are always called with the component instance as `this`. When you pass an Angular method as a prop to a React component, `this` will be `undefined`.
```ts
@Component({
template: ``
})
class AppComponent {
Button = ButtononClick() {
console.log(this) // undefined
}
}
```You can fix it as follows:
```ts
@Component({
template: ``
})
class AppComponent {
Button = ButtononClick = () => {
console.log(this) // AppComponent instance
}
}
```## Further reading
See this blog post for the motivation and more details: [Transitioning from Angular to React, without starting from scratch](https://dev.to/bubblydoo/transitioning-from-angular-to-react-without-starting-from-scratch-j66)