Overview

@onecx/react-remote-components library is a set of tools aimed at simplifying the development of Angular applications integrated with OneCX by using and extending the functionality of {integration-interface_url}[@onecx/integration-interface]. This library focuses on connecting applications to already existing Angular-based environment, which is useful for integrating microfrontends with OneCX without directly using Topics.

The functionality of this library is based on module-federation. To provide a connection between applications running on different frameworks, we use module-federation 2.0, which focuses on runtime functionality. But it works just as well with a more static/webpack approach. We basically use a mix of both worlds. To expose the component we use static approach as it fits our case:

const mfConfig = {
  name: 'vite-example-ui',
  filename: 'remoteEntry.js',
  exposes: {
    './ViteWebcomponent': './src/app/bootstrap.ts',
    './ViteRemote': './src/remotes/vite-remote/bootstrap.ts',
  },
};

For retrieving OneCX portal components, we need to take a slightly different approach. Making it work statically, as it currently is, would be difficult due to the complexity of the portal. Right now we have webpack based with Angular mf there, which means it uses internal scope. It also has traefik, which makes it even more complex. So the approach we had to take was to use a framework-agnostic type of connection. Therefore we used module-federation 2.0. The whole process can be divided into 3 steps:

1) Initialization

Initialization is usually added to the _app.tsx file and it also creates a FEDERATION object in the window. This object is very useful because it contains all information about each registered remote. You can find there information about remotes, shared libs, caches, preloads etc. Because of the shared libs info, it will be also much easier to detect which remote has older or newer version of any library, and for that reason migrating will be much easier.

init({
  name: 'test-ui',
  remotes: [],
  shared: {
    react: {},
    'react-dom': {},
    'react-router-dom': {},
    rxjs: {},
    '@module-federation/enhanced': {},
    '@module-federation/runtime': {},
    '@module-federation/runtime-core': {},
    '@onecx/accelerator': {},
    '@onecx/integration-interface': {},
  },
});

2) Register Remote

To register remotes we should use another function from the same @module-federation/enhanced/runtime library. It simply registers a remote in the FEDERATION object. You could also turn on 'force' option, which basically means you can dynamically overwrite a remote.

registerRemotes(

            [{
                name: ExamplAppId,
                entry: '/mfe/example',
                type:
                  technology === 'Angular' ||
                  technology === 'WebComponentModule'
                    ? 'module'
                    : 'script',
              },],{ force: true }
          );

To make it work with traefik in entry, you need to put relative path as above. Otherwise it will not work.

3) Fetch Remote

Last step is to actually load the remote. In our webcomponent-based approach, it essentially means to fetch the remote, which will create a webcomponent that we can use inside our applications. It can be done using this function:

loadRemote(`${appId}/${exposedModule}`, {
        from: 'runtime',
      });

It is asynchronous function so we need to wait for it to be done.

Usage

To use other components of OneCX portal, you need only 2 functionalities: HOC 'withSlot' and 'SlotComponent'.

1) withSlot

This HOC uses providers that you need to fetch the desired component under the hood, so the developer is able to use only `withSlot` on export.
export const withSlot = <P extends object>(
  WrappedComponent: ComponentType<P>
): FC<P> => {
  const hocComponent = (props: P) => (
    <PermissionProvider>
      <SlotProvider>
        <WrappedComponent {...props} />
      </SlotProvider>
    </PermissionProvider>
  );

  return hocComponent;
};

export default withSlot(Testing);

2) SlotComponent

This component is just a container with a name property that you use to specify which shared component you want to use. What the component does is, based on the name property, it checks if there even is such a slot and if it has any components, and if it does, it creates an empty slot container with a tag name of those components. For example:

<ocx-user-siderbar-menu-component></ocx-user-siderbar-menu-component>

Then it just fetches the actual webcomponents(remoteEntry’s) using information from the RemoteComponents topic. After that, the fetched components are registered in the window, and this is the moment when you can see components inside created containers.

[#Example Usage]:

const Testing = () ⇒ ( <div> <h1>Testing</h1> <SlotComponent name="vite-example" /> </div> ); export default withSlot(Testing)

[#vite]
== Vite

This library works smoothly with vite applications.

[#nextjs]
== Next.js


[NOTE]
====
The next applications need more research to be able to share components. Although there is no problem with consuming other components.
====