react2angular icon indicating copy to clipboard operation
react2angular copied to clipboard

How to inject angularjs dependencies in child react components?

Open coreysnyder opened this issue 6 years ago • 1 comments

Let's say I use this library to attach a react component to a route /users.

import { react2angular } from 'react2angular';
import EditContractContainer from './EditContractContainer.jsx';
import EpisodeEditorApiMod from '../../yamsf/episode-editor';
import PayorApiMod from '../../yamsf/payer-service';
var angular = require('angular');

module.exports = angular.module('mymod.edit', [
  EpisodeEditorApiMod, PayorApiMod
])
.component('editContractContainer', react2angular(EditContractContainer, [], ['$injector']))
.config(function($stateProvider) {
  $stateProvider.state({
    name: 'EditContractContainer',
    url: '/edit-contract/:contractId',
    component: 'editContractContainer'
  });
})

.name;

And then Then that react component has it's own child components. How do those child components get access to angularjs services?

I ran into this so I came up w/ the idea to inject $injector into my parent component. Then I pass it down as props to the children. Then they can use this to pick of any angularjs services they need.

    this.$stateParams = $injector.get('$stateParams');
    this.contractService = $injector.get('contractService');

But there's a catch.. You can only inject providers which have been added as dependencies to the parent module. So that means if a downstream component wants to $inject userService, I'd have to go back up to the parent module where the react2angular component is added, and add:

import UserAPIMod from '../../yamsf/user-service';

And then add it as a requirement

module.exports = angular.module('mymod.edit', [
  EpisodeEditorApiMod, PayorApiMod, UserAPIMod
])

Is there a way around this? I really don't like it because then components have requirements on their parents. Also it's undoubtedly going to get to the point where that parent module is including a bunch of stuff its children no longer use. How would you combat this?

coreysnyder avatar Sep 17 '18 21:09 coreysnyder

Hi there! If you are comfortable with using something a bit stateful, and not varying injectors by component tree, you can do this with:

const myService = angular.element(document.body).injector().get(SERVICE_NAME);

You can even write a hook to cache this. Something like:

function useAngularInjection(name) {
  return useMemo(() => angular.element(document.body).injector().get(name), [])
}

function MyComponent(props) {
   const stateParams = useAngularInjection('$stateParams');
  return <> ... </>;
}

cpimhoff avatar Sep 16 '19 21:09 cpimhoff