DEV Community

Cover image for From a Brown Field to One in Bloom: Revitalizing AngularJS with React
Gianluca La Manna for Claranet

Posted on • Edited on • Originally published at claranet.com

From a Brown Field to One in Bloom: Revitalizing AngularJS with React

If you have worked at least once on a brownfield project, you know how difficult and frustrating it can be.

You can use old approaches, but you're probably writing code without the Typescript superset. Furthermore, you are losing all the functionalities that a new framework has, which compromises the velocity and the devEx.

We can write the entire solution with another framework. Probably, this won't be a good idea, basically for two reasons:

  • Time 🕰️
  • Costs 💸

The good news is that you can write the new features with the latest framework while keeping the old features. If you have good craftsmanship, you will be able to use the strangler fig pattern by Martin Fowler, promoting a smooth and clean transition.

From brownfield to one in bloom 🌸, but how?

AngularJS issues

We take the example of AngularJS.

The most significant risk of staying on AngularJS is safety, given that Google doesn't provide new security patches.

Another big issue is the two-way-data-binding. Each change in UI is reflected in the model and vice versa.

Each state change synchronizes more variables, and the use of a digest cycle can slow the application.

The next example of how AngularJS makes you suffer is Dependency Injection 💉. In AngularJS, dependencies are injected by the name of an argument.

function MyController($scope, $state) { 
// … 
}
Enter fullscreen mode Exit fullscreen mode

Here, the .toString() method is called, which extracts the argument names and then compares them with the already registered dependencies.
So, when you minify the code, it stops working.

Ultimately, we won't have TypeScript and all its advantages.

Perhaps we have enough points to think about writing with a new framework:

  • Safety
  • Two way data binding
  • DI
  • TS

Now, let's see how to integrate React into the AngularJS application.

Scaffolding React

The idea is to create a new folder in the AngularJS project's tree structure called app-new. This will be the entry point of our React application. Here, we can install the latest version of React. Preferably, we will install React with the Vite plugin.

Then, we will have a similar tree:

/
├── app
│   ├── assets
│   ├── modules
│   └── package.json
├── app-new
│   └── public
│       ├── src
│       └── package.json
├── assets
├── cypress
└── doc
Enter fullscreen mode Exit fullscreen mode

As you can see, we have two package.json. One for app (AngularJS) and one for app-new (React)

Integration layer between two apps

We will create an Angular component that we will call into the routes.js file. Then, we will define a new route for this component.

 .state('react-home', {
        url: '/home',
        template: '<app-new-wrapper></app-new-wrapper>',
        data: { requiresAuth: true }
      })
Enter fullscreen mode Exit fullscreen mode

Here, we can pass any props for this component.

Then, in the index.js Angular file, we should define a scope variable. This variable defines a base URL that will be used from React.

scope.NEW_APP_BASE_URL = 'http://localhost:3000';
Enter fullscreen mode Exit fullscreen mode

The controller, instead, should be something like that:

(function () {
  const controller = function ($sce) {
    const ctrl = this;

    ctrl.$onInit = () => {
      ctrl.baseUrl = window.NEW_APP_BASE_URL;

      ctrl.url = $sce.trustAsResourceUrl(`${ctrl.baseUrl}/app-new/`);
    };
  };

  angular.module('libClient.common').component('appNewWrapper', {
    bindings: {
    },
    templateUrl: '/modules/common/component/appNewWrapper/appNewWrapper.html',
    controller: [
      '$sce',
      controller
    ]
  });
})();

Enter fullscreen mode Exit fullscreen mode

and the view

<iframe name="iframe1" frameBorder="0" width="100%" height="100%" scrolling="no" src="{{$ctrl.url}}" allow="camera; microphone; display-capture; geolocation"></iframe>

Enter fullscreen mode Exit fullscreen mode

Here's a little summary:

Diagram How it works

Generally, the iframe is not a good idea for several reasons. The basic idea is to use it only for the transition from Angular to React, then remove it. With iframe, if you have SEO on your website, you may be penalized by Google indexing and have a ranking problem, in addition to various security issues.

So, we will have 2 apps launched on different ports.

To call our React components inside the iframe, let's define them in our AppRouter with the correct url that will open the iframe.

const AppRouter = () => {
  return useRoutes([
    {
      path: '/home',
      element: <Home />
    },

    ...
Enter fullscreen mode Exit fullscreen mode

Communication both frameworks

All right! Now we might ask ourselves a rather spontaneous question:

How can I communicate between a part of my app in angular and one in react or navigate through pages?

The answer? 🥁... : window.postMessage()

The window.postMessage() method safely enables cross-origin communication between a page and an iframe embedded within it

So, we create a function called goTo to navigate across the pages.

function goTo(destination: Destination, params?: NavigationParams): void {

    const {
        parent
    } = window

    parent.postMessage({
        type: 'navigation',
        state: destination,
        params: params ? params : null
    }, "*")
}
Enter fullscreen mode Exit fullscreen mode

If we send a message from React to Angular, we use this

window.parent.postMessage(
        {
            type: 'UPDATE_ID',
            id: params.id.toString(),
        },
        '*'
    );
Enter fullscreen mode Exit fullscreen mode

and then in the Angular controller

 window.addEventListener('message', (event) => {
  if (event.data?.type) {
    switch (event.data.type) {
      case 'UPDATE_ID':
        ctrl.id = event.data.id;
        break;
      }
    }
  });
Enter fullscreen mode Exit fullscreen mode

Conclusion

With this approach, we can integrate React into AngularJS and revitalize it. A good next step would be to rewrite the old AngularJS controllers into React components. This way, we have a smooth transition from AngularJS to React. In the end, we can remove our app-new-wrapper and iframe and delete the old app folder. Then, we will have the whole project in the React framework ✨

See ya next time. 👋🏻

Top comments (0)