🤖 Meet OnCall AI, our observability copilot that makes troubleshooting easy. Read announcement.

Skip to content
Engineering

Framework Fusion: Edge Delta's Agile Approach to Migrating Our Frontend Architecture

Jan 11, 2024 / 4 minute read

Recently, we shifted Edge Delta from Angular to React. Learn how we streamlined migration with two tools to fuse the strengths of React with our Angular app.

.

Written on behalf of the Edge Delta Frontend Engineering Team: Ashlinn Brennan, Tyler Burkhardt, Dali Hajek, Gokhan Kurt, Dave Reese, Rohit Sharma, Üstün Özgür

At Edge Delta, the engineer-led decision to transition our application from Angular to React was met with an innovative Agile solution. As the size and complexity of our application grew, Angular became a hindrance to our ability to iterate faster, test more thoroughly, and hire talented engineers. React comes with a more robust and active community, more modern features such as error boundaries and HMR, and smaller bundle size. But switching between frontend frameworks is no easy task.

Our frontend team developed two pivotal tools that allowed us to interweave the strengths of React into our established Angular application: useAngularServices and ReactWrapperComponent. This strategic approach enabled us to reimplement features in React, maintaining the robustness of Angular while we migrated, ensuring a seamless evolution of our platform and continually minimizing the risk of changing architectures.

We identified two questions whose answers helped us determine our approach:

  1. How can we continue to leverage existing Angular services and focus on the UI before implementing a data retrieval and state management solution?

  2. How can we expedite and streamline the adoption of the Mantine UI library iteratively, considering most of our pages were–at the time–still written with Angular?

Angular Services

Our solution to the first question was a custom React hook that utilizes Angular’s dependency injection system to open up service-based functionality to individual React components. This was as simple as creating a React context of type Injector, which is provided by Angular. From there, it’s as simple as sending in an array of Angular services and returning them to the hook invoker.

const AngularInjectorContext = createContext<Injector | undefined>(undefined);

const useAngularServices = (services) => {

    const injector = useContext(AngularInjectorContext);

    return services.map(service => injector.get(service, undefined, options));

};

The biggest advantage we gained from this approach was the preservation of existing functionality for our customers. The foundation of the Agile approach to software development is to preserve functionality throughout the product while expanding or updating targeted features. We were able to swap out individual UI components one by one without worrying about the risk of damaging the crucial data retrieval pipeline.

React Components

Our answer to finding a method for supporting rapid UI migration was the custom ReactWrapperComponent. This is an Angular component designed to encapsulate a React component within. It uses all of the standard lifecycle methods of an Angular component, but “translates” them, in a way, to their React counterparts. For instance, ngOnChanges commands the React component to re-render. The code below gives some insight into how that is invoked.

    if (!this.reactRootElement) {

      this.reactRootElement = this.reactRendererService.createReactRootElement(portalProps);

    } else {

      this.reactRootElement.rerender(portalProps);

    }

This “wrapper” component can best be described as a “bridge”, as its primary role is to facilitate communication between Angular and React. It acts as an intermediary, allowing for the seamless transfer of properties (props) from an Angular component to its React equivalent. This is implemented within the component that extends ReactWrapperComponent. Such a design ensures that the properties passed from the Angular side are effectively mapped to their corresponding React component. This bridge is particularly useful when aligning differing property interfaces between Angular and Mantine UI components.

Aligning function callbacks between the frameworks is equally straightforward. Take, for example, a standard atomic UI component: a checkbox. In our existing Angular pages, a checkbox may be rendered with the following snippet:

 <shared-checkbox

[label]="'Only new'"

[checked]="isOnlyNew"

(toggled)="setIsOnlyNew($event)"

class="mr-24"

></shared-checkbox>

The toggled function here is crucial, and is easily wrapped up for passing down to the React component within the wrapper with the following code:

  @Output() toggled = new EventEmitter<boolean>();

  onToggleHandler = ($event: boolean) => this.zone.run(() => this.toggled.emit($event));

Summary

The ability to leverage existing Angular services, as well as the ability to wrap React components and render them on Angular pages, boosted our migration from Angular to React in one very crucial way: we were able to develop new features in React as our migration continued. This meant that we were always buying down tech debt and getting ourselves closer to our new architecture. By using established functionality for data retrieval and the capability to render React features on Angular pages, we bought ourselves the time to focus on one portion of the migration at a time while still delivering value and constantly improving Edge Delta.

.

Benny Bennett

Senior Software Engineer

Filed Under

Share

Stay in Touch

Sign up for our newsletter to be the first to know about new articles.